Skip to main content

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.
This template deploys into an existing GVC that you already have. It does not create one.

What Gets Created

In HA mode with 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 from postgres.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

Browse, install, and manage templates visually

CLI

Manage templates from your terminal

Terraform

Declare templates in your Terraform configurations

Pulumi

Declare templates in your Pulumi programs

Configuration

Temporal Server

  • image — The upstream auto-setup image. At boot it creates the temporal and temporal_visibility databases if missing, applies or upgrades the schema, registers the default namespace, 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; 512 suits most deployments.
  • namespaceRetention — How long closed workflow histories stay queryable in the default namespace, as a Go duration such as 72h or 168h.
Template version 1.3.0 renamed resources.cpu and resources.memory to resources.maxCpu and resources.maxMemory (and the same under ui.resources). An upgrade that still carries the old names is refused at render — see Upgrading from 1.2.x.

Web UI

  • ui.enabled — Set false to remove the UI workload.
The Temporal Web UI has no built-in authentication, so this template keeps it internal-only and has no knob to expose it publicly. Reach it from your own machine with cpln port-forward (see Connecting), or put your own authenticating proxy in front of it.

Access

Both workloads are internal-only; there is no public-access option in this template. internalAccess.type sets the internal firewall scope for both the gRPC frontend and the UI: A change to internalAccess takes a few minutes to take effect. Re-test rather than trusting the first response.

Highly Available PostgreSQL

The default database. Exactly one of postgresHA 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 from postgres.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 disable postgresHA 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 the postgres block.
  • postgres.credentials.database — Must remain temporal. The server reads its main store name as the literal temporal and 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 is postgresHA.config.credentialsSecretName).
The credentials are applied when the database volume is first initialized. Changing 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 the internalAccess scope, or from your own machine through cpln port-forward. Confirm the server is serving by running the same health check the chart’s own probe uses; the output ends in SERVING:
To open the Web UI from your own machine, tunnel to the UI workload and browse to http://localhost:8080:

First Run

Temporal has no setup wizard. A fresh install already has a default namespace, so the first run is a worker and a workflow.
1

Check the namespace in the Web UI

With the port-forward above running, open http://localhost:8080. The default namespace is already registered, with the retention you set in namespaceRetention.
2

Run a worker

Workers run as their own workloads inside the internalAccess scope and connect to the gRPC frontend by its full hostname. A minimal Python worker:
Python workers must guard their entrypoint with if __name__ == "__main__":. The Temporal Python SDK’s workflow sandbox re-imports the worker module, and an unguarded module-level main() crashes the worker.
3

Start a workflow

The server image ships the Temporal CLI, so you can start a workflow on the worker’s task queue without installing anything locally:
The run appears in the Web UI under Workflows.

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.
1

Create a bucket

Create an S3 bucket. Set backup.aws.bucket and backup.aws.region to match.
2

Set up a Cloud Account

If you do not have one, create a Cloud Account for your AWS account. Set backup.aws.cloudAccountName to its name.
3

Create a bucket-scoped IAM policy

Create an AWS IAM policy with the JSON below (replace 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

This restore path has not been exercised against a Temporal install. It is derived from the backup image (a pg_dumpall of the whole server, gzip-compressed) and from the restore procedure of the bundled database templates. Rehearse it on a throwaway release before you depend on it.
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:
  1. Restore into a database Temporal is not writing to. The dump recreates temporal and temporal_visibility, so the cleanest target is a database from which both have been dropped, with the Temporal server not running against it.
  2. Keep historyShards identical to the value the dump was taken under. The server will not start against a history store initialized with a different shard count.
Then run the pipeline documented by the bundled database template from a client with access to the bucket, using the 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:
For GCS replace the first line of the pipeline with 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 running cpln 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: Carrying an old credentials key forward fails with the bundled Postgres template’s message, which begins 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.
Template version 1.0.0 did not compact the etcd cluster inside the bundled HA database, so its backend grew with time alone and eventually went read-only, taking PostgreSQL failover with it. Only HA-mode installs are affected. Upgrading to 1.0.1 or later turns compaction on, which stops further growth but cannot shrink a backend that has already grown; a cluster that has already raised a NOSPACE alarm needs operator recovery rather than an upgrade. The mechanism and the inspection commands are on the PostgreSQL Highly Available page.

Upgrading from 1.1.0

Version 1.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:
Use the credentials your cluster already has. The HA cluster was initialized with the postgresHA.postgres.* values you installed with, and PostgreSQL still enforces them. Copy that username and password into postgres.credentials.username and postgres.credentials.password before upgrading. Putting new values there updates the secret but not the database, and Temporal can no longer authenticate.
An upgrade that still carries the 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: version 1.2.1 replaced postgresHA.backup.minio.accessKey and postgresHA.backup.minio.secretKey with postgresHA.backup.minio.credentialsSecretName, a dictionary secret you create holding accessKey and secretKey — see Backing Up. The HA and single-instance paths now use the same shape.
  • From 1.2.0 or 1.2.1: version 1.3.0 renamed 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 with resources.cpu was RENAMED to resources.maxCpu (or the matching message for the other three):
The values themselves are unchanged (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 resources and by the database behind it.
  • Database availability is the HA cluster’s job. In the default postgresHA mode a PostgreSQL leader failure is handled by Patroni and the HAProxy endpoint keeps the same address, so Temporal reconnects without a change. In postgres mode there is no failover.
  • Every cpln helm upgrade restarts 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 uninstall deletes them, along with the chart-created credentials secret, taking every workflow history with it — enable backups if the data matters.

Troubleshooting

Symptom: 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.
Symptom: Render fails with 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.
Symptom: 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.
Symptom: 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.
Symptom: 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.
Symptom: Installing a second Temporal release in the same org fails with 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.
Symptom: After an upgrade that changed 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.
Symptom: A worker or client fails to resolve or reach the server.Cause: Either the worker uses the short workload name, which does not resolve, or it runs outside the 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.
Symptom: With MinIO backups enabled, 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.
Symptom: A single-file Python worker crashes immediately with an error from inside the Temporal workflow sandbox.Cause: The module calls 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.password before installing. The default change-me-temporal-db-password is published in the template repository, and it is the database password in both database modes.
  • historyShards is 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-forward or your own authenticating proxy.
  • Use the full .cpln.local hostname 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 DATABASE and 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.

External References

Temporal Documentation

Official Temporal documentation

Self-Hosted Guide

Running and operating a self-hosted Temporal service

SDKs

Writing workflows, activities, and workers in every supported language

Namespaces

Namespaces and workflow-history retention

Web UI Reference

The Temporal Web UI reference

Temporal Template

View the source files, default values, and chart definition