Installation
This guide walks you through deploying Zitadel on Kubernetes using Helm.
Prerequisites
You need a running Kubernetes cluster. Supported versions:
- Kubernetes 1.30.13+
- Kubernetes 1.31.9+
- Kubernetes 1.32.5+
- Kubernetes 1.33.1+
An Ingress Controller
Zitadel requires an ingress controller to route traffic to the Zitadel and Login pods. The chart creates standard Kubernetes Ingress resources that work with any ingress controller, including Traefik, NGINX, and cloud-native options like AWS ALB or GCP Cloud Load Balancer.
TLS must be terminated at the ingress controller. The Zitadel and Login containers do not handle TLS termination. Your ingress controller is responsible for obtaining and renewing TLS certificates (via ACME, cert-manager, or manually), terminating TLS connections, and forwarding traffic to the Zitadel pods.
Without an ingress controller and valid TLS certificates, Zitadel will not work. The chart configures the ingress to use HTTP/2 for communication between the ingress controller and Zitadel pods.
A PostgreSQL Database
Zitadel requires PostgreSQL 14 or later. You can use a managed PostgreSQL service such as Amazon RDS, Google Cloud SQL, or Azure Database for PostgreSQL, or run PostgreSQL in your cluster using a Helm chart or operator. The database must be accessible from the Zitadel pods.
Zitadel uses two database users: an admin user for schema migrations and an application user for normal operations. See the Database page for connection options.
Helm
Install Helm 3.x or 4.x following the official instructions. Verify your installation:
helm versionkubectl
Install kubectl following the Kubernetes documentation. Verify connectivity to your cluster:
kubectl cluster-infoAdd the Helm Repository
Add the Zitadel Helm repository:
helm repo add zitadel https://charts.zitadel.comUpdate your local repository cache:
helm repo updateVerify the repository was added:
helm search repo zitadelYou should see output similar to:
NAME CHART VERSION APP VERSION DESCRIPTION
zitadel/zitadel 9.x.x v4.x.x A Helm chart for ZitadelInstall
Step 1: Create Secrets
Create the masterkey secret:
kubectl create secret generic zitadel-masterkey \
--from-literal=masterkey="$(tr -dc A-Za-z0-9 </dev/urandom | head -c 32)"Create the database credentials secret:
kubectl create secret generic zitadel-db-credentials \
--from-literal=config.yaml="
Database:
Postgres:
User:
Password: your-secure-app-password
Admin:
Password: your-secure-admin-password
"Step 2: Configure Values
Create a values.yaml file. Replace zitadel.example.com with your actual domain:
replicaCount: 2
zitadel:
masterkeySecretName: zitadel-masterkey
configSecretName: zitadel-db-credentials
configmapConfig:
ExternalDomain: "zitadel.example.com"
ExternalSecure: true
ExternalPort: 443
TLS:
Enabled: false
Database:
Postgres:
Host: "postgres.database.svc.cluster.local"
Port: 5432
Database: "zitadel"
User:
Username: "zitadel"
SSL:
Mode: "verify-full"
Admin:
Username: "postgres"
SSL:
Mode: "verify-full"
FirstInstance:
Org:
Human:
UserName: "admin"
FirstName: "Zitadel"
LastName: "Admin"
Email: "admin@example.com"
Password: "YourSecurePassword123!"
PasswordChangeRequired: true
ingress:
enabled: true
className: traefik
annotations:
traefik.ingress.kubernetes.io/router.entrypoints: websecure
traefik.ingress.kubernetes.io/router.tls: "true"
traefik.ingress.kubernetes.io/router.tls.certresolver: letsencrypt
hosts:
- host: zitadel.example.com
paths:
- path: /
pathType: Prefix
tls:
- secretName: zitadel-tls
hosts:
- zitadel.example.com
podDisruptionBudget:
enabled: true
minAvailable: 1Step 3: Install the Chart
helm install zitadel zitadel/zitadel --values values.yamlInstall Zitadel:
helm install my-zitadel zitadel/zitadel --values values.yamlVerify Deployment
Watch the pods come up:
kubectl get pods --watchYou should see:
my-zitadel-init-*job completesmy-zitadel-setup-*job completesmy-zitadel-*deployment pods become Ready
Check the deployment status:
helm status my-zitadelVerify all pods are running:
kubectl get pods --selector app.kubernetes.io/name=zitadelYou should see output similar to:
NAME READY STATUS RESTARTS AGE
my-zitadel-xxxxxxxxx-xxxxx 1/1 Running 0 5m
my-zitadel-xxxxxxxxx-xxxxx 1/1 Running 0 5mAccess the UI
Access Zitadel at:
https://zitadel.example.com/ui/consoleFirst Login
Access the console at https://zitadel.example.com/ui/console and log in with the admin credentials configured in your values.yaml.
Next Steps
- Configuration — Customize Zitadel settings
- Configuring the Ingress — Set up ingress for Zitadel and Login containers
- Configuring the Database — Connect to PostgreSQL
- Operations — Learn about upgrades and scaling
- Uninstalling — Remove Zitadel from your cluster
Was this page helpful?