Deploy with Kubernetes

Helm objects and values

Look up the chart objects and supported values.

For: cluster operators.

Look up the chart objects and supported values.

What the chart runs

ObjectWhat it does
StatefulSet <release>-serverThe servers. A StatefulSet so each keeps its name across restarts (the servers table, its leases, its logs), and upgrades roll one at a time. Each has a volume of its own for its data directory (--data-dir, server.persistence): the metering writes the database refused, kept until it takes them, and the erasure ledger, which a replaced pod (a rollout, an eviction, a drain) must not lose. --advertise is the pod's IP.
Job <release>-initserver init as a pre-install,pre-upgrade hook.
Service <release>-edgePorts 7233 (Temporal for nodes) and 7443 (join, renewals, control stream).
Service <release>-managementPort 8443: the admin API and the console.
Service <release>-serversHeadless: stable names for the servers.
ProbesStartup and liveness GET /healthz, readiness GET /readyz, over HTTPS on 7443. A fenced edge answers 503, so the Services stop sending it nodes while its peers serve.
PodDisruptionBudgetAt most one server down for a node drain (server.pdb).
NetworkPolicyServers reach each other on every port. The edge's ports are open to the chart's nodes and to networkPolicy.edgeFrom (by default anyone, for nodes outside the cluster). Management's port admits only the servers and networkPolicy.managementFrom, never nodes. Nodes reach only the edge, DNS and, unless nodes.networkPolicy.allowPublicEgress is off, public addresses for their plugins; private ranges stay closed.
CronJob <release>-backupOptional: server backup on a schedule into a volume (Back up).
StatefulSet <release>-nodeOptional: nodes in the cluster.

Kubernetes port-forward reaches management without a NetworkPolicy rule. An Ingress controller needs one: add its namespace to networkPolicy.managementFrom.

Values

The full list with comments is the chart's values.yaml. The groups:

ValuesWhat they set
image.server, image.nodeRepository, tag (default: the chart's appVersion) or digest, pull policy. imagePullSecrets.
clusterWhat the first install creates: name, historyShards (never changes), tenant, operator.
databaseexistingSecret and urlKey: the PostgreSQL URL. Required.
masterKeyexistingSecret and key: the master key file. Required.
masterKey.command, caSigner.commandA key command that unwraps the master key, and the CA signing command, passed to init and every server. Bring the executables with server.extraVolumes, server.extraVolumeMounts or server.extraInitContainers; they run without a shell, with a minimal environment: name the variables they need in OZ0_KEY_COMMAND_ENV through server.extraEnv, such as AWS_*,VAULT_ADDR,VAULT_TOKEN.
erasureLedgerDetails
initenabled (off: run server init --master-key-file yourself before the first install), adminPassword.existingSecret (required while enabled is on), backoffLimit, activeDeadlineSeconds, resources.
serverreplicas (2 or more), logLevel, sans, publicURL and oidcConfig.existingSecret (Sign-in), routineOutputAllowNets, llm.anthropicURL and llm.openaiURL, persistence (on by default: size, storageClass; off, the data directory is an emptyDir, and a pod that is replaced loses the metering writes waiting there), extraArgs, extraEnv, extraVolumes, extraVolumeMounts, extraInitContainers, resources, scheduling (nodeSelector, tolerations, affinity, topologySpreadConstraints, priorityClassName), security contexts, the three probes' timings, pdb.
service.edge, service.managementService type, annotations; for the edge also loadBalancerSourceRanges, externalTrafficPolicy and nodePorts.
ingressDetails
metricsenabled, port (9464): Prometheus metrics over plain HTTP, and a headless Service for them; serviceMonitor for the Prometheus Operator.
otlpendpoint, plain headers, and secretHeaders whose values come from Secrets.
networkPolicyenabled, edgeFrom, managementFrom, metricsFrom: lists of NetworkPolicy peers.
backupenabled, schedule, keep (archives to keep), persistence (existingClaim, or size and storageClass for one the chart makes once and keeps when it is uninstalled).
nodesenabled, replicas, joinToken.existingSecret or caHash, server (default: the edge Service), sandbox, sandboxTier1 (Sandbox tier 1 in node pods), runtimeClassName, stopGraceSeconds, persistence, resources, scheduling, networkPolicy.allowPublicEgress, privateRanges and extraEgress.
temporalThe backend, connection, encryption, UI and codec settings. See Temporal chart settings.
rolesDetails
payloadStoreurl (s3://bucket/prefix?...) keeps payloads and plugin artifacts in an S3-compatible bucket instead of PostgreSQL, on every server that runs the edge role; credentials.existingSecret and credentials.key name a Secret with an AWS shared credentials file for it. See Payload storage.

Temporal chart settings

backend: embedded (Temporal's services in every server, on your PostgreSQL), external or cloud, with address, tls, apiKey, cloudAccount, createNamespaces, encryptPayloads (true by default: payloads are encrypted with each tenant's key; false turns it off, which oz0 doctor warns about), uiURL and codec for the latter two (codec.audience, codec.accountClaim and codec.maxTokenAge bind the Temporal Cloud UI's tokens to this server).

The chart refuses what the server would: cloud without cloudAccount, an API key to an external Temporal without tls.enabled, and a codec.jwksURL of your own without codec.jwtIssuer. See Temporal.

Erasure ledger storage

Where management records every erasure for a restore to apply again (backups and erasure). By default each server gets a volume of its own for it (persistence: size, storageClass), mounted at /var/lib/orchestrator-zero/erasure-ledger, apart from the database and its backups. existingClaim names a ReadWriteMany claim instead, with one file per pod; roles.separate needs it, since management's Deployment has no volume per pod. enabled: false keeps no ledger (--erasure-ledger=off). A ledger on a pod's own storage would be lost at every rollout, and erasures would then be refused, so the chart refuses to render one.

Ingress configuration

An Ingress for the console on port 8443. Management speaks only HTTPS, so set your controller's backend protocol (for ingress-nginx: nginx.ingress.kubernetes.io/backend-protocol: HTTPS). The CLI uses client certificates, which pass only through TLS passthrough; use a port-forward otherwise. The Ingress hosts are added to the servers' certificates.

Set ingress.trustedProxies to the addresses the controller's own pods connect from: management then counts failed sign-ins per browser, by the client the controller names in X-Forwarded-For, instead of counting every browser as the controller's one address (sign-in).

Name the controller alone, never the cluster's whole pod network: a pod inside a trusted network chooses the address it is counted as, and unless roles.separate is on, the edge's API, which nodes and other pods reach, trusts the same list. Pin the controller where nothing else runs (a node pool of its own, whose pod range you name, or host networking and those machines' addresses), or run the roles separately. The install notes remind you while it is empty.

Separate roles configuration

separate: true runs management as a Deployment and edges as a StatefulSet with a volume each. With the embedded backend, Temporal's services run as a separate StatefulSet. roles.manager, roles.edge and roles.temporal set their replicas and resources.

The edges also need these settings:

SettingPurpose
managementURLManagement's internal API address.
payloadDatabaseA Secret with the edges' own PostgreSQL URL for payloads and plugin artifacts. Required unless payloadStore.url is set; a separate edge will not start without one of them.
secretsKeyRequired key for sealing tenant secrets, created with oz0 server secrets-key create.
persistenceThe edge's persistent volume configuration.

With separate: true, the database user needs CREATEROLE, and pg_hba.conf must let oz0_edge_<database> log in with a password from the management pods (database requirements). The init Job checks both from its own pod with server init --roles-apart and fails with what to change.

Copyright © 2026