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