Что такое ZITADEL и почему я на него вообще посмотрел#
Раз уж я по очереди прошелся почти по всем заметным self-hosted SSO/IAM решениям - Authentik, Authelia, Keycloak - было бы странно пропустить ZITADEL. Тем более что его довольно часто рекомендуют как “то, на что стоит смотреть, если вырос из Authelia, но Keycloak кажется монстром” - и мне стало интересно, так ли это на самом деле.
Разработала его швейцарская компания CAOS AG (Санкт-Галлен), сейчас проект живет под собственным брендом ZITADEL Inc., а CAOS AG осталась чем-то вроде местной операционной “дочки” - деталь скорее для справки, чем для практики. По сути это open-source IAM-платформа: управление пользователями, аутентификация, авторизация, причем в отличие от Authelia она не просит “принеси мне уже настроенный SSO-провайдер”, а сама и есть этот провайдер, со своей базой пользователей, MFA и панелью администратора.
Если хочется сразу понять, куда его ставить относительно остальных трех решений - у меня есть отдельное сравнение Authelia/Authentik/Keycloak/ZITADEL, там расклад по сценариям использования.
Если статья или видео оказались полезны - поддержать канал можно на бусти, ссылка в контактах.
Что внутри#
Смысла пересказывать документацию построчно нет, но пробежаться по тому, что реально важно, стоит.
Протоколы - OIDC, OAuth 2.0 и SAML 2.0 из коробки, то есть подружить с ним современное веб-приложение почти всегда не проблема.
Multi-tenant из коробки - можно завести несколько организаций внутри одного инстанса, у каждой свои пользователи и права. Для домашней лаборатории с одним человеком за пультом это, честно говоря, избыточно - но если вы, как и я, держите ZITADEL не только для себя, а еще и для родственников или небольшой команды, разделение на организации быстро становится удобным, а не просто галочкой в фиче-листе.
MFA - Email/OTP, TOTP (тот же Google Authenticator), SMS, WebAuthn (значит и YubiKey заходит). Настраивается и глобально, и на уровне отдельной организации.
API и CLI - REST, gRPC, свой zitadel CLI. Если вы из тех, кто автоматизирует управление доступом через GitOps/CI, тут есть на чем развернуться - но для обычного homelab-сценария вы, скорее всего, этим даже не воспользуетесь, и это нормально.
RBAC/ABAC - роли на уровне проектов, приложений, пользователей, плюс кастомные claims и scopes в токенах, если нужно передавать в приложение что-то нестандартное.
Развертывание - можно взять готовый облачный SaaS от самих разработчиков, а можно поднять self-hosted через Docker, Kubernetes или Podman. База - PostgreSQL или CockroachDB, третьего варианта не предусмотрено.
Как он смотрится на фоне соседей#
| Возможность | ZITADEL | Keycloak | Auth0 | Authelia / TinyAuth |
|---|---|---|---|---|
| Open Source | ✅ | ✅ | ❌ | ✅ |
| OIDC/SAML | ✅ | ✅ | ✅ | ❌/частично |
| MFA | ✅ | ✅ | ✅ | ❌/частично |
| Multi-tenant | ✅ | Частично | ✅ | ❌ |
| UI для self-service | ✅ | ❌ | ✅ | ❌ |
| API/CLI | ✅ | Частично | ✅ | ❌ |
Если совсем грубо: там, где Authelia просто открывает или закрывает дверь перед уже настроенным приложением, ZITADEL (как и Keycloak) сам является полноценным центром выдачи личности - со своей базой пользователей, ролями и токенами. Плата за это - он тяжелее и не такой “поставил и забыл”, как Authelia.
Разворачиваем#
Ниже - тот же docker-compose, что я использовал в видео:
services:
zitadel:
restart: "always"
container_name: zitadel
networks:
- zitadel
- proxy
image: "ghcr.io/zitadel/zitadel:v4.15.0" # не используйте :latest - зафиксируйте версию явно, актуальную смотрите на https://github.com/zitadel/zitadel/releases
command: 'start-from-init --masterkey "${MASTERKEY_32}" --tlsMode external'
env_file: .env
environment:
ZITADEL_DATABASE_POSTGRES_HOST: zitadel-db
ZITADEL_DATABASE_POSTGRES_PORT: 5432
ZITADEL_DATABASE_POSTGRES_DATABASE: zitadel
ZITADEL_DATABASE_POSTGRES_USER_USERNAME: ${DB_USER}
ZITADEL_DATABASE_POSTGRES_USER_PASSWORD: ${DB_PASSWORD}
ZITADEL_DATABASE_POSTGRES_USER_SSL_MODE: disable
ZITADEL_DATABASE_POSTGRES_ADMIN_USERNAME: ${DB_ADMIN_USER}
ZITADEL_DATABASE_POSTGRES_ADMIN_PASSWORD: ${DB_ADMIN_PASSWORD}
ZITADEL_DATABASE_POSTGRES_ADMIN_SSL_MODE: disable
ZITADEL_FIRSTINSTANCE_ORG_HUMAN_USERNAME: ${ZITADEL_LOGIN_USER}
ZITADEL_FIRSTINSTANCE_ORG_HUMAN_PASSWORD: ${ZITADEL_LOGIN_PASSWORD}
ZITADEL_FIRSTINSTANCE_ORG_HUMAN_PASSWORDCHANGEREQUIRED: false
ZITADEL_FIRSTINSTANCE_ORG_NAME: HomeLab
ZITADEL_PORT: 8080 #For External TLS (Also use h2c)
ZITADEL_EXTERNALPORT: 443
ZITADEL_EXTERNALDOMAIN: ${ZITADEL_SUBDOMAIN}.${DOMAIN_NAME} #eg. auth.DOMAIN_NAME
ZITADEL_EXTERNALSECURE: true
labels:
- "traefik.enable=true"
- "traefik.http.routers.zitadel.entrypoints=http"
- "traefik.http.routers.zitadel.rule=Host(`${ZITADEL_SUBDOMAIN}.${DOMAIN_NAME}`)"
- "traefik.http.middlewares.https-redirect.redirectscheme.scheme=https"
- "traefik.http.routers.zitadel.middlewares=https-redirect"
- "traefik.http.routers.zitadel-secure.entrypoints=https"
- "traefik.http.routers.zitadel-secure.rule=Host(`${ZITADEL_SUBDOMAIN}.${DOMAIN_NAME}`)"
- "traefik.http.routers.zitadel-secure.tls=true"
- "traefik.http.routers.zitadel-secure.service=zitadel"
- "traefik.http.services.zitadel.loadbalancer.server.scheme=h2c"
- "traefik.http.services.zitadel.loadbalancer.passHostHeader=true"
- "traefik.http.services.zitadel.loadbalancer.server.port=8080"
- "traefik.docker.network=proxy"
depends_on:
zitadel-db:
condition: "service_healthy"
zitadel-db:
restart: "always"
container_name: zitadel-db
image: postgres:17-alpine
env_file: .env
environment:
POSTGRES_USER: ${DB_ADMIN_USER}
POSTGRES_PASSWORD: ${DB_ADMIN_PASSWORD}
networks:
- zitadel
healthcheck:
test:
[
"CMD-SHELL",
"pg_isready",
"-d",
"zitadel",
"-U",
"${DB_ADMIN_USER}",
]
interval: "10s"
timeout: "30s"
retries: 5
start_period: "20s"
volumes:
- ${DB_LOCATION}:/var/lib/postgresql/data
networks:
zitadel:
name: zitadel
proxy:
external: true
name: proxyИ рядом .env с переменными:
MASTERKEY_32=GENERATE_RANDOM_KEY_LENGTH_32_ChangeMe #Replace Master Key (tr -dc A-Za-z0-9 </dev/urandom | head -c 32)
DB_USER=Enter_DB_User_ChangeMe
DB_PASSWORD=Enter_DB_Password_ChangeMe
DB_ADMIN_USER=Enter_DB_Root_User_ChangeMe
DB_ADMIN_PASSWORD=Enter_DB_Root_Password_ChangeMe
ZITADEL_LOGIN_USER=Loginusername@Your_Domain_ChangeMe.com
ZITADEL_LOGIN_PASSWORD=Zitadel_Password_ChangeMe
ZITADEL_SUBDOMAIN=zitadel
DOMAIN_NAME=Your_Domain_ChangeMe.com
DB_LOCATION=/path/to/directory/zitadel/zitadel-dbПара моментов, на которых реально можно застрять:
MASTERKEY_32- должен быть ровно 32 символа, не больше и не меньше. Генерируется командой из комментария рядом (tr -dc A-Za-z0-9 </dev/urandom | head -c 32), но если решите придумать его сами - обязательно посчитайте длину, иначе ZITADEL просто не запустится с невнятной ошибкой про мастер-ключ.command: start-from-init- это команда именно для первого запуска, она создает первую организацию и первого пользователя из переменныхZITADEL_FIRSTINSTANCE_*. После того как инстанс поднялся и вы залогинились, часть гайдов рекомендует переключить команду на обычныйstart- я оставилstart-from-init, он безопасен и для повторных запусков (просто не находит, что инициализировать, и идет дальше), но знать про существование обычногоstartне лишнее.loadbalancer.server.scheme=h2cв лейблах Traefik - не опечатка и не “для красоты”. ZITADEL общается по gRPC, а gRPC поверх обычного HTTP/1.1 не работает - нужен HTTP/2 без TLS между Traefik и контейнером (тот самый h2c, “HTTP/2 Cleartext”). Если убрать эту строку “для простоты”, получите на входе загадочные ошибки авторизации, которые никак не намекают на настоящую причину.
Если у вас уже есть отдельный ZITADEL_LOGIN_USER/PASSWORD с реальными данными - не забудьте, что это учетка первого администратора, а не просто “тестовый пользователь для проверки” - именно под ней вы будете заходить в консоль и настраивать все остальное.
Что в итоге#
Однозначного вывода “берите ZITADEL, а не Х” здесь не будет - слишком по-разному стоит вопрос у каждого. Если вам просто нужна форма логина перед парой веб-сервисов - ZITADEL для этого явно избыточен, возьмите Authelia или TinyAuth. А вот если хочется собственного центра идентификации с ролями, MFA и API - причем не такого тяжелого в администрировании, как Keycloak - ZITADEL стоит попробовать всерьез, а не просто прочитать про него в очередном сравнительном списке.




