ZITADEL Docs
Deploy & OperateSelf-HostedKubernetes

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 version

kubectl

Install kubectl following the Kubernetes documentation. Verify connectivity to your cluster:

kubectl cluster-info

Add the Helm Repository

Add the Zitadel Helm repository:

helm repo add zitadel https://charts.zitadel.com

Update your local repository cache:

helm repo update

Verify the repository was added:

helm search repo zitadel

You should see output similar to:

NAME            CHART VERSION   APP VERSION     DESCRIPTION
zitadel/zitadel 9.x.x           v4.x.x          A Helm chart for Zitadel

Install

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: 1

Step 3: Install the Chart

helm install zitadel zitadel/zitadel --values values.yaml

Install Zitadel:

helm install my-zitadel zitadel/zitadel --values values.yaml

Verify Deployment

Watch the pods come up:

kubectl get pods --watch

You should see:

  1. my-zitadel-init-* job completes
  2. my-zitadel-setup-* job completes
  3. my-zitadel-* deployment pods become Ready

Check the deployment status:

helm status my-zitadel

Verify all pods are running:

kubectl get pods --selector app.kubernetes.io/name=zitadel

You 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          5m

Access the UI

Access Zitadel at:

https://zitadel.example.com/ui/console

First 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

Was this page helpful?

On this page