Helm Chart Reference
Values, images, ingress, secrets integration, the migration job, autoscaling, and security defaults of the Helm chart.
The application layer is deployed by a single Helm chart. This page documents its
structure and the values you will set. For the environment variables that go inside
*.config and secrets, see Configuration.
Chart identity and images
| Field | Value |
|---|---|
| Chart name | chart |
| Chart version | Tracks the release you deploy |
| App version | Default image tag for all components |
Every component image defaults to tag: "", which resolves to the chart's
appVersion. This couples the whole set of images to the chart version, so a given
chart deploys a matching, consistent set of images. Pin a concrete chart version and
tag for reproducible production deployments; avoid tracking a moving latest tag.
global.imageRegistry is prepended to every image repository. Set it to your
registry (Marketplace ECR, your mirror, or Artifact Registry). A trailing slash is
normalized for you.
global:
imageRegistry: "123456789012.dkr.ecr.us-west-2.amazonaws.com/"
imagePullSecrets: []Components and values
Each workload (web, server, worker) shares the same value shape:
server:
enabled: true
replicaCount: 1
image:
repository: workflows/server
tag: "" # empty = chart appVersion
pullPolicy: Always
config: # non-secret env vars, rendered onto the pod
CLOUD_PROVIDER: aws
# ...
envFrom: # attach the secret(s) here (not automatic)
- secretRef:
name: app-secrets
resources:
requests: { memory: 1Gi, cpu: 500m }
limits: { memory: 2Gi, cpu: 1000m }
service:
type: ClusterIP
port: 80
targetPort: 3001
autoscaling:
enabled: true
minReplicas: 2
maxReplicas: 10
targetCPUUtilization: 70
targetMemoryUtilization: 80
pdb:
enabled: false
minAvailable: 1Defaults by component:
| Component | targetPort | Requests (cpu/mem) | Limits (cpu/mem) | HPA min-max | HPA CPU target |
|---|---|---|---|---|---|
web | 3000 | 250m / 512Mi | 500m / 1Gi | 1 - 6 | 70% |
server | 3001 | 500m / 1Gi | 1000m / 2Gi | 2 - 10 | 70% |
worker | (no service) | 100m / 256Mi | 500m / 512Mi | 1 - 8 | 60% |
The worker has no Service (it pulls work from Temporal rather than receiving inbound traffic) and a 120-second termination grace period so in-flight activities can drain.
Secrets integration
Secrets are never stored in the chart. Choose one of two mechanisms; both produce a
Kubernetes Secret that you then attach to components via envFrom.
External Secrets Operator (recommended)
Install the External Secrets Operator and create a SecretStore (or
ClusterSecretStore) pointing at your cloud secrets manager. Then:
externalSecrets:
enabled: true
secretStoreRef:
name: aws-secrets-manager # your SecretStore
kind: SecretStore
remoteRef: workflows-prod/app-secrets # provider secret path
refreshInterval: 1hThis renders an ExternalSecret that extracts all keys from your provider secret
into a Kubernetes Secret. Example SecretStore manifests for AWS Secrets Manager,
Doppler, and Vault ship in the chart's examples/ directory.
Plain chart-created secret (simple setups)
secrets:
create: true
# set only the keys you needWhichever mechanism you use, the resulting Secret is not attached to pods
automatically. Set server.envFrom, worker.envFrom, and (if it needs any
secret) web.envFrom to reference it, and make sure the name matches. At minimum
the Secret must contain PG_BASE_URL and TEMPORAL_API_KEY.
Ingress
One ingress template serves both clouds; the difference is className plus
annotations.
AWS (ALB)
ingress:
enabled: true
className: alb
annotations:
alb.ingress.kubernetes.io/scheme: internet-facing
alb.ingress.kubernetes.io/target-type: ip
alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS": 443}]'
alb.ingress.kubernetes.io/ssl-redirect: "443"
hosts:
- host: workflows.example.com
paths:
- { path: /api, pathType: Prefix, service: server }
- { path: /, pathType: Prefix, service: web }
tls:
enabled: true
certificateArn: arn:aws:acm:us-west-2:123456789012:certificate/...When certificateArn is set, the chart injects the ACM certificate annotation and
the ALB terminates TLS. Requires the AWS Load Balancer Controller.
GCP (GCE)
ingress:
enabled: true
className: gce
annotations:
kubernetes.io/ingress.global-static-ip-name: workflows-ip
networking.gke.io/managed-certificates: workflows-cert
kubernetes.io/ingress.allow-http: "false"
hosts:
- host: workflows.example.com
paths:
- { path: /api, pathType: Prefix, service: server }
- { path: /, pathType: Prefix, service: web }
tls:
enabled: trueOn GCP, TLS is a Google-managed certificate referenced by annotation, and you
reserve a global static IP. There is no certificateArn and no per-host TLS secret.
Path routing sends API prefixes (/api, and others such as /mcp, /channel,
/chat, /workflow in the sample overlays) to the server service and everything
else to web.
Database migration job
The chart runs schema migrations automatically:
- It is a Kubernetes Job registered as a
pre-install,pre-upgradeHelm hook, so it runs and completes before the application pods roll out on every install and upgrade. backoffLimit: 0(no retries; a failure surfaces immediately and Helm rolls back) andactiveDeadlineSecondsdefaults to 600.- It reads
PG_BASE_URL(viamigrations.envFrom) and optionalmigrations.configvalues such asSYSTEM_DB_NAMEandSEED_DATA.
migrations:
enabled: true
activeDeadlineSeconds: 600
image:
repository: workflows/migration
tag: ""
envFrom:
- secretRef:
name: app-secrets
config:
SYSTEM_DB_NAME: systemBecause the migration is a pre-install/pre-upgrade hook (hook weight -5), it runs
before the chart's main resources, including the ServiceAccount, are created. On
GCP with Workload Identity, create the ServiceAccount out-of-band (set
serviceAccount.create: false) so the hook job and External Secrets can use it.
Autoscaling and disruption budgets
- HPAs use
autoscaling/v2.serverandwebscale on CPU (and memory if a target is set). Theworkerscales on CPU by default, or on custom or external metrics if you setworker.autoscaling.metrics(for example, Temporal queue depth), in which case the CPU/memory targets are ignored. - PodDisruptionBudgets are defined for all three workloads but disabled by default.
Enable
pdb.enabledper component for production to protect availability during node drains.
Service account and identity
serviceAccount:
create: true
name: workflows-sa
annotations:
# AWS IRSA:
eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/workflows-prod-workloads-role
# or GCP Workload Identity:
# iam.gke.io/gcp-service-account: workflows-prod-workloads@project.iam.gserviceaccount.comTerraform outputs the exact annotation to use (service_account_annotation).
Security defaults
The chart is hardened by default and generally should not be loosened:
- Pods run as non-root (
runAsUser: 1000,fsGroup: 1000). - Containers set
allowPrivilegeEscalation: false,readOnlyRootFilesystem: true, and drop all capabilities.emptyDirvolumes are mounted at/tmpand/home/node/.npmto satisfy the read-only root filesystem. webpods disable service account token automount.
Namespace
namespace: workflows
createNamespace: false # set true to have the chart create it