Skip to content

Deploying Sulu on Kubernetes

Sulu ships a Helm chart that installs the backend, frontend, landing site, docs, and all required infrastructure into a Kubernetes cluster. The chart is published to GHCR as an OCI artifact and is versioned in lockstep with the Sulu Docker images.

Prerequisites

  • Kubernetes 1.27 or newer
  • Helm 3.14 or newer (older versions don't support OCI registries fully)
  • An ingress controller (we recommend ingress-nginx)
  • A TLS certificate provisioner (we recommend cert-manager)
  • A Sulu pull token (issued by your Sulu account team)

Quick start

1. Authenticate to GHCR

bash
# For Docker images
echo "$SULU_PULL_TOKEN" | docker login ghcr.io -u <your-handle> --password-stdin

# For the Helm chart (OCI)
echo "$SULU_PULL_TOKEN" | helm registry login ghcr.io -u <your-handle> --password-stdin

2. Create the image-pull secret in your cluster

bash
kubectl create namespace sulu
kubectl create secret docker-registry sulu-ghcr-creds \
  --docker-server=ghcr.io \
  --docker-username=<your-handle> \
  --docker-password=$SULU_PULL_TOKEN \
  --namespace sulu

3. Create your overrides file

yaml
# my-overrides.yaml
suluSecretKey: "<output of: openssl rand -base64 32>"

bootstrapAdmin:
  email: "[email protected]"
  password: "<output of: openssl rand -base64 24>"

publicUrl: "https://sulu.yourcompany.example"

ingress:
  className: "nginx"
  tls:
    enabled: true
    secretName: "sulu-tls"
  hosts:
    app: "sulu.yourcompany.example"
    api: "api.sulu.yourcompany.example"
    landing: "www.yourcompany.example"
    docs: "docs.sulu.yourcompany.example"

smtp:
  host: "smtp.yourcompany.example"
  port: 587
  user: "[email protected]"
  password: "<smtp-password>"
  from: "[email protected]"

4. Install the chart

bash
helm install sulu oci://ghcr.io/ellyzz/charts/sulu \
  --version 1.0.0 \
  --namespace sulu \
  -f my-overrides.yaml

Helm will:

  1. Pull the chart from GHCR
  2. Resolve the bundled Bitnami subcharts (Postgres + RabbitMQ + MinIO)
  3. Run a pre-install Job that waits for Postgres and applies Flyway migrations
  4. Roll out backend, frontend, landing, and docs Deployments
  5. Render an Ingress with the four hosts you configured

Watch the rollout:

bash
kubectl --namespace sulu get pods -l app.kubernetes.io/instance=sulu --watch

5. Verify

bash
helm test sulu --namespace sulu

This runs a smoke probe against /actuator/health/readiness on the backend.

Bring your own infrastructure

If your platform already provides managed Postgres, RabbitMQ, and S3, disable the bundled subcharts:

bash
helm install sulu oci://ghcr.io/ellyzz/charts/sulu \
  --version 1.0.0 \
  --namespace sulu \
  -f my-overrides.yaml \
  -f values-examples/byo-deps.yaml

The values-byo-deps.yaml example wires the backend to external services. You'll need to create Kubernetes Secrets for the credentials beforehand:

bash
kubectl create secret generic sulu-postgres-creds \
  --from-literal=password='<your-postgres-password>' \
  --namespace sulu

Autoscaling

Both backend and frontend support horizontal pod autoscaling. Enable in your overrides:

yaml
backend:
  autoscaling:
    enabled: true
    minReplicas: 2
    maxReplicas: 10
    targetCPUUtilizationPercentage: 70

frontend:
  autoscaling:
    enabled: true
    minReplicas: 2
    maxReplicas: 5

You'll need the metrics-server running in your cluster for HPA to read pod metrics.

NetworkPolicy

To restrict inter-pod traffic, enable NetworkPolicy:

yaml
networkPolicy:
  enabled: true

This applies two policies:

  1. Frontend/landing/docs accept ingress only from namespace ingress-nginx
  2. Backend accepts ingress from frontend pods

You'll need a CNI plugin that enforces NetworkPolicy (Calico, Cilium, etc.).

Upgrades

bash
helm upgrade sulu oci://ghcr.io/ellyzz/charts/sulu \
  --version 1.1.0 \
  --namespace sulu \
  -f my-overrides.yaml

The pre-upgrade hook runs Flyway migrations before any pod restart. If migrations fail, the upgrade aborts cleanly and no pods are recreated.

Uninstall

bash
helm uninstall sulu --namespace sulu
kubectl delete namespace sulu

PersistentVolumeClaims belonging to the bundled Postgres/RabbitMQ/MinIO are NOT auto-deleted. Delete them explicitly if you want to wipe data:

bash
kubectl delete pvc -l app.kubernetes.io/instance=sulu --namespace sulu

Troubleshooting

SymptomLikely causeFix
Image pull errorsPull secret missing or wrongRe-create sulu-ghcr-creds (see step 2).
Pre-install Job stuckPostgres not reachableCheck kubectl logs job/sulu-migrate-<rev> and the Postgres pod.
Backend CrashLoopBackOffsuluSecretKey or bootstrapAdmin missingRe-run with --set values, or fix overrides file.
Ingress 502Backend not ready yet, or hostname mismatchkubectl get ingress -n sulu and kubectl describe.

Reference

Sulu Test Management System