Worker Helm chart examples
Use these worker configuration variations: ingress, intermediate, and egress topologies, DNS-based addressing, and registration methods, to adapt a worker to your network position. They assume a Boundary controller or HCP Boundary cluster already exists.
Create the namespace once before you install any of the examples:
$ kubectl create namespace boundary
The chart enables TLS for the operations listener by default. Create the default TLS Secret before you install a worker. Replace the placeholders with paths to a certificate and private key that are valid for the worker operations endpoint:
$ kubectl create secret tls boundary-worker-tls \
--namespace boundary \
--cert=<path-to-tls.crt> \
--key=<path-to-tls.key>
The examples keep operations TLS enabled and use the chart's tls.ops.disabled and tls.mountPath values in the HCL. If you change tls.secretName, create the TLS Secret with the same name.
| Topology | Accepts connections from | Typical Service type | Needs public_addr |
|---|---|---|---|
| Ingress | Downstream workers and clients | LoadBalancer | Yes |
| Intermediate | Upstream and downstream workers | LoadBalancer | Yes |
| Egress | Upstream or intermediate workers. Connects to targets | ClusterIP | No |
Worker with ingress capabilities
An ingress worker accepts connections from downstream workers and clients. It commonly uses the controller cluster as its upstream and exposes the proxy listener publicly through a LoadBalancer Service.
Save the following complete HCL and values as ingress-values.yaml:
worker:
config: |
disable_mlock = true
listener "tcp" {
address = "0.0.0.0:9202"
purpose = "proxy"
}
listener "tcp" {
address = "0.0.0.0:9203"
purpose = "ops"
tls_disable = "{{ .Values.tls.ops.disabled }}"
tls_cert_file = "{{ .Values.tls.mountPath }}/tls.crt"
tls_key_file = "{{ .Values.tls.mountPath }}/tls.key"
}
worker {
name = "boundary-worker"
public_addr = "boundary-worker.example.com:9202"
initial_upstreams = ["boundary-controller-cluster:9201"]
tags {
type = ["worker", "ingress"]
}
}
kms "aead" {
purpose = "worker-auth"
aead_type = "aes-gcm"
key = "<base64-encoded-32-byte-key>"
key_id = "<worker-auth-key-id>"
}
events {
audit_enabled = true
sysevents_enabled = true
observations_enabled = true
sink "stderr" {
name = "all-events"
event_types = ["*"]
format = "cloudevents-json"
}
}
service:
proxy:
type: LoadBalancer
port: 9202
targetPort: 9202
ops:
type: ClusterIP
port: 9203
targetPort: 9203
persistence:
authStorage:
enabled: false
path: /var/lib/boundary
recording:
enabled: false
path: /boundary/recording
Install the worker Helm chart:
$ helm install boundary-worker hashicorp/boundary-worker \
--namespace boundary \
--values ingress-values.yaml \
--wait
If you do not know the public_addr until the load balancer is created, wait for the external address:
$ kubectl get svc boundary-worker-proxy --namespace boundary --watch
Example output:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
boundary-worker-proxy LoadBalancer 10.0.0.30 9202:31600/TCP 10s
boundary-worker-proxy LoadBalancer 10.0.0.30 203.0.113.20 9202:31600/TCP 45s
Update public_addr in ingress-values.yaml, and then upgrade with the same values file:
$ helm upgrade boundary-worker hashicorp/boundary-worker \
--namespace boundary \
--values ingress-values.yaml \
--rollback-on-failure \
--wait
Worker with intermediate capabilities
A worker with intermediate capabilities relays traffic between upstream and downstream workers. It requires both initial_upstreams and public_addr.
Save the following complete HCL and values as intermediate-values.yaml:
worker:
config: |
disable_mlock = true
listener "tcp" {
address = "0.0.0.0:9202"
purpose = "proxy"
}
listener "tcp" {
address = "0.0.0.0:9203"
purpose = "ops"
tls_disable = "{{ .Values.tls.ops.disabled }}"
tls_cert_file = "{{ .Values.tls.mountPath }}/tls.crt"
tls_key_file = "{{ .Values.tls.mountPath }}/tls.key"
}
worker {
name = "boundary-worker"
public_addr = "boundary-worker.example.com:9202"
initial_upstreams = ["ingress-or-intermediate-worker.example.com:9202"]
tags {
type = ["worker", "intermediate"]
}
}
kms "aead" {
purpose = "worker-auth"
aead_type = "aes-gcm"
key = "<base64-encoded-32-byte-key>"
key_id = "<worker-auth-key-id>"
}
events {
audit_enabled = true
sysevents_enabled = true
observations_enabled = true
sink "stderr" {
name = "all-events"
event_types = ["*"]
format = "cloudevents-json"
}
}
service:
proxy:
type: LoadBalancer
port: 9202
targetPort: 9202
ops:
type: ClusterIP
port: 9203
targetPort: 9203
persistence:
authStorage:
enabled: false
path: /var/lib/boundary
recording:
enabled: false
path: /boundary/recording
Install the worker Helm chart:
$ helm install boundary-worker hashicorp/boundary-worker \
--namespace boundary \
--values intermediate-values.yaml \
--wait
If you do not know the public_addr until the load balancer is created, wait for the external address (refer to the example output shown in Worker with ingress capabilities):
$ kubectl get svc boundary-worker-proxy --namespace boundary --watch
Update public_addr in intermediate-values.yaml, then upgrade with the same values file:
$ helm upgrade boundary-worker hashicorp/boundary-worker \
--namespace boundary \
--values intermediate-values.yaml \
--rollback-on-failure \
--wait
Worker with egress capabilities
A worker with egress capabilities connects to target systems and is usually the final hop before a target. It often does not need a public_addr.
Both Services always render. For an egress worker, use ClusterIP for the proxy and operations Services. Save the following complete HCL and values as egress-values.yaml:
worker:
config: |
disable_mlock = true
listener "tcp" {
address = "0.0.0.0:9202"
purpose = "proxy"
}
listener "tcp" {
address = "0.0.0.0:9203"
purpose = "ops"
tls_disable = "{{ .Values.tls.ops.disabled }}"
tls_cert_file = "{{ .Values.tls.mountPath }}/tls.crt"
tls_key_file = "{{ .Values.tls.mountPath }}/tls.key"
}
worker {
name = "boundary-worker"
initial_upstreams = ["ingress-or-intermediate-worker.example.com:9202"]
tags {
type = ["worker", "egress"]
}
}
kms "aead" {
purpose = "worker-auth"
aead_type = "aes-gcm"
key = "<base64-encoded-32-byte-key>"
key_id = "<worker-auth-key-id>"
}
events {
audit_enabled = true
sysevents_enabled = true
observations_enabled = true
sink "stderr" {
name = "all-events"
event_types = ["*"]
format = "cloudevents-json"
}
}
service:
proxy:
type: ClusterIP
port: 9202
targetPort: 9202
ops:
type: ClusterIP
port: 9203
targetPort: 9203
persistence:
authStorage:
enabled: false
path: /var/lib/boundary
recording:
enabled: false
path: /boundary/recording
Install the worker with a values file that includes the worker configuration:
$ helm install boundary-worker hashicorp/boundary-worker \
--namespace boundary \
--values egress-values.yaml \
--wait
Worker with a pre-configured DNS address
If you already control the DNS name for the worker, set public_addr during installation. The address must use host:port format without a URL scheme. Save the following complete HCL and values as ingress-values.yaml:
worker:
config: |
disable_mlock = true
listener "tcp" {
address = "0.0.0.0:9202"
purpose = "proxy"
}
listener "tcp" {
address = "0.0.0.0:9203"
purpose = "ops"
tls_disable = "{{ .Values.tls.ops.disabled }}"
tls_cert_file = "{{ .Values.tls.mountPath }}/tls.crt"
tls_key_file = "{{ .Values.tls.mountPath }}/tls.key"
}
worker {
name = "boundary-worker"
public_addr = "boundary-worker.example.com:9202"
initial_upstreams = ["boundary-controller-cluster:9201"]
tags {
type = ["worker", "ingress"]
}
}
kms "aead" {
purpose = "worker-auth"
aead_type = "aes-gcm"
key = "<base64-encoded-32-byte-key>"
key_id = "<worker-auth-key-id>"
}
events {
audit_enabled = true
sysevents_enabled = true
observations_enabled = true
sink "stderr" {
name = "all-events"
event_types = ["*"]
format = "cloudevents-json"
}
}
service:
proxy:
type: LoadBalancer
port: 9202
targetPort: 9202
ops:
type: ClusterIP
port: 9203
targetPort: 9203
persistence:
authStorage:
enabled: false
path: /var/lib/boundary
recording:
enabled: false
path: /boundary/recording
Install the worker Helm chart:
$ helm install boundary-worker hashicorp/boundary-worker \
--namespace boundary \
--values ingress-values.yaml \
--wait
After the Service receives an external address, point the DNS record to that address. This avoids changing the worker HCL after installation, but you remain responsible for ensuring that DNS resolves to the reachable worker endpoint.
Worker-led registration
For worker-led registration, install the worker without a controller-generated activation token. The worker generates identity material at startup and writes registration information to the pod logs. For more information, see Worker-led registration.
Save the following complete HCL and values as worker-values.yaml. Worker-led registration requires persistent auth storage at /var/lib/boundary:
worker:
config: |
disable_mlock = true
listener "tcp" {
address = "0.0.0.0:9202"
purpose = "proxy"
}
listener "tcp" {
address = "0.0.0.0:9203"
purpose = "ops"
tls_disable = "{{ .Values.tls.ops.disabled }}"
tls_cert_file = "{{ .Values.tls.mountPath }}/tls.crt"
tls_key_file = "{{ .Values.tls.mountPath }}/tls.key"
}
worker {
initial_upstreams = ["boundary-controller-cluster:9201"]
auth_storage_path = "/var/lib/boundary"
tags {
type = ["worker", "worker-led"]
}
}
events {
audit_enabled = true
sysevents_enabled = true
observations_enabled = true
sink "stderr" {
name = "all-events"
event_types = ["*"]
format = "cloudevents-json"
}
}
persistence:
authStorage:
enabled: true
size: 1Gi
path: /var/lib/boundary
Install the worker Helm chart:
$ helm install boundary-worker hashicorp/boundary-worker \
--namespace boundary \
--values worker-values.yaml \
--wait
The typical workflow is as follows:
- Install the worker chart with worker-led registration settings in
worker.config. - Read the worker pod logs.
- Register or authorize the emitted worker registration information with Boundary.
- Confirm the worker becomes active.
View logs:
$ kubectl logs --namespace boundary deployment/boundary-worker-deployment
Keep auth storage enabled for worker-led registration so the generated worker identity persists across pod replacement. When worker.persistence.authStorage.enabled is false, the chart mounts an emptyDir at /var/lib/boundary, and the identity does not persist when Kubernetes replaces the pod.
Controller-led registration
The typical workflow for controller-led registration is as follows. For more details about controller-led registration, refer to Controller-led registration:
- Create the worker resource in Boundary or HCP Boundary.
- Obtain the controller-generated activation token.
- Supply the token using one of the methods below.
- Install the worker Helm chart.
- Confirm the worker registers and persists its credentials to auth storage.
The worker uses the activation token during initial bootstrap. After successful registration, the worker uses persisted credentials from auth storage.
The chart supports three ways to supply the activation token.
Plaintext token
Embed the token directly in worker.config. Suitable for development only. The token is visible to anyone who can read the ConfigMap.
Save the following complete HCL and values as worker-values.yaml:
worker:
config: |
disable_mlock = true
listener "tcp" {
address = "0.0.0.0:9202"
purpose = "proxy"
}
listener "tcp" {
address = "0.0.0.0:9203"
purpose = "ops"
tls_disable = "{{ .Values.tls.ops.disabled }}"
tls_cert_file = "{{ .Values.tls.mountPath }}/tls.crt"
tls_key_file = "{{ .Values.tls.mountPath }}/tls.key"
}
worker {
name = "boundary-worker"
initial_upstreams = ["boundary-controller-cluster:9201"]
auth_storage_path = "/var/lib/boundary"
controller_generated_activation_token = "<activation-token>"
tags {
type = ["worker", "controller-led"]
}
}
events {
audit_enabled = true
sysevents_enabled = true
observations_enabled = true
sink "stderr" {
name = "all-events"
event_types = ["*"]
format = "cloudevents-json"
}
}
persistence:
authStorage:
enabled: true
size: 1Gi
path: /var/lib/boundary
Install the worker Helm chart:
$ helm install boundary-worker hashicorp/boundary-worker \
--namespace boundary \
--values worker-values.yaml \
--wait
Token via extraEnv
Set controller_generated_activation_token to the env:// reference in worker.config and supply the token as a key-value pair in extraEnv. Save the following complete HCL and values as worker-values.yaml:
extraEnv:
- name: BOUNDARY_WORKER_CONTROLLER_GENERATED_ACTIVATION_TOKEN
value: "<activation-token>"
worker:
config: |
disable_mlock = true
listener "tcp" {
address = "0.0.0.0:9202"
purpose = "proxy"
}
listener "tcp" {
address = "0.0.0.0:9203"
purpose = "ops"
tls_disable = "{{ .Values.tls.ops.disabled }}"
tls_cert_file = "{{ .Values.tls.mountPath }}/tls.crt"
tls_key_file = "{{ .Values.tls.mountPath }}/tls.key"
}
worker {
name = "boundary-worker"
initial_upstreams = ["boundary-controller-cluster:9201"]
auth_storage_path = "/var/lib/boundary"
controller_generated_activation_token = "env://BOUNDARY_WORKER_CONTROLLER_GENERATED_ACTIVATION_TOKEN"
tags {
type = ["worker", "controller-led"]
}
}
events {
audit_enabled = true
sysevents_enabled = true
observations_enabled = true
sink "stderr" {
name = "all-events"
event_types = ["*"]
format = "cloudevents-json"
}
}
persistence:
authStorage:
enabled: true
size: 1Gi
path: /var/lib/boundary
Install the worker Helm chart:
$ helm install boundary-worker hashicorp/boundary-worker \
--namespace boundary \
--values worker-values.yaml \
--wait
Token from a Kubernetes Secret
Store the activation token in a Kubernetes Secret and reference it using secretRefs. The chart injects the Secret value as the BOUNDARY_WORKER_CONTROLLER_GENERATED_ACTIVATION_TOKEN environment variable, which Boundary reads at startup via the env:// prefix.
Create the Secret before installing the chart:
$ kubectl create secret generic boundary-worker-secrets \
--namespace boundary \
--from-literal=worker-controller-generated-activation-token='<activation-token>'
The key name (worker-controller-generated-activation-token) must match the value set in secretRefs.keys.controllerGeneratedActivationToken in your Helm values file. This command is provided as an example. Use any Secret management method that fits your workflow.
Set controller_generated_activation_token to the exact supported env://BOUNDARY_WORKER_CONTROLLER_GENERATED_ACTIVATION_TOKEN reference. Save the following complete HCL and values as worker-values.yaml:
secretRefs:
secretName: boundary-worker-secrets
validateExisting: true
keys:
controllerGeneratedActivationToken: worker-controller-generated-activation-token
worker:
config: |
disable_mlock = true
listener "tcp" {
address = "0.0.0.0:9202"
purpose = "proxy"
}
listener "tcp" {
address = "0.0.0.0:9203"
purpose = "ops"
tls_disable = "{{ .Values.tls.ops.disabled }}"
tls_cert_file = "{{ .Values.tls.mountPath }}/tls.crt"
tls_key_file = "{{ .Values.tls.mountPath }}/tls.key"
}
worker {
name = "boundary-worker"
initial_upstreams = ["boundary-controller-cluster:9201"]
auth_storage_path = "/var/lib/boundary"
controller_generated_activation_token = "env://BOUNDARY_WORKER_CONTROLLER_GENERATED_ACTIVATION_TOKEN"
tags {
type = ["worker", "controller-led"]
}
}
events {
audit_enabled = true
sysevents_enabled = true
observations_enabled = true
sink "stderr" {
name = "all-events"
event_types = ["*"]
format = "cloudevents-json"
}
}
persistence:
authStorage:
enabled: true
size: 1Gi
path: /var/lib/boundary
With secretRefs.validateExisting: true, the chart looks up the Secret during an install or upgrade and fails early when the Secret or configured key is missing. Client-only helm template rendering cannot find an in-cluster Secret, so use a cluster-connected install or upgrade to perform this validation.
Install the worker Helm chart:
$ helm install boundary-worker hashicorp/boundary-worker \
--namespace boundary \
--values worker-values.yaml \
--wait
Keep auth storage enabled at /var/lib/boundary for every controller-led token method so registered credentials persist across pod replacement. When worker.persistence.authStorage.enabled is false, the chart mounts an emptyDir at that path, and the credentials do not persist when Kubernetes replaces the pod.
KMS (key management system)-backed worker authentication
Boundary only supports KMS-backed worker authentication in self-managed deployments. For more information, refer to External KMS registration. It is not available for HCP Boundary workers.
For self-managed deployments, configure the worker HCL with an external KMS worker-auth stanza that matches the controllers. The following example uses AWS KMS. Configure workload credentials that grant the pod access to the KMS key, and replace the placeholders for your environment. Save the complete HCL and values as worker-values.yaml:
worker:
config: |
disable_mlock = true
listener "tcp" {
address = "0.0.0.0:9202"
purpose = "proxy"
}
listener "tcp" {
address = "0.0.0.0:9203"
purpose = "ops"
tls_disable = "{{ .Values.tls.ops.disabled }}"
tls_cert_file = "{{ .Values.tls.mountPath }}/tls.crt"
tls_key_file = "{{ .Values.tls.mountPath }}/tls.key"
}
worker {
name = "boundary-worker"
initial_upstreams = ["boundary-controller-cluster:9201"]
tags {
type = ["worker", "kms"]
}
}
kms "awskms" {
purpose = "worker-auth"
region = "<aws-region>"
kms_key_id = "<aws-kms-key-id>"
}
events {
audit_enabled = true
sysevents_enabled = true
observations_enabled = true
sink "stderr" {
name = "all-events"
event_types = ["*"]
format = "cloudevents-json"
}
}
persistence:
authStorage:
enabled: false
path: /var/lib/boundary
Install the worker Helm chart:
$ helm install boundary-worker hashicorp/boundary-worker \
--namespace boundary \
--values worker-values.yaml \
--wait
KMS authentication does not require persistent local auth material. When worker.persistence.authStorage.enabled is false, the chart mounts an emptyDir at /var/lib/boundary instead of creating a PVC. Data in that volume does not persist when Kubernetes replaces the pod.
Enable session recording storage
If the worker stores session recordings, enable recording persistence and set recording_storage_path in worker.config to the same path.
Save the following complete HCL and values as worker-values.yaml:
worker:
config: |
disable_mlock = true
listener "tcp" {
address = "0.0.0.0:9202"
purpose = "proxy"
}
listener "tcp" {
address = "0.0.0.0:9203"
purpose = "ops"
tls_disable = "{{ .Values.tls.ops.disabled }}"
tls_cert_file = "{{ .Values.tls.mountPath }}/tls.crt"
tls_key_file = "{{ .Values.tls.mountPath }}/tls.key"
}
worker {
name = "boundary-worker"
initial_upstreams = ["boundary-controller-cluster:9201"]
recording_storage_path = "/boundary/recording"
tags {
type = ["worker", "recording"]
}
}
kms "aead" {
purpose = "worker-auth"
aead_type = "aes-gcm"
key = "<base64-encoded-32-byte-key>"
key_id = "<worker-auth-key-id>"
}
events {
audit_enabled = true
sysevents_enabled = true
observations_enabled = true
sink "stderr" {
name = "all-events"
event_types = ["*"]
format = "cloudevents-json"
}
}
persistence:
recording:
enabled: true
size: 10Gi
path: /boundary/recording
Install the worker Helm chart:
$ helm install boundary-worker hashicorp/boundary-worker \
--namespace boundary \
--values worker-values.yaml \
--wait
Ensure the recording storage path in worker.config matches worker.persistence.recording.path.
Change service ports
You can change the proxy listener port when the HCL listener address and worker.service.proxy.targetPort match. Save the following complete HCL and values as worker-values.yaml:
worker:
config: |
disable_mlock = true
listener "tcp" {
address = "0.0.0.0:9222"
purpose = "proxy"
}
listener "tcp" {
address = "0.0.0.0:9203"
purpose = "ops"
tls_disable = "{{ .Values.tls.ops.disabled }}"
tls_cert_file = "{{ .Values.tls.mountPath }}/tls.crt"
tls_key_file = "{{ .Values.tls.mountPath }}/tls.key"
}
worker {
name = "boundary-worker"
public_addr = "boundary-worker.example.com:9222"
initial_upstreams = ["boundary-controller-cluster:9201"]
tags {
type = ["worker", "ingress"]
}
}
kms "aead" {
purpose = "worker-auth"
aead_type = "aes-gcm"
key = "<base64-encoded-32-byte-key>"
key_id = "<worker-auth-key-id>"
}
events {
audit_enabled = true
sysevents_enabled = true
observations_enabled = true
sink "stderr" {
name = "all-events"
event_types = ["*"]
format = "cloudevents-json"
}
}
service:
proxy:
port: 9222
targetPort: 9222
ops:
port: 9443
targetPort: 9203
Install the worker Helm chart:
$ helm install boundary-worker hashicorp/boundary-worker \
--namespace boundary \
--values worker-values.yaml \
--wait
The chart does not synchronize proxy settings automatically. The operations listener and worker.service.ops.targetPort must remain 9203 because the Deployment's named operations container port and health probes are hardcoded to 9203. You can change the Service-facing worker.service.ops.port, as the example shows.
Troubleshooting
Session recordings fail to export
If session recording exports fail, confirm that the worker environment provides FFmpeg 5.1.0 or later and the libsvtav1 codec, as described in Enable session recording storage.
More information
To view supported Helm values so that you can configure or update workers, refer to Worker values.