Skip to content

Deployment Topology

This page draws the Kubernetes deployment two ways: what the Helm chart renders today, and what the high-availability epic (#3408) plans to add. They are separate diagrams on purpose. Nothing in the second one is in the chart today, and a single diagram would make it look as though it is.

Use it with Deployment to install, Backup & Restore to recover, and Durability & Redundancy for what each failure costs.

The chart renders six long-running workloads (web, api, celery-worker, celery-beat, PostgreSQL and Valkey), plus three optional pieces: the Ingress, the media PVC and the backup CronJob. The object-storage bucket is yours to provide. Everything below is verified against packages/helm/templates/ and values.yaml.

Datastores (bundled sub-charts: dev and demo only)

Application tier (one release)

/

/api, /ws

/api, /ws

reads

mediaDir, read-only

Browser / API client

Ingressoff by default (ingress.enabled)

web (nginx)serves the SPA, proxies /api and /ws

api (uvicorn, ASGI)REST and WebSocket in one processinit: migrate, bootstrap, collectstatic

celery-workerCPM, imports, exports, email

celery-beatscheduler, always 1 replica

PostgreSQLsingle pod, no replicaPVC 8Gi

Valkeysingle pod, no replicaPVC 2Gi, AOF

media PVCoptional, persistence.mediashared by api, worker, beat

Object storageS3-compatible bucketyou provide it

backup CronJoboptional, backup.enabledpg_dump, custom format

Backup destinationbackup PVC and/or S3 bucket

Solid boxes are rendered by a default install. Dashed boxes are opt-in or something you supply. Two things the diagram deliberately does not draw because the chart does not render them: there is no separate WebSocket (Channels) deployment, since the api pod runs uvicorn trueppm_api.asgi:application and serves REST and WebSocket together; and there is no migration Job, since each api pod runs migrate_locked as an init container, which serializes concurrent pods on an advisory lock.

  • One PostgreSQL pod, one Valkey pod. The bundled sub-charts are single-replica StatefulSets with ReadWriteOnce PVCs. They survive a container restart because the data is on the volume. If the pod is lost it is an outage until Kubernetes reschedules it onto the same volume, and data is at risk only if that volume was node-local storage on a lost node (see the failure-mode matrix). Because /api/v1/readyz checks a live cache round-trip, losing Valkey marks every api pod NotReady. See Valkey High Availability.
  • No point-in-time recovery and no in-cluster HA today. The chart has no WAL archiving, so recovery is to the last logical backup. The production overlay (values-prod.yaml) disables both sub-charts and points DATABASE_URL and REDIS_URL at managed services; that is the supported way to get HA or PITR today.
  • replicaCount scales api, celery-worker and web, not celery-beat. Beat is pinned to one replica, because a second one double-fires every periodic task.
  • The web pod is the front door. With the Ingress path split in values.yaml, / goes to web and /api and /ws go straight to api. The web pod also proxies /api/ and /ws/ to api.
  • The demo seed Job and demo reset CronJob exist only when demo.enabled is set, and are left out.
DataWhere it livesIn the chart’s backup?
Application databasePostgreSQL volume (8Gi PVC on the bundled sub-chart, or your managed database)Yes. The CronJob runs pg_dump --format=custom.
PostgreSQL extensions ltree, pg_trgm, btree_gistCreated in the database by the application’s migrationsPreserved by the custom-format dump. A managed database must allow them. A restore fails loudly if they are missing.
Everything written to the media volume: attachments, the workspace logo, seed-import payloads and the project, program and workspace export bundles (six write paths, listed at persistence.media in values.yaml)The media PVC if you use local storage, otherwise the object-storage bucketMedia PVC: only if you set backup.mediaDir and mount the claim read-only. A ReadWriteOnce claim is readable by the backup Job only if the Job lands on the same node as the api pod that holds it, which is one reason the default is ReadWriteMany. Bucket: no, back it up with the object store’s own tooling.
Valkey (broker, Channels layer, cache, throttle counters)Valkey volume (2Gi PVC, AOF)No, by design. It holds reconstructible state only.
INTEGRATION_ENCRYPTION_KEY and the other application SecretsKubernetes SecretNo. The dump is useless without the key, so back it up separately.
Backup artifactsThe backup PVC, an S3-compatible bucket, or bothThey are the output, not an input. The chart refuses to render an enabled backup with nowhere durable to write.

The backup is off by default (backup.enabled: false). Restores are manual, using scripts/restore.sh; see Backup & Restore for the runbook and the restore drill.

This diagram draws only the planned datastore changes, so the application tier is shown as it is today. The epic also plans changes that are not drawn: a connection-pooling path (#2275), separate Celery queues with queue-depth worker autoscaling (#2276), and a single application Secret contract with an ExternalSecret template (#3405). Any of these may change the paths between the application tier and the datastores.

Planned (#3404): replicated Valkey

Planned (#3403): in-cluster HA PostgreSQL

Application tier (unchanged)

replicates

archives WAL

replicates

Browser / API client

web (nginx)

api (uvicorn, ASGI)

celery-worker

celery-beatstill one replica

PostgreSQL primaryoperator-managed

PostgreSQL replica(s)streaming replication,automatic failover

WAL archiveobject-storage bucketenables point-in-time recovery

Valkey primary

Valkey replica(s)automatic failover(mechanism still open)

Dashed boxes are planned. In this diagram only the application tier exists today. The Ingress, media PVC, object storage and backup paths are not redrawn; they are unchanged from Ships today.

This is the single-cluster line: surviving the loss of a pod or node inside one cluster. It does not survive the loss of the cluster or the region. Cross-region replication, geo failover and managed backup verification are Enterprise features and are not drawn here.

A few things to plan around:

  • Managed services remain fully supported and are the way to get HA or PITR today. Nothing in the planned work removes that option.
  • How the logical backup relates to WAL archiving is undecided. #3403 will decide it, including whether the backup CronJob stays as it is once WAL archiving is on. Until then, the dump-and-restore runbook above is the supported recovery path, and Valkey is not part of any backup.
  • Sentinel client support already exists, but no Sentinel nodes are deployed. The chart can be pointed at Sentinels you run yourself (valkey.sentinel, experimental). What is planned is the chart standing up a replicated Valkey with automatic failover for you; the roadmap plans Sentinel, while #3404 still weighs a Valkey operator as the alternative.