From 3fdac54878787290c987bfcad7152e6fa522ffe2 Mon Sep 17 00:00:00 2001 From: Florian Forster Date: Tue, 17 Mar 2026 03:36:23 -0700 Subject: [PATCH] docs: simplify helm chart guide (#11781) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 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 <223556219+Copilot@users.noreply.github.com> --- .../self-hosting/deploy/kubernetes/index.mdx | 245 +++++++++++++++--- .../deploy/kubernetes/installation.mdx | 226 ---------------- apps/docs/lib/sidebar-data.ts | 1 - 3 files changed, 207 insertions(+), 265 deletions(-) delete mode 100644 apps/docs/content/self-hosting/deploy/kubernetes/installation.mdx diff --git a/apps/docs/content/self-hosting/deploy/kubernetes/index.mdx b/apps/docs/content/self-hosting/deploy/kubernetes/index.mdx index 70397f8be..e603f44da 100644 --- a/apps/docs/content/self-hosting/deploy/kubernetes/index.mdx +++ b/apps/docs/content/self-hosting/deploy/kubernetes/index.mdx @@ -1,63 +1,232 @@ --- -title: Kubernetes +title: Deploy ZITADEL on Kubernetes sidebar_label: Overview -description: Deploy ZITADEL on Kubernetes using Helm charts for scalable identity management +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 -Deploy Zitadel, the open-source identity and access management platform, on Kubernetes using Helm. +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. -## What's in the Chart +### Prerequisites -By default, this chart installs a highly available Zitadel deployment. The chart deploys: +- 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 -- A Zitadel **init job** that prepares the database schema -- A Zitadel **setup job** that bootstraps the first instance and admin user -- A Zitadel **deployment** with configurable replicas for high availability + +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" +``` + -The execution order is orchestrated using Helm hooks on installations and upgrades. +### Install the full stack -## Architecture Overview +```bash +mkdir zitadel-helm && cd zitadel-helm && +curl -fsSLO https://raw.githubusercontent.com/zitadel/zitadel-charts/main/examples/0-quickstart/quickstart-values.yaml +``` -Zitadel is deployed as two separate containers within your Kubernetes cluster. The main Zitadel container handles all API traffic, the console UI, and OIDC endpoints, while the Login container serves the login UI at the `/ui/v2/login` path. Both containers are stateless and can be scaled horizontally to handle increased load. +Edit `quickstart-values.yaml` before installing. For a local k3d or k3s cluster, these values usually work as-is: -Traffic enters the cluster through an ingress controller such as Traefik or NGINX. The chart creates two separate Ingress resources, one for each container, both typically sharing the same domain and TLS certificate. +```yaml +zitadel: + configmapConfig: + ExternalDomain: localhost + ExternalPort: 80 -TLS termination happens at the ingress controller, which forwards unencrypted HTTP/2 traffic to the pods. cert-manager or the ingress controller's built-in ACME support can be used to automatically obtain and renew TLS certificates from Let's Encrypt. +ingress: + className: traefik -Zitadel stores all persistent data in PostgreSQL. The database holds user accounts, organizations, projects, applications, sessions, and all other identity data. Zitadel uses two database connections: an admin connection for schema migrations during startup and an application connection for normal operations. Both connections should use TLS in production environments. +login: + ingress: + className: traefik +``` -For observability, Zitadel pushes traces to an OpenTelemetry Collector using the OTLP protocol over gRPC. Prometheus scrapes metrics from the `/debug/metrics` endpoint on the Zitadel pods. Both containers write structured logs to stdout, which must be collected by a log collector such as Fluent Bit or Vector. The Login container does not support tracing or metrics, only log collection. All observability data can be forwarded to a unified backend such as Grafana, OpenObserve, or your preferred monitoring stack. +If you are using a different ingress controller, replace both `className` values with that controller's IngressClass name. -## Prerequisites at a Glance +```bash +helm repo add zitadel https://charts.zitadel.com && +helm repo update && +helm upgrade --install zitadel zitadel/zitadel --values quickstart-values.yaml --wait +``` -| Requirement | Minimum Version | -|-------------|-----------------| -| Kubernetes | 1.30+ | -| Helm | 3.x or 4.x | -| kubectl | 1.30+ | -| PostgreSQL | 14+ | +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!`. -You also need: + +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 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. + + +### 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 manual) +- TLS certificates (via cert-manager, ACME, or manually) +- PostgreSQL 14+ (managed service or in-cluster) +- Helm 3.x+ -## Quick Links + +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. + -| Resource | Link | -|----------|------| -| Helm Chart Repository | [charts.zitadel.com](https://charts.zitadel.com) | -| Source Code | [github.com/zitadel/zitadel-charts](https://github.com/zitadel/zitadel-charts) | -| Zitadel Documentation | [zitadel.com/docs](https://zitadel.com/docs) | -| Artifact Hub | [artifacthub.io/packages/helm/zitadel/zitadel](https://artifacthub.io/packages/helm/zitadel/zitadel) | +### Create secrets -## Next Steps +```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