zitadel

zitadel

Official

Docker app from Nebur692's Repository

Overview

ZITADEL is a self-hosted identity infrastructure platform: SSO (OIDC/SAML), MFA, and user management for your own apps and services. This template deploys the ZITADEL v4 core/API service only. Since v4, the interactive login pages were split into a separate service (zitadel-login) that MUST be deployed alongside this one, or logging in will fail with a 404 on /ui/v2/login/login. See the companion "zitadel-login" template from the same repository. Requires an external Postgres 14+ database (not included in this template) and a 32-character Master Key. IMPORTANT: besides filling in the "Master Key" field below, you MUST also replace the placeholder in "Post Arguments" (under Extra Parameters, near the bottom of the Add Container page) with that exact same key — ZITADEL has a known bug where the environment variable alone isn't reliably read with the start-from-init command, and the container will crash-loop with "no master key provided" if you skip this. Default admin login after first start: zitadel-admin@zitadel.your external domain / Password1! — you will be asked to change this password on first login (unless you disable that below). Full setup guide: https://github.com/Nebur692/Unraid-sso-templates

ZITADEL Logo ZITADEL Logo

GitHub Workflow Status (with event) GitHub contributors

The Identity Infrastructure for Developers

ZITADEL is an open-source identity and access management platform built for teams that need more than basic auth. Whether you're securing a SaaS product, building a B2B platform, or self-hosting a production IAM stack — ZITADEL gives you everything out of the box: SSO, MFA, Passkeys, OIDC, SAML, SCIM, and a battle-tested multi-tenancy model.

No vendor lock-in. No compromise on control. Just a robust, API-first identity platform you can own.


🏡 Website  |  💬 Chat  |  📋 Docs  |  🧑‍💻 Blog  |  📞 Contact


Why ZITADEL

We built ZITADEL to handle the hardest IAM challenges at scale — starting with multi-tenancy.

ZITADEL FusionAuth Keycloak Auth0/Okta
Open-source ✅ ❌ ✅ ❌
Self-hostable ✅ ✅ ✅ ❌
Infrastructure-level tenants ✅ Instances (High scale) ✅ Tenants 🟡 Realms (Scaling limits) ❌ (Multi-tenant = multi-account)
B2B Organizations ✅ Native & Unlimited 🟡 via Entity Management ✅ (Recent addition) 🟡 (Plan/Account dependent)
Full audit trail ✅ Comprehensive Event Stream* 🟡 Audit logs 🟡 Audit logs 🟡 Audit logs
Passkeys (FIDO2) ✅ ✅ ✅ ✅
Actions / webhooks ✅ ✅ 🟡 via SPI ✅
API-first (gRPC + REST) ✅ 🟡 REST only 🟡 REST only 🟡 REST only
SaaS + self-host parity ✅ ✅ ➖ N/A ➖ N/A

ZITADEL Cloud and self-hosted ZITADEL run the same codebase.

Key differentiators for architects:

  • Relational core, event-driven soul — every mutation is written as an immutable event for a complete, API-accessible audit trail. Unlike systems that log only select activities, ZITADEL provides a comprehensive event stream that can be audited or streamed to external systems via Webhooks.
  • Strict multi-tenant hierarchy — Identity System → Organizations → Projects, with isolated data and policy scoping at multiple levels
  • API-first design — every resource and action is available via connectRPC, gRPC, and HTTP/JSON APIs
  • Zero-downtime updates and horizontal scalability without external session stores

Get Started in 3 Minutes

👉 Quick Start Guide

ZITADEL Self-Hosted

# Docker Compose — up and running in under 3 minutes
curl -LO https://raw.githubusercontent.com/zitadel/zitadel/main/deploy/compose/docker-compose.yml \
  && curl -LO https://raw.githubusercontent.com/zitadel/zitadel/main/deploy/compose/.env.example \
  && cp .env.example .env \
  && docker compose up -d --wait

Full deployment guides:

Need professional support for your self-hosted deployment? Contact us.

ZITADEL Cloud (SaaS)

Start for free at zitadel.com — no credit card required. Available in US · EU · AU · CH. Pay-as-you-go pricing.


Integrate with the V2 API

ZITADEL exposes every capability over a typed API. Here's how to create a user with the V2 REST API:

curl -X POST https://$ZITADEL_DOMAIN/v2/users/human \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "username": "alice@example.com",
    "profile": { "givenName": "Alice", "familyName": "Smith" },
    "email": { "email": "alice@example.com", "sendCode": {} }
  }'

Explore the full API reference — including connectRPC and gRPC transports — or jump straight to quickstart examples.


Features

Authentication

Multi-Tenancy

Integration

Self-Service & Admin

Deployment

Track upcoming features on our roadmap and follow our changelog for recent updates.


Showcase

Login V2

Our new, fully customizable login experience — documentation


Adopters & Ecosystem

Used in production by organizations worldwide. See the full Adopters list — and add yours by submitting a pull request.


How To Contribute

ZITADEL is built in the open and welcoming to contributions of all kinds.

Contributors

Made with contrib.rocks.


Security

Security policy: SECURITY.md

Vulnerability Disclosure Policy — how to responsibly report security issues.

Technical Advisories are published for major issues that could impact security or stability in production.

License

AGPL-3.0 — see LICENSING.md for the full licensing policy, including Apache 2.0 and MIT exceptions for specific directories.

Requirements

An external PostgreSQL 14+ database, reachable from this container, plus the companion "zitadel-login" template for the interactive login pages to work.

Related apps

Details

Repository
ghcr.io/zitadel/zitadel:latest
Last Updated2026-10-12
First Seen2024-11-03

Runtime arguments

Web UI
http://[IP]:[PORT:8080]/ui/console
Network
bridge
Shell
sh
Privileged
false
Extra Params
--restart unless-stopped

Template configuration

Master KeyVariable

REQUIRED. Exactly 32 characters, used to encrypt secrets at rest. Generate once, keep it safe, never change it after first start (you will lose access to encrypted data). Example: openssl rand -base64 24 | cut -c1-32. IMPORTANT: ZITADEL has a known bug where this environment variable alone is not always read with the start-from-init command — you MUST also replace the placeholder in Post Arguments (Extra Parameters section) with this exact same value, or the container will crash-loop with 'no master key provided'.

Target
ZITADEL_MASTERKEY
External DomainVariable

REQUIRED. The public domain users will use to reach ZITADEL (e.g. sso.example.com), without protocol or port. Must match what your reverse proxy serves.

Target
ZITADEL_EXTERNALDOMAIN
Default
localhost
Value
localhost
External PortVariable

The public-facing port your reverse proxy exposes ZITADEL on (usually 443 for HTTPS). This is NOT the container's internal port.

Target
ZITADEL_EXTERNALPORT
Default
443
Value
443
External Secure (HTTPS)Variable

Set to true if users reach ZITADEL over HTTPS (normal case, e.g. behind Nginx Proxy Manager with a valid certificate). Set to false only for plain-HTTP test setups.

Target
ZITADEL_EXTERNALSECURE
Default
true
Value
true
TLS Enabled (container-internal)Variable

Whether ZITADEL itself terminates TLS inside the container. Leave false if a reverse proxy (Nginx Proxy Manager, Traefik, Caddy...) handles HTTPS in front of it, which is the recommended setup.

Target
ZITADEL_TLS_ENABLED
Default
false
Value
false
Database Postgres HostVariable

REQUIRED. Hostname or IP of your external Postgres server/container.

Target
ZITADEL_DATABASE_POSTGRES_HOST
Database Postgres PortVariable

Postgres port.

Target
ZITADEL_DATABASE_POSTGRES_PORT
Default
5432
Value
5432
Database NameVariable

Database name ZITADEL will use/create.

Target
ZITADEL_DATABASE_POSTGRES_DATABASE
Default
zitadel
Value
zitadel
Database Admin UsernameVariable

Username with rights to create the app role/database on first start. IMPORTANT: if your Postgres container was created with a custom POSTGRES_USER (instead of leaving it as the default 'postgres'), that custom user IS your superuser — put that same username here, not 'postgres'. Using the wrong one causes a password authentication failed / restart loop.

Target
ZITADEL_DATABASE_POSTGRES_ADMIN_USERNAME
Default
postgres
Value
postgres
Database Admin PasswordVariable

Password for the admin username above.

Target
ZITADEL_DATABASE_POSTGRES_ADMIN_PASSWORD
Database Admin SSL ModeVariable

SSL mode for the admin connection (disable/require/verify-ca/verify-full).

Target
ZITADEL_DATABASE_POSTGRES_ADMIN_SSL_MODE
Default
disable
Value
disable
Database App UsernameVariable

Username ZITADEL uses day-to-day (created automatically on first start using the admin credentials above, unless it's the same as the admin username).

Target
ZITADEL_DATABASE_POSTGRES_USER_USERNAME
Default
zitadel
Value
zitadel
Database App PasswordVariable

Password for the day-to-day database user above.

Target
ZITADEL_DATABASE_POSTGRES_USER_PASSWORD
Database App SSL ModeVariable

SSL mode for the app connection (disable/require/verify-ca/verify-full).

Target
ZITADEL_DATABASE_POSTGRES_USER_SSL_MODE
Default
disable
Value
disable
Require Password Change On First LoginVariable

Whether the default admin account must change its password on first login. Recommended: true. Only has effect on the very first start.

Target
ZITADEL_FIRSTINSTANCE_ORG_HUMAN_PASSWORDCHANGEREQUIRED
Default
true
Value
true
Login Client PAT Path (bootstrap)Variable

CRITICAL, do not change. On first start, ZITADEL writes here the access token that the companion zitadel-login container needs to function. Must point inside the shared bootstrap volume below.

Target
ZITADEL_FIRSTINSTANCE_LOGINCLIENTPATPATH
Default
/zitadel/bootstrap/login-client.pat
Value
/zitadel/bootstrap/login-client.pat
Login Client Machine UsernameVariable

Do not change unless you know why. Internal service-account username created on first start for the login UI to authenticate as.

Target
ZITADEL_FIRSTINSTANCE_ORG_LOGINCLIENT_MACHINE_USERNAME
Default
login-client
Value
login-client
Login Client Machine NameVariable

Display name for the internal login-client service account.

Target
ZITADEL_FIRSTINSTANCE_ORG_LOGINCLIENT_MACHINE_NAME
Default
Automatically Initialized IAM_LOGIN_CLIENT
Value
Automatically Initialized IAM_LOGIN_CLIENT
Login Client PAT Expiration DateVariable

CRITICAL, do not leave empty: without a valid RFC3339 date here, ZITADEL silently skips writing the login-client.pat file entirely (no error logged) and zitadel-login will never work. The default is ~100 years out, effectively 'never expires' for practical purposes.

Target
ZITADEL_FIRSTINSTANCE_ORG_LOGINCLIENT_PAT_EXPIRATIONDATE
Default
2124-01-01T00:00:00Z
Value
2124-01-01T00:00:00Z
Login V2 RequiredVariable

Do not change: v4 always requires the new login UI. Kept visible for transparency.

Target
ZITADEL_DEFAULTINSTANCE_FEATURES_LOGINV2_REQUIRED
Default
true
Value
true
Login V2 Base URIVariable

REQUIRED. Full public URL of the login pages, e.g. https://sso.example.com/ui/v2/login/ — must match your External Domain/Port/Secure settings above, with the trailing slash.

Target
ZITADEL_DEFAULTINSTANCE_FEATURES_LOGINV2_BASEURI
OIDC Default Login URL V2Variable

REQUIRED. e.g. https://sso.example.com/ui/v2/login/login?authRequest=

Target
ZITADEL_OIDC_DEFAULTLOGINURLV2
OIDC Default Logout URL V2Variable

REQUIRED. e.g. https://sso.example.com/ui/v2/login/logout?post_logout_redirect=

Target
ZITADEL_OIDC_DEFAULTLOGOUTURLV2
SAML Default Login URL V2Variable

REQUIRED if you plan to use SAML (e.g. Zabbix). e.g. https://sso.example.com/ui/v2/login/login?samlRequest=

Target
ZITADEL_SAML_DEFAULTLOGINURLV2
Bootstrap Volume (shared with zitadel-login)Pathrw

Shared with the zitadel-login container (mounted read-only there) so it can read the login-client.pat token written here on first start. Both templates must point at the exact same host path. IMPORTANT: ZITADEL runs internally as UID 1000. If Unraid auto-creates this folder on first start, it may end up owned by root/nobody without write access for UID 1000, causing the token to silently fail to write with no error in the log. If the folder stays empty after 'setup completed', run: chown -R 1000:1000 this path on the Unraid host, then wipe the Postgres database (the first-instance bootstrap only runs once per empty database) and restart.

Target
/zitadel/bootstrap
Default
/mnt/user/appdata/zitadel/bootstrap
Value
/mnt/user/appdata/zitadel/bootstrap
Web UI / API PortPorttcp

Internal port. Put your reverse proxy in front of this — do not expose it directly to the internet without TLS.

Target
8080
Default
8080
Value
8080
HTTP Client Deny List (SSRF protection)Variable

ZITADEL blocks Actions V2 Target endpoints on private IP ranges by default (SSRF protection) — this INCLUDES 192.168.0.0/16 by default, which breaks creating a Target pointing at any container on a typical home LAN (fails with 'Errors.Target.DeniedURL'). This default value is the stock deny list with 192.168.0.0/16 removed, so LAN targets work while keeping protection against loopback, link-local, and cloud metadata endpoints (169.254.169.254) intact. Only widen this further if you understand the SSRF risk of doing so.

Target
ZITADEL_HTTPCLIENT_DENYLIST
Default
localhost,0.0.0.0/8,10.0.0.0/8,100.64.0.0/10,127.0.0.0/8,169.254.0.0/16,172.16.0.0/12,198.18.0.0/15,::/128,::1/128,fc00::/7,fe80::/10
Value
localhost,0.0.0.0/8,10.0.0.0/8,100.64.0.0/10,127.0.0.0/8,169.254.0.0/16,172.16.0.0/12,198.18.0.0/15,::/128,::1/128,fc00::/7,fe80::/10