Skip to content

Temporal

The temporal engine driver runs native Temporal workflow services with persistence and SQL visibility in a bound Karakoram PostgreSQL cluster. Temporal roles are stateless; database HA and durable recovery belong to the source PostgreSQL cluster.

  • Provider: Zerops is not supported yet; use Dokploy or Nomad. Temporal membership requires a private IPv4 route to the bound PostgreSQL writer, while Zerops uses private IPv6. Zerops support is pending IPv6 private networking; see release status and provider availability.
  • An existing Karakoram PostgreSQL source cluster, major 12 or later, reachable from the Temporal members. The PostgreSQL engine driver’s own version catalog still applies.
  • The PostgreSQL binding service (ADR 0045), which the standard server constructs once and shares with Temporal, OpenBao, Keycloak and the bindings API. A composition without it rejects Temporal clusters with PostgreSQL bindings are unavailable.
  • Locked native artifacts and private networking between roles and the source writer. Members advertise the private IPv4 address used to reach that writer.
  • Provider horizontal scaling for component replicas above one; configured Dokploy HTTPS/subdomain routing for a public Web UI. Nomad supports private UI access only.

Create, replace and patch admission (including dry runs) reject providers whose runtime layout does not declare private IPv4 networking, before persisting intent.

The PostgreSQLBinding status condition reports Bound only after the binding has an identity and its source connection has resolved. An unreachable source member leaves the condition False/SourceUnavailable, including when a binding identity was saved during an earlier reconciliation.

Setting Value
Server catalog 1.32.1 (default), 1.31.4; spec.version selects the server
Web UI Release-locked ui-server 2.56.0
Artifacts temporal-server-1.32, temporal-server-1.31, temporal-ui-server; checksum-pinned native artifacts
Topology combined (default) or split; immutable after creation
Frontend Private TLS gRPC 7233; client certificates required by default
Recovery Source PostgreSQL full / diff / incr backup through the binding, plus a Temporal manifest

All roles use spec.components.extra, with independent replicas and resources. Omit the standard components.engine slot from create, replace and patch requests; it is unused for both combined and split topologies. Responses include that slot with zero replicas as an internal default, but the shared request schema does not accept an explicit zero there. Nonzero engine replicas are also rejected by the Temporal engine driver. Each extra component is one provider service; replicas share that service identity.

Component Combined Split Replica bounds
server All four server roles, default 1 Disabled 1–5 when enabled
history Disabled Default 1 1–9
matching Disabled Default 1 1–5
frontend Disabled Default 1 1–5
worker Disabled Default 1; Temporal’s internal worker, not application workers 1–3
ui Default 1 Default 1 0–3; zero disables

The dev preset uses combined server: 1, ui: 1. The prod preset uses split history: 3, matching: 2, frontend: 2, worker: 1, ui: 1. Presets are configurations, not availability guarantees. Inspect karactl providers get temporal -o json and copy the desired preset into a complete cluster request; there is no --preset flag.

A minimal API-shaped JSON request:

{
"name": "workflows",
"spec": {
"provider": "temporal",
"version": "1.32.1",
"topology": {"type": "combined"},
"engineOptions": {
"persistence": {"postgres": {"cluster": "appdb"}},
"historyShards": 512,
"frontend": {"requireClientAuth": true},
"namespaces": [{"name": "orders", "retention": "168h", "description": "Order workflows"}],
"ui": {"publicHttps": false}
}
}
}
Terminal window
karactl -n NS clusters create -f temporal.json --wait
karactl -n NS clusters status workflows -o json

engineOptions is a strict JSON object: unknown fields and inline secrets are rejected. engineConfig and generic public frontend exposure are not supported.

Option Default / rule
persistence.postgres.cluster Required source PostgreSQL cluster name
persistence.postgres.namespace Temporal cluster’s Karakoram namespace
persistence.postgres.database temporal_<cluster>; cluster-name hyphens replaced with underscores
persistence.postgres.visibilityDatabase temporal_<cluster>_visibility, with the same substitution
historyShards 512; integer 1–16384
frontend.requireClientAuth true; TLS remains enabled when false
namespaces Additional declarations with name, Go-duration retention ≥24h, optional description ≤1024 bytes; default is always ensured at 72h
ui.publicHttps false; public HTTPS requires enabled UI, OIDC and provider support
ui.oidc.issuer / ui.oidc.clientId HTTPS issuer and nonempty client ID; OIDC requires ui.publicHttps: true

Database names must be distinct lowercase SQL identifiers, at most 63 characters ([a-z_][a-z0-9_]*); PostgreSQL system databases and the _karakoram prefix are reserved. Namespace declarations are limited to 99 entries, and the serialized ensure request including the implicit default namespace must fit within 64 KiB.

Both databases live on the same source and have one binding-managed owner/login role. Connections use postgres12 (lib/pq), the stable writer endpoint and TLS hostname verification (verify-full). Passwords are resolved from the binding, never stored in options/status; credential-generation changes roll the components.

Server persistence and visibility connections and schema migrations set lib/pq disable_prepared_binary_result=yes, forcing all-text results even for explicit prepared statements. ProxySQL 3.0.11 cannot represent mixed text/binary result formats. pgx’s default_query_exec_mode=exec alone is insufficient: explicitly prepared visibility queries bypass that default. The agent’s schema checks do not explicitly prepare and continue using pgx exec. These choices apply to direct singleton sources too; bound parameters and TLS verification are retained.

Named prepared statements are supported by this proxy; they are not named cursor portals. lib/pq executes them with an unnamed portal and no incremental cursor fetch. The dedicated pgx schema-lock connection uses a session advisory lock, which ProxySQL tracks to disable multiplexing. That lock is lost with its backend session, not migrated across a database failover. Do not intentionally fail over a source during schema migration.

After a primary failure the stable writer endpoint routes new database work to the promoted primary; SQL pools can replace failed connections. In-flight transactions may fail and Temporal must recover through its persistence/task retry paths. Qualify history, binary payloads, timers and explicit-prepared visibility lookups after failover; frontend health alone is insufficient. See the binding failover boundary.

This fix requires newly deployed consumer agents; a control-plane upgrade or ordinary restart does not replace them. See the v0.4.1 consumer rollout procedure before recreating any existing consumer with valuable state.

The persistence block, historyShards and topology are immutable after creation. The Temporal cluster name is the Karakoram cluster name. Choose database names and shard count before admitting production workflows.

Normal cluster status is password-free:

  • version, phase, message, observedGeneration and members describe convergence. Members report component set/role, observed version and health; UI members report the UI artifact version.
  • connection.hosts, connection.uri and the frontend endpoint identify the private gRPC service; public UI URLs appear as ui endpoints only when enabled.
  • PostgreSQLBinding conditions describe source namespace/cluster, shared and consumerCount, or SourceUnavailable while waiting.
  • TemporalNamespaces contains observed workflow namespaces; TemporalSchema conditions report persistence/visibility versions by component; TemporalUpgrade reports an active rolling upgrade.
  • TemporalRestoreRecovery fences maintenance while stopped runtimes are restarted and readiness-checked after an interrupted or failed source restore.

A Ready component proves that every live instance passed authenticated generic agent/configuration checks and native readiness. Status publishes that positive agent evidence with a fresh control-plane timestamp so the shared availability lease can accept it. Each live sweep replaces the prior observation identity; failed or expired observations never renew readiness. Repairing missing agent observation evidence requires only a control-plane upgrade, not agent replacement.

No multi-cluster replication or global namespaces, no per-namespace authorization, and no individual client-certificate revocation. Namespace descriptions cannot be cleared through Temporal’s API. Temporal restores require recorded backups, not a PITR timestamp or an arbitrary clone destination. Backup manifests live in Temporal cluster state, so preserve control-plane state and its master key as well as PostgreSQL backups.

See access and TLS, operations and recovery, and ADR 0056.