Deploying FlowForge on Kubernetes¶
FlowForge ships a Helm chart at deployment/kubernetes/helm/flowforge
and a rendered copy of its default output at
deployment/kubernetes/manifests/flowforge.yaml. The chart is the source of
truth; the rendered manifest is committed for operators who prefer
kubectl apply over Helm and is regenerated with helm template whenever the
chart changes.
What gets deployed¶
| Resource | Kind | Notes |
|---|---|---|
flowforge |
Namespace | The target namespace. |
flowforge-secret |
Secret | Created only if existingSecret is empty. Holds credentials by reference. |
flowforge-config |
ConfigMap | Non-sensitive settings (settings module, throttle rates, …). |
flowforge-migrate |
Job (Helm hook) | Runs manage.py migrate before workloads start. |
flowforge-web |
Deployment + Service | Gunicorn API; /health/live/ and /health/ready/ probes. |
flowforge-scheduler |
Deployment | Runs manage.py scheduler. |
flowforge-publisher |
Deployment | Runs manage.py publisher. |
flowforge-worker |
Deployment + HPA + PDB | Runs manage.py worker; autoscaled; disruption-budgeted. |
| NetworkPolicy | ×2 | Default-deny ingress; ingress allowed to web only. |
Every workload runs as a non-root user with a read-only root filesystem,
dropped capabilities, seccompProfile: RuntimeDefault, resource requests/limits,
and an init container that blocks startup until manage.py migrate --check
passes — so no pod serves traffic against an un-migrated schema.
Prerequisites¶
The chart does not deploy PostgreSQL, RabbitMQ or Redis. Create them in-cluster (e.g. the Bitnami charts) or point the credentials at managed services before installing — see High Availability for how to make those three survive a failure. Then provide credentials via a Secret:
kubectl create secret generic flowforge-secret -n flowforge \
--from-literal=django-secret-key="$(openssl rand -hex 32)" \
--from-literal=database-url="postgres://flowforge:PASSWORD@postgres:5432/flowforge" \
--from-literal=rabbitmq-url="amqp://flowforge:PASSWORD@rabbitmq:5672/" \
--from-literal=redis-url="redis://redis:6379/0"
Install with Helm¶
Set the image tag. image.tag is empty by default, which makes the chart fall
back to .Chart.AppVersion — and CI publishes only main and main-<commit-sha>
to ghcr.io/abolfazlrwm/flowforge, never a per-version tag. Without
--set image.tag=… the pods will fail to pull. Use a main-<sha> tag or, better,
a digest:
IMAGE_TAG="main-$(git rev-list -n1 HEAD)" # or sha256:... for a pinned deploy
helm install flowforge deployment/kubernetes/helm/flowforge \
--namespace flowforge --create-namespace \
--set existingSecret=flowforge-secret \
--set image.tag="$IMAGE_TAG" \
--set config.allowedHosts="api.example.com"
Or, if you let the chart create its own Secret (not for production):
helm install flowforge deployment/kubernetes/helm/flowforge \
--namespace flowforge --create-namespace \
--set image.tag="$IMAGE_TAG" \
--set secret.databaseUrl="postgres://..." --set secret.rabbitmqUrl="..." \
--set secret.redisUrl="..." --set secret.djangoSecretKey="$(openssl rand -hex 32)"
Install with kubectl (no Helm)¶
kubectl apply -f deployment/kubernetes/manifests/flowforge.yaml
# then patch/replace the Secret with real values, or delete the chart-managed
# Secret and create your own named flowforge-secret before applying.
The committed manifest is the chart's default output, so it pins
image: ghcr.io/abolfazlrwm/flowforge:1.0.0 — a tag CI does not publish. Replace
the image with a main-<sha> tag or a digest before applying (or render the chart
with --set image.tag=…, which is the supported path).
Validation¶
helm lint and kubeconform -strict run in CI on every push (see the k8s
job in .github/workflows/ci.yml). A kind-based end-to-end smoke test is
available on demand via .github/workflows/k8s-e2e.yml (manual dispatch) and
scripts/kind-e2e.sh.