--- title: Deploy ZITADEL on Kubernetes sidebar_label: Overview description: Deploy ZITADEL on Kubernetes using Helm — from a local 5-minute quickstart to a hardened production cluster. --- This guide takes you from zero to a running ZITADEL instance on Kubernetes and then shows you how to harden it for production. ## Stage 1 — Quickstart The ZITADEL chart ships with an optional PostgreSQL subchart so a single `helm install` deploys the database alongside ZITADEL. You still need a routing controller running in your cluster to expose the service. ### Prerequisites - A Kubernetes cluster (1.30+) - An Ingress controller (e.g., Traefik, NGINX) **OR** a Gateway API controller - [kubectl](https://kubernetes.io/docs/tasks/tools/) - [Helm](https://helm.sh/docs/intro/install/) 3.x or 4.x No cluster yet? [k3d](https://k3d.io/) is a quick way to spin one up locally — it runs k3s in Docker, with Traefik as the built-in ingress controller: ```bash k3d cluster create zitadel --port "80:80@loadbalancer" ``` ### Install the full stack ```bash mkdir zitadel-helm && cd zitadel-helm && curl -fsSLO https://raw.githubusercontent.com/zitadel/zitadel-charts/main/examples/0-quickstart/quickstart-values.yaml ``` Edit `quickstart-values.yaml` before installing. For a local k3d or k3s cluster, these standard Ingress values usually work as-is: ```yaml zitadel: configmapConfig: ExternalDomain: localhost ExternalPort: 80 ingress: className: traefik login: ingress: className: traefik ``` If you are using a different ingress controller, replace both `className` values with that controller's IngressClass name. ```bash helm repo add zitadel https://charts.zitadel.com && helm repo update && helm upgrade --install zitadel zitadel/zitadel --values quickstart-values.yaml --wait ``` That's it. Visit [http://localhost/ui/console?login_hint=zitadel-admin@zitadel.localhost](http://localhost/ui/console?login_hint=zitadel-admin@zitadel.localhost) and log in with password `Password1!`. The masterkey [encrypts sensitive data at rest](/concepts/architecture/secrets). The `quickstart-values.yaml` file contains a **dev placeholder** masterkey — generate a real one before any non-local deployment: ```bash tr -dc A-Za-z0-9 The stack runs: your ingress/gateway controller → **ZITADEL API** (Go) + **ZITADEL Login** (Next.js) → **PostgreSQL** (bundled). See [HTTP/2](/self-hosting/manage/http2) for requirements on how traffic must be forwarded to ZITADEL pods via h2c. ### Swap out components The bundled PostgreSQL subchart is for quickstart use only. You can replace it independently: | Component | How to replace | |-----------|---------------| | **Database** | Set `postgresql.enabled: false` and configure `ZITADEL_DATABASE_POSTGRES_DSN` pointing to your own PostgreSQL. Use the `postgres` maintenance database so ZITADEL can create its own database during initialization. | | **Routing / Ingress** | Update `ingress.className` to match your controller, or disable the Helm ingress (`ingress.enabled: false`) to manage your own Gateway API `HTTPRoute` resources. | | **Caching** | Add Redis by configuring `zitadel.configmapConfig.Caches` (see [Caching](/self-hosting/deploy/kubernetes/caching)). | ## Stage 2 — Production Cluster For production you need: - A Kubernetes cluster (1.30+) - A routing solution: Ingress controller (Traefik, NGINX) or a Gateway API implementation - A domain with DNS configured - TLS certificates (via cert-manager, ACME, or manually) - PostgreSQL 14+ (managed service or in-cluster) - Helm 3.x+ **PostgreSQL compatibility:** ZITADEL supports PostgreSQL **14–18**. When using **PostgreSQL 18**, ensure you run **ZITADEL v4.11.0 or newer**. For the most up-to-date compatibility matrix and configuration details, see [Database requirements](/self-hosting/manage/database). The ZITADEL management console [requires end-to-end HTTP/2 support](/self-hosting/manage/http2). Ensure your routing controller is configured to forward HTTP/2 (h2c) traffic to ZITADEL pods. ### Create secrets ```bash # Masterkey — generate once, store safely, cannot be changed after first run kubectl create secret generic zitadel-masterkey \ --from-literal=masterkey="$(tr -dc A-Za-z0-9