mirror of
https://github.com/andwati/zitadel.git
synced 2026-10-22 07:24:26 +00:00
## Summary Rewrites the Kubernetes quickstart (Stage 1) to match the simplicity of the Docker Compose quickstart. With the new bundled chart, users deploy the full stack — PostgreSQL, Traefik, ZITADEL API, ZITADEL Login — with two copy-paste blocks and no prerequisites beyond a Kubernetes cluster. ## Before / After **Before:** - Requires a pre-installed ingress controller - 4 manual steps: install PostgreSQL separately, create secrets, wire DB config, install ZITADEL - Users need to understand Helm dependency management **After:** ```bash mkdir zitadel-helm && cd zitadel-helm && curl -fsSLO https://raw.githubusercontent.com/zitadel/zitadel-charts/main/examples/0-quickstart/quickstart-values.yaml ``` ```bash helm repo add zitadel https://charts.zitadel.com && helm repo add bitnami https://charts.bitnami.com/bitnami && helm repo add traefik https://traefik.github.io/charts && helm repo update && helm upgrade --install zitadel zitadel/zitadel --values quickstart-values.yaml --wait ``` Same UX as the Docker Compose quickstart. Mirrors the GitLab chart pattern. ## Changes to `kubernetes/index.mdx` ### Stage 1 — Quickstart (full rewrite) - Prerequisites reduced to: a Kubernetes cluster (1.30+), kubectl, Helm - k3d moved to an optional callout tip (no longer required) - Install section: `mkdir` → `curl` → `helm upgrade --install --wait` (two copy-paste blocks, same pattern as compose) - `helm upgrade --install` instead of `helm install` — idempotent, safe to re-run - "Swap out components" table: shows how to replace PostgreSQL, Traefik, or add Redis - Callouts: masterkey warning, stack architecture diagram ### Stage 2+ (unchanged) Production cluster setup, TLS, cert-manager, etc. are untouched. ## Related - Chart changes PR: [zitadel/zitadel-charts#560](https://github.com/zitadel/zitadel-charts/pull/560) --------- Co-authored-by: Copilot <[email protected]>
233 lines
7.4 KiB
Plaintext
233 lines
7.4 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.configmapConfig.Database` pointing to your own PostgreSQL |
|
|
| **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">
|
|
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
|
|
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
|
|
"
|
|
```
|
|
|
|
### Configure values
|
|
|
|
Save the following as `values.yaml`. Replace `zitadel.example.com` with your domain:
|
|
|
|
```yaml
|
|
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"
|
|
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 |
|