Skip to main content
Kubernetes with Helm is the recommended way to run Filament in production. The chart deploys the server and control plane, runs database migrations, and creates a worker Job for each run. It does not create a long-running worker Deployment.
Run exactly one control-plane replica. Multiple replicas may race while coordinating schedules and runs. Keep controlPlane.replicas at 1 and leave controlPlane.autoscaling.enabled disabled.

Install the chart

The default setup uses PostgreSQL as its datastore, NATS JetStream as its event bus, and the datastore-backed secret provider. It needs these three values: Generate an encryption key and install the chart:
The encryption key must be base64 and decode to 16, 24, or 32 bytes. To manage these values outside Helm, set existingSecret to a Kubernetes Secret that contains PERSISTENCE_DSN, NATS_URL, and ENCRYPTION_KEY. Add AUTH_PAT when using an external Zitadel provider. When existingSecret is set, the chart does not create a Secret. If you use AWS Secrets Manager instead of the datastore-backed provider, you do not need ENCRYPTION_KEY. See Configuration for provider settings.

Resources

Server

The server Deployment serves the API and web application through a Service on port 8080. It starts with one replica. The chart can also create an Ingress, HorizontalPodAutoscaler, and PodDisruptionBudget.

Control plane

The control-plane Deployment runs the scheduler, dispatcher, tracker, and reaper. It starts with one replica and exposes health endpoints on port 8081. Its ServiceAccount has namespaced access to the Kubernetes Jobs it creates and removes.

Database migrations

A post-install and post-upgrade hook runs migrations with the server image. The migration Job waits up to five minutes for PostgreSQL and retries failed attempts up to six times. Set persistence.migrate: false if you manage migrations separately.

Rollouts and health checks

Both Deployments annotate their pods with checksums of the rendered ConfigMap and chart-managed Secret. Changing either resource through Helm triggers a rollout even when the image tag stays the same. Changes to an existingSecret do not affect the checksum, so restart both Deployments after rotating one. The server and control plane expose /startupz, /readyz, and /livez. Startup succeeds after NATS connects and the runtime modules start. Readiness checks PostgreSQL and NATS; the server also ensures that its default tenant exists. Liveness checks that the process can answer HTTP requests. The startup probe gates the other checks, readiness marks a pod unready, and liveness can restart its container. Worker Jobs do not expose health endpoints. They report progress through heartbeats instead.

Worker Jobs

For each run, the control plane creates a Kubernetes Job that starts the worker image. It passes TENANT_ID and RUN_ID to the worker. The worker loads shared settings from a Secret and ConfigMap, then loads the run from the datastore. Each pipeline can set its own CPU, memory, node selectors, and tolerations. This lets Kubernetes place and size each run independently. Common worker settings include:
  • controlPlane.dispatch.job.namePrefix (default filament)
  • controlPlane.dispatch.job.backoffLimit (default 1)
  • controlPlane.dispatch.job.ttlSecondsAfterFinished (default 3600)
  • controlPlane.dispatch.worker.restartPolicy (default Never)
  • controlPlane.dispatch.worker.terminationGraceSeconds
  • controlPlane.dispatch.worker.activeDeadlineSeconds
For all worker settings, see the Helm chart README.

Scaling

The server is stateless and can scale horizontally. Keep the control plane at one replica. Worker capacity scales through Kubernetes because each run gets its own Job and resource configuration.

Vendored dependencies

The chart includes optional PostgreSQL, NATS, and Zitadel subcharts. All three are disabled by default:
  • Bitnami PostgreSQL with an 8Gi PVC and optional daily pg_dumpall backups
  • NATS with JetStream and a 10Gi file store
  • Zitadel for authentication
Use the vendored subcharts for local and test clusters. For production, use managed services with lifecycles separate from Filament.
Authentication is disabled by default. Set auth.enabled: true, auth.uiOrigin, and auth.zitadel.issuer to use Zitadel. An external Zitadel instance also requires auth.zitadel.pat or an AUTH_PAT entry in existingSecret. The vendored Zitadel setup mints its own token.
Do not use --wait when enabling the vendored Zitadel chart. Its setup Jobs must mint the server token before the server can become ready.

Chart reference

The Helm chart README lists every supported value and its default.