Overview
Temporal is an open-source (MIT) durable-execution platform: applications write workflows as ordinary code, and the Temporal server guarantees they run to completion, surviving process crashes, restarts, and long waits. This template deploys a single-process Temporal server exposing its gRPC frontend to your workloads, plus the Temporal Web UI, both internal-only. By default both are backed by the PostgreSQL Highly Available template as a bundled subchart; a single-instance PostgreSQL subchart is available for development. Database and schema setup run automatically at boot, including schema migrations when the server image is upgraded.What Gets Created
postgresHA.backup.mode: wal-g, no cron workload is created; a wal-g-backup sidecar container runs inside RELEASE_NAME-postgres-ha instead.
Prerequisites
None for a default install. The database credentials are not a prerequisite: this chart creates the credentials secret for you frompostgres.credentials.* and hands it to the bundled database by name. You only have to change the password before installing.
Optional database backups need a bucket and, for AWS or GCP, a Control Plane Cloud Account. Backups to MinIO or another S3-compatible endpoint additionally need a dictionary secret holding accessKey and secretKey, which must exist before you enable them — see Backing Up.
Installation
Install with the database password set to something strong. Nothing else is required for a default install:UI
CLI
Terraform
Pulumi
Configuration
Temporal Server
image— The upstreamauto-setupimage. At boot it creates thetemporalandtemporal_visibilitydatabases if missing, applies or upgrades the schema, registers thedefaultnamespace, then starts the server. All steps are idempotent, so restarts and image upgrades are safe.historyShards— Permanent. The shard count is fixed when the cluster first boots and the server will not start against a cluster initialized with a different value. Size it before installing;512suits most deployments.namespaceRetention— How long closed workflow histories stay queryable in thedefaultnamespace, as a Go duration such as72hor168h.
Web UI
ui.enabled— Setfalseto remove the UI workload.
Access
internalAccess.type sets the internal firewall scope for both the gRPC frontend and the UI:
internalAccess takes a few minutes to take effect. Re-test rather than trusting the first response.
Highly Available PostgreSQL
The default database. Exactly one ofpostgresHA and postgres must be enabled; the chart refuses to render otherwise.
postgresHA.config.credentialsSecretName— Name of the dictionary secret this chart creates and the HA cluster reads. Its contents come frompostgres.credentials.*below, in both database modes. Secret names are organization-wide, so give each Temporal release its own name: a second release left on the default is refused at install and creates nothing, and the first release is unaffected.postgresHA.replicas/postgresHA.volumeset.capacity— PostgreSQL replica count and volume size per replica. The remaining cluster settings (etcd, HAProxy, PgBouncer) take the defaults of the PostgreSQL Highly Available template; the HAProxy leader endpoint must stay enabled because it is the address Temporal connects to.postgresHA.backup.*— Optional backups of both Temporal databases, as scheduled logical dumps or continuous WAL-G archiving. See Backing Up.
Single-Instance PostgreSQL
Enable this block and disablepostgresHA for a lighter non-HA deployment.
postgres.credentials.username/postgres.credentials.password— The bundled database’s credentials. Change the password before installing. These two values populate the chart-created credentials secret in both database modes, even though they sit under thepostgresblock.postgres.credentials.database— Must remaintemporal. The server reads its main store name as the literaltemporaland auto-setup creates only the visibility store alongside it, so any other value leaves the install unable to start; the chart refuses to render a different value.postgres.config.credentialsSecretName— Name of the credentials secret in single-instance mode (the HA counterpart ispostgresHA.config.credentialsSecretName).
postgres.credentials.password on an existing release updates the secret but not the password PostgreSQL enforces, so Temporal can no longer authenticate. To rotate, change the password inside PostgreSQL first (ALTER ROLE temporal WITH PASSWORD '...'), then upgrade the release with the matching value and force a redeployment of RELEASE_NAME-temporal so it picks up the new secret value.Connecting
This template exposes no public endpoint. Everything below is reachable from workloads inside theinternalAccess scope, or from your own machine through cpln port-forward.
SERVING:
http://localhost:8080:
First Run
Temporal has no setup wizard. A fresh install already has adefault namespace, so the first run is a worker and a workflow.
Check the namespace in the Web UI
http://localhost:8080. The default namespace is already registered, with the retention you set in namespaceRetention.Run a worker
internalAccess scope and connect to the gRPC frontend by its full hostname. A minimal Python worker:if __name__ == "__main__":. The Temporal Python SDK’s workflow sandbox re-imports the worker module, and an unguarded module-level main() crashes the worker.Start a workflow
Operations
Backing Up
Database backups are optional and disabled by default. They cover both Temporal databases —temporal (workflow histories, task queues, timers) and temporal_visibility — because the backup job runs pg_dumpall against the whole server. Enable them with postgresHA.backup.enabled or postgres.backup.enabled, matching your database mode, and complete the storage setup for your provider before enabling. The keys below are written as backup.*; set them inside the enabled database block.
In HA mode, postgresHA.backup.mode selects logical (a scheduled dump from the RELEASE_NAME-postgres-ha-backup cron workload, through the HAProxy leader endpoint) or wal-g (continuous WAL archiving from a wal-g-backup sidecar inside RELEASE_NAME-postgres-ha, with a base backup every walg.intervalSeconds). Single-instance mode takes scheduled logical dumps only.
- AWS S3
- Google Cloud Storage
- S3-compatible (MinIO, R2, Wasabi)
Create a bucket
backup.aws.bucket and backup.aws.region to match.Set up a Cloud Account
backup.aws.cloudAccountName to its name.Create a bucket-scoped IAM policy
YOUR_BUCKET), then set backup.aws.policyName to the policy’s name. The template attaches that policy to the backup identity and nothing else, so the bucket in your policy is the only storage the backup job can reach:Restoring a Backup
A logical backup is a full-server dump (pg_dumpall), so one file holds both Temporal databases and the temporal role. Two constraints follow from the chart:
- Restore into a database Temporal is not writing to. The dump recreates
temporalandtemporal_visibility, so the cleanest target is a database from which both have been dropped, with the Temporal server not running against it. - Keep
historyShardsidentical to the value the dump was taken under. The server will not start against a history store initialized with a different shard count.
username and password from cpln secret reveal my-temporal-db-credentials -o yaml and the matching host — RELEASE_NAME-postgres-ha-proxy.GVC_NAME.cpln.local in HA mode, RELEASE_NAME-postgres.GVC_NAME.cpln.local in single mode:
gsutil cp "gs://BUCKET_NAME/PREFIX/BACKUP_FILE.sql.gz" -; for MinIO export AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY, run aws configure set default.s3.addressing_style path, and add --endpoint-url "http://MINIO_ENDPOINT:9000" to the aws s3 cp. The WAL-G point-in-time procedure for HA mode — a new volume set, a one-off wal-g backup-fetch, and re-pointing the cluster — is documented on the PostgreSQL Highly Available page and carries the same Temporal caveats above.
Upgrading from 1.0.x
Apply every subsection from your version onward, in order, before runningcpln helm upgrade. Each removed key is rejected at render, so an upgrade that still carries one fails before anything is applied and the running release is left untouched.
Version 1.1.0 moved the bundled single-instance database to the postgres 3.4.1 template, which no longer takes credentials or MinIO keys as values. This template absorbed the credentials change rather than passing it on — it now creates the credentials secret itself — so there is no new prerequisite for the database, only renames on the single-instance path:
config.username was REMOVED in postgres 3.4.0 and tells you to create a dictionary secret yourself. Ignore that advice for the three credentials keys: this template creates that secret from postgres.credentials.*. Use the username and password your database was initialized with, not new ones.
Upgrading from 1.1.0
Version1.2.0 moved the bundled HA database to postgres-highly-available 2.5.0, which removed the postgresHA.postgres block. The HA cluster now reads the same chart-created credentials secret as the single-instance path:
postgresHA.postgres block fails with the HA template’s message, which begins postgres-highly-available: the `postgres` block was REMOVED in 2.5.0 and tells you to create a dictionary secret. Ignore that part: delete the block and set postgres.credentials.* instead. If you installed 1.1.0 or earlier with the published default change-me-temporal-db-password, rotate it inside PostgreSQL with ALTER ROLE as part of this upgrade and put the new value in postgres.credentials.password.
Upgrading from 1.2.x
- From
1.2.0, HA MinIO backups only: version1.2.1replacedpostgresHA.backup.minio.accessKeyandpostgresHA.backup.minio.secretKeywithpostgresHA.backup.minio.credentialsSecretName, a dictionary secret you create holdingaccessKeyandsecretKey— see Backing Up. The HA and single-instance paths now use the same shape. - From
1.2.0or1.2.1: version1.3.0renamed the resource limits so the ceiling is no longer ambiguous next to the floor. Rename these keys in your values; an upgrade carrying an old name is refused at render withresources.cpu was RENAMED to resources.maxCpu(or the matching message for the other three):
1000m / 2Gi for the server, 500m / 512Mi for the UI), so a release on the defaults upgrades with no resource change.
Scaling and Availability
- The Temporal server runs exactly one replica. Autoscaling is disabled and the workload is pinned to
minScale: 1,maxScale: 1. Do not raise the replica count by hand: a second replica does not form a working cluster with this template. - Workers scale independently. Run as many worker workloads and replicas as your task queues need; the server’s capacity is governed by
resourcesand by the database behind it. - Database availability is the HA cluster’s job. In the default
postgresHAmode a PostgreSQL leader failure is handled by Patroni and the HAProxy endpoint keeps the same address, so Temporal reconnects without a change. Inpostgresmode there is no failover. - Every
cpln helm upgraderestarts the server replica. A new server image runs its schema migrations at boot before the gRPC port opens. Expect a brief frontend outage per upgrade; workers reconnect and in-flight workflows resume where they left off. The first upgrade after an install can also restart the bundled database. - Durability is in the database, not the server. The server workload has no volume; the two volume sets belong to the database.
cpln helm uninstalldeletes them, along with the chart-created credentials secret, taking every workflow history with it — enable backups if the data matters.
Troubleshooting
Install fails with enable exactly one database
Install fails with enable exactly one database
helm install or helm upgrade stops at render with temporal: enable exactly one database — set either postgresHA.enabled or postgres.enabled to true, not both, or the variant naming postgresHA.enabled (production) or postgres.enabled (dev/lightweight).Cause: Both database blocks are enabled, or neither is.Fix: Set exactly one of postgresHA.enabled and postgres.enabled to true. Switching an existing release between modes is a new database, not a migration — the data stays on the old mode’s volume set.Install fails with postgres.credentials.database must be temporal
Install fails with postgres.credentials.database must be temporal
temporal: postgres.credentials.database must be "temporal", got "...".Cause: The server reads its main store name as the literal temporal, and auto-setup creates only the visibility store alongside it. Any other value would leave the install unable to start, so the chart refuses it.Fix: Leave postgres.credentials.database at temporal.Upgrade fails with resources.cpu was RENAMED to resources.maxCpu
Upgrade fails with resources.cpu was RENAMED to resources.maxCpu
helm upgrade stops at render with temporal: resources.cpu was RENAMED to resources.maxCpu (or the same message for resources.memory, ui.resources.cpu, ui.resources.memory).Cause: Your values file predates template version 1.3.0.Fix: Rename the keys as listed in Upgrading from 1.2.x.Upgrade fails with the postgres block was REMOVED in 2.5.0
Upgrade fails with the postgres block was REMOVED in 2.5.0
helm upgrade stops at render with a message beginning postgres-highly-available: the `postgres` block was REMOVED in 2.5.0.Cause: Your values still carry postgresHA.postgres.username, postgresHA.postgres.password or postgresHA.postgres.database from template version 1.1.0 or earlier.Fix: Delete the postgresHA.postgres block and copy the same username and password into postgres.credentials.* — do not create a secret yourself, this chart creates it. See Upgrading from 1.1.0.Upgrade fails with config.username was REMOVED in postgres 3.4.0
Upgrade fails with config.username was REMOVED in postgres 3.4.0
helm upgrade stops at render with config.username was REMOVED in postgres 3.4.0 (or the same for config.password / config.database).Cause: Your single-instance values still use postgres.config.username, postgres.config.password or postgres.config.database from template version 1.0.x.Fix: Move the three values to postgres.credentials.* — do not create a secret yourself. See Upgrading from 1.0.x.Second release fails with managed by a different release
Second release fails with managed by a different release
The resource 'my-temporal-db-credentials' cannot be updated because it is being managed by a different release, and nothing is created.Cause: Secret names are organization-wide, and both releases are trying to create the credentials secret under the default name.Fix: Give the new release its own postgresHA.config.credentialsSecretName (or postgres.config.credentialsSecretName in single mode). The first release is unaffected.Server restarts repeatedly after changing historyShards
Server restarts repeatedly after changing historyShards
historyShards, RELEASE_NAME-temporal never becomes ready and keeps restarting; the server refuses to start against a history store initialized with a different shard count.Cause: The shard count is permanent for the life of the cluster.Fix: Set historyShards back to the value the cluster was created with and upgrade again. Changing it for real means a new release with a fresh database.Workers cannot connect to the frontend
Workers cannot connect to the frontend
internalAccess scope.Fix: Connect to RELEASE_NAME-temporal.GVC_NAME.cpln.local:7233 and make sure the worker’s workload is covered by internalAccess.type (or listed in internalAccess.workloads for workload-list). After changing the scope, allow a few minutes before re-testing.Backup workload never starts and logs nothing
Backup workload never starts and logs nothing
RELEASE_NAME-postgres-ha-backup (or RELEASE_NAME-postgres-backup, or in wal-g mode RELEASE_NAME-postgres-ha itself) never becomes ready and cpln logs returns zero lines.Cause: The secret named by backup.minio.credentialsSecretName did not exist when the workload was applied.Fix: Create it as shown in Backing Up, then read status.versions[].message with cpln workload get-deployments WORKLOAD_NAME --gvc GVC_NAME -o yaml to confirm the reference resolves, or force a redeployment of the affected workload.Python worker crashes on start
Python worker crashes on start
main() at import time. The SDK’s sandbox re-imports the worker module, re-running it.Fix: Guard the entrypoint with if __name__ == "__main__": as in the First Run sample.Important Notes
- Change
postgres.credentials.passwordbefore installing. The defaultchange-me-temporal-db-passwordis published in the template repository, and it is the database password in both database modes. historyShardsis permanent. It is fixed at the cluster’s first boot and the server will not start against a different value; size it before installing.- Never expose the Web UI publicly. It has no authentication, and this template deliberately has no knob to do so. Use
cpln port-forwardor your own authenticating proxy. - Use the full
.cpln.localhostname in every worker and client; short workload names do not resolve. - Keep the server at one replica. Scaling the workload by hand does not form a working cluster.
- Give each release its own
config.credentialsSecretName. Secret names are organization-wide; a second release on the default name is refused at install. - Temporal connects as the bundled database’s superuser and needs
CREATE DATABASEand DDL rights at every server upgrade; do not downgrade that role. - Every upgrade is a brief frontend outage. The single server replica restarts and migrates the schema at boot; in-flight workflows resume afterwards.
- Uninstall deletes the database volume sets and the chart-created credentials secret, taking every workflow history with it. Enable backups if the data matters.