mirror of
https://github.com/andwati/zitadel.git
synced 2026-10-20 14:34:25 +00:00
222 lines
7.6 KiB
Plaintext
222 lines
7.6 KiB
Plaintext
---
|
||
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 an ingress controller running in your cluster.
|
||
|
||
### Prerequisites
|
||
|
||
- A Kubernetes cluster (1.30+)
|
||
- An ingress controller (e.g. Traefik, NGINX)
|
||
- [kubectl](https://kubernetes.io/docs/tasks/tools/)
|
||
- [Helm](https://helm.sh/docs/intro/install/) 3.x or 4.x
|
||
|
||
<Callout type="info">
|
||
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"
|
||
```
|
||
</Callout>
|
||
|
||
### 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 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/[email protected]](http://localhost/ui/[email protected]) and log in with password `Password1!`.
|
||
|
||
<Callout type="warn">
|
||
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 </dev/urandom | head -c 32
|
||
```
|
||
Once ZITADEL has been initialized with a masterkey, it **cannot be changed** without losing access to encrypted data.
|
||
</Callout>
|
||
|
||
<Callout type="info">
|
||
The stack runs: your ingress controller → **ZITADEL API** (Go) + **ZITADEL Login** (Next.js) → **PostgreSQL** (bundled).
|
||
See [HTTP/2](/self-hosting/manage/http2) for requirements on how the ingress controller must forward traffic to ZITADEL pods.
|
||
</Callout>
|
||
|
||
### 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 |
|
||
| **Ingress controller** | Update `ingress.className` (and `login.ingress.className`) to match your controller's IngressClass |
|
||
| **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+)
|
||
- An ingress controller (Traefik, NGINX, or cloud-native)
|
||
- A domain with DNS configured
|
||
- TLS certificates (via cert-manager, ACME, or manually)
|
||
- PostgreSQL 14+ (managed service or in-cluster)
|
||
- Helm 3.x+
|
||
|
||
<Callout type="warn">
|
||
**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).
|
||
</Callout>
|
||
|
||
<Callout type="warn">
|
||
The ZITADEL management console [requires end-to-end HTTP/2 support](/self-hosting/manage/http2). Ensure your ingress controller is configured to forward HTTP/2 (h2c) traffic to ZITADEL pods.
|
||
</Callout>
|
||
|
||
### 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 </dev/urandom | head -c 32)"
|
||
|
||
# Database credentials — store the full DSN
|
||
kubectl create secret generic zitadel-db-credentials \
|
||
--from-literal=dsn="postgresql://zitadel:[email protected]:5432/postgres?sslmode=verify-full"
|
||
```
|
||
|
||
### Configure values
|
||
|
||
Save the following as `values.yaml`. Replace `zitadel.example.com` with your domain:
|
||
|
||
```yaml
|
||
replicaCount: 2
|
||
|
||
zitadel:
|
||
masterkeySecretName: zitadel-masterkey
|
||
env:
|
||
- name: ZITADEL_DATABASE_POSTGRES_DSN
|
||
valueFrom:
|
||
secretKeyRef:
|
||
name: zitadel-db-credentials
|
||
key: dsn
|
||
configmapConfig:
|
||
ExternalDomain: "zitadel.example.com"
|
||
ExternalSecure: true
|
||
ExternalPort: 443
|
||
TLS:
|
||
Enabled: false
|
||
FirstInstance:
|
||
Org:
|
||
Human:
|
||
UserName: "admin"
|
||
Email: "[email protected]"
|
||
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
|
||
|
||
login:
|
||
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: /ui/v2/login
|
||
pathType: Prefix
|
||
tls:
|
||
- secretName: zitadel-tls
|
||
hosts:
|
||
- zitadel.example.com
|
||
|
||
podDisruptionBudget:
|
||
enabled: true
|
||
minAvailable: 1
|
||
```
|
||
|
||
### Install
|
||
|
||
```bash
|
||
helm repo add zitadel https://charts.zitadel.com && helm repo update
|
||
|
||
helm install zitadel zitadel/zitadel --values values.yaml --wait
|
||
```
|
||
|
||
### Verify
|
||
|
||
Watch the pods come up:
|
||
|
||
```bash
|
||
kubectl get pods --watch
|
||
```
|
||
|
||
You should see the `zitadel-init` and `zitadel-setup` jobs complete, followed by the `zitadel` deployment pods becoming Ready.
|
||
|
||
Check the Helm release status:
|
||
|
||
```bash
|
||
helm status zitadel
|
||
```
|
||
|
||
Access the console at `https://zitadel.example.com/ui/console`.
|
||
|
||
## What's next
|
||
|
||
Review the [Production Checklist](/self-hosting/manage/productionchecklist) before going live, then use the detailed guides for each concern:
|
||
|
||
| Guide | Description |
|
||
|-------|-------------|
|
||
| [Configuration](/self-hosting/deploy/kubernetes/configuration) | All configmap and secret options, autoscaling, security contexts |
|
||
| [Ingress](/self-hosting/deploy/kubernetes/ingress) | Traefik, NGINX, and cloud-native ingress setup |
|
||
| [Database](/self-hosting/deploy/kubernetes/database) | PostgreSQL TLS modes, credentials, and connection pooling |
|
||
| [Operations](/self-hosting/deploy/kubernetes/operations) | Upgrades, manual and automatic scaling, resource limits |
|
||
| [Caching](/self-hosting/deploy/kubernetes/caching) | Redis/Valkey caching configuration |
|
||
| [Observability](/self-hosting/deploy/kubernetes/observability) | Traces (OTLP), Prometheus metrics, and log collection |
|
||
| [Uninstalling](/self-hosting/deploy/kubernetes/uninstalling) | Remove ZITADEL from your cluster |
|