ZITADEL на Ubuntu 24.04: пошаговая установка
Если вы устали платить Auth0 или Okta за каждого активного пользователя, а Keycloak кажется избыточно тяжёлым для новой архитектуры — присмотритесь к ZITADEL. Это cloud-native identity-платформа с мультитенантностью «из коробки», написанная на Go, event-sourcing внутри и без легаси Java-стека под капотом. Ниже — рабочая установка на чистой Ubuntu 24.04 через Docker Compose, с доменом, TLS и первыми шагами настройки организации.
Содержание
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →Что такое ZITADEL и зачем он вам
ZITADEL решает ту же задачу, что Auth0, Okta или Keycloak: централизованная аутентификация, OAuth2/OIDC, SAML, управление пользователями и организациями для ваших приложений. Отличия, которые обычно и склоняют выбор в его пользу:
- Мультитенантность на уровне архитектуры, а не надстройка поверх однотенантной модели — если вы строите SaaS с изолированными организациями клиентов, это ложится естественно.
- Event sourcing: каждое изменение состояния (создание пользователя, смена пароля, выдача роли) — событие в журнале. Это даёт полный аудит-лог бесплатно.
- Go-бинарник + PostgreSQL, никакой Java, никакого отдельного слоя кэша обязательного к установке. Меньше движущихся частей, чем у Keycloak.
- Открытый исходный код (Apache 2.0 для self-managed, с отдельным облачным SaaS-предложением от авторов) — можно поднять полностью у себя, без вендор-лока.
Из минусов: сообщество и экосистема плагинов заметно меньше, чем у Keycloak, документация местами отстаёт от темпа релизов. Если у вас уже есть большая инсталляция Keycloak и она работает — переезжать ради архитектурной красоты не всегда оправдано. Но для нового проекта, особенно с прицелом на мультитенантный SaaS, ZITADEL — разумный кандидат.
Требования к серверу и подготовка
ZITADEL — не самый прожорливый сервис, но под PostgreSQL и сам процесс стоит закладывать запас:
| Ресурс | Минимум (тест/dev) | Рекомендуется (прод, до ~10k пользователей) |
|---|---|---|
| CPU | 2 vCPU | 4 vCPU |
| RAM | 2 GB | 4-8 GB |
| Диск | 20 GB SSD | 40+ GB SSD (event-store растёт) |
| ОС | Ubuntu 24.04 LTS | Ubuntu 24.04 LTS |
Для боевой установки понадобится домен с A-записью на IP сервера — ZITADEL завязан на TLS и корректный внешний hostname. Обновляем систему и ставим Docker с Compose-плагином:
apt update && apt upgrade -y
apt install -y ca-certificates curl gnupg
install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
chmod a+r /etc/apt/keyrings/docker.asc
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
apt update
apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
docker --version && docker compose version
Если предпочитаете разворачивать Docker на чистой машине по готовому сценарию — есть отдельный разбор: Ubuntu 24.04: установка Docker с нуля.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверDocker Compose: ZITADEL + PostgreSQL
ZITADEL официально поддерживает два варианта БД — PostgreSQL и CockroachDB. Для одиночного сервера без потребности в геораспределённом кластере PostgreSQL проще в эксплуатации и бэкапе, поэтому берём его.
Создаём рабочую директорию и структуру:
mkdir -p /opt/zitadel && cd /opt/zitadel
Файл docker-compose.yml:
services:
zitadel-db:
image: postgres:16-alpine
container_name: zitadel-db
restart: unless-stopped
environment:
POSTGRES_USER: zitadel
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: zitadel
volumes:
- ./data/postgres:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U zitadel -d zitadel"]
interval: 10s
timeout: 5s
retries: 5
zitadel:
image: ghcr.io/zitadel/zitadel:v2.63.0
container_name: zitadel
restart: unless-stopped
depends_on:
zitadel-db:
condition: service_healthy
command: start-from-init --masterkeyFromEnv --tlsMode external
environment:
ZITADEL_MASTERKEY: ${ZITADEL_MASTERKEY}
ZITADEL_DATABASE_POSTGRES_HOST: zitadel-db
ZITADEL_DATABASE_POSTGRES_PORT: 5432
ZITADEL_DATABASE_POSTGRES_DATABASE: zitadel
ZITADEL_DATABASE_POSTGRES_USER_USERNAME: zitadel
ZITADEL_DATABASE_POSTGRES_USER_PASSWORD: ${POSTGRES_PASSWORD}
ZITADEL_DATABASE_POSTGRES_USER_SSL_MODE: disable
ZITADEL_DATABASE_POSTGRES_ADMIN_USERNAME: zitadel
ZITADEL_DATABASE_POSTGRES_ADMIN_PASSWORD: ${POSTGRES_PASSWORD}
ZITADEL_DATABASE_POSTGRES_ADMIN_SSL_MODE: disable
ZITADEL_EXTERNALSECURE: "true"
ZITADEL_EXTERNALDOMAIN: ${ZITADEL_DOMAIN}
ZITADEL_EXTERNALPORT: "443"
ZITADEL_FIRSTINSTANCE_ORG_HUMAN_USERNAME: admin
ZITADEL_FIRSTINSTANCE_ORG_HUMAN_PASSWORD: ${ZITADEL_ADMIN_PASSWORD}
ports:
- "127.0.0.1:8080:8080"
Порт ZITADEL смотрит только на 127.0.0.1 — наружу его отдаёт reverse-прокси с TLS. --tlsMode external означает, что сам ZITADEL TLS не терминирует и полагается на прокси перед собой.
Файл .env рядом (обязательно закройте права chmod 600 .env, там пароли):
POSTGRES_PASSWORD=замените_на_длинный_случайный_пароль
ZITADEL_MASTERKEY=замените_на_ровно_32_символа_ключ
ZITADEL_DOMAIN=auth.example.com
ZITADEL_ADMIN_PASSWORD=замените_на_надёжный_пароль_админа
ZITADEL_MASTERKEY — это ключ шифрования данных внутри БД (секреты приложений, токены), и он должен быть ровно 32 символа. Сгенерировать удобно так:
openssl rand -base64 24 | cut -c1-32
Сохраните этот ключ в отдельном менеджере паролей — без него вы не расшифруете БД при переносе на другой сервер, и это не мелочь, которую можно исправить постфактум.
Поднимаем стек:
docker compose up -d
docker compose logs -f zitadel
Первый запуск инициализирует схему БД и создаёт первую организацию с указанным админом — это может занять минуту-две. Дождитесь строки о готовности сервера (обычно что-то вроде server is listening on), прежде чем идти дальше.
Nginx как reverse-прокси с TLS
Ставим Nginx и Certbot:
apt install -y nginx certbot python3-certbot-nginx
Конфиг /etc/nginx/sites-available/zitadel:
server {
listen 80;
server_name auth.example.com;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# ZITADEL активно использует gRPC-Web / HTTP2 внутри UI
proxy_set_header Connection "";
proxy_buffering off;
}
}
Активируем и выпускаем сертификат:
ln -s /etc/nginx/sites-available/zitadel /etc/nginx/sites-enabled/
nginx -t && systemctl reload nginx
certbot --nginx -d auth.example.com --redirect
Certbot сам допишет блок listen 443 ssl и настроит редирект с 80 на 443. Если вы уже используете Caddy вместо Nginx на этом сервере — он получит сертификат ещё проще, одной строкой reverse_proxy в Caddyfile; сравнение подходов есть в статье Caddy или Nginx: что выбрать для сервера.
После этого шага откройте https://auth.example.com — должна открыться страница логина ZITADEL Console.
Первый вход и настройка организации
Заходите под учёткой, которую задали в ZITADEL_FIRSTINSTANCE_ORG_HUMAN_USERNAME / ..._PASSWORD (по умолчанию логин собирается как admin@<ваш-домен-инстанса>, ZITADEL покажет точный адрес на экране логина). При первом входе система попросит сменить пароль и настроить MFA — не пропускайте этот шаг для админской учётки, это единственная точка входа с полными правами.
Дальше в консоли ZITADEL:
- Organization — у вас уже есть первая организация (создана при инициализации). Каждая новая организация — изолированный "клиент" со своими пользователями, не видящий чужих данных.
- Projects — создайте проект для вашего приложения. Проект — контейнер для приложений и ролей.
- Applications — внутри проекта добавьте приложение нужного типа: Web (Authorization Code + PKCE для SPA/SSR), API (client credentials для сервис-к-сервису), Native (мобильные, тоже PKCE).
- Roles — определите роли проекта (
admin,editor,viewerи т.п.), они попадут в токен как claims.
Для веб-приложения на Authorization Code Flow с PKCE ZITADEL выдаст Client ID и адреса эндпоинтов автоматически по discovery URL:
https://auth.example.com/.well-known/openid-configuration
Большинство современных OIDC-библиотек (oidc-client-ts, NextAuth, Spring Security OAuth2) подхватывают конфигурацию по этому URL без ручного прописывания каждого эндпоинта.
Настройка SMTP для писем и уведомлений
Без настроенного SMTP ZITADEL не сможет отправлять письма подтверждения регистрации, сброса пароля и приглашения в организацию — а это база любого identity-провайдера. Настраивается через Console: Instance → Settings → Notification Providers → SMTP.
Понадобятся:
- SMTP host и порт (587 для STARTTLS — стандартный выбор);
- логин/пароль SMTP-аккаунта;
- адрес отправителя (
Sender Email), желательно с того же домена, что иZITADEL_EXTERNALDOMAIN; - при желании — свои HTML-шаблоны писем (ZITADEL позволяет их кастомизировать по языкам).
Проверить доставляемость проще всего практически: зарегистрировать тестового пользователя и посмотреть, дошло ли письмо и не улетело ли в спам. Если письма теряются — обычно дело в отсутствующей SPF/DKIM-записи на домене отправителя, а не в самом ZITADEL.
Резервное копирование
Всё состояние ZITADEL — это PostgreSQL плюс мастер-ключ. Файловой системы, которую нужно бэкапить отдельно (кроме самой БД), практически нет — event store целиком живёт в базе.
Простой скрипт для ежедневного дампа:
#!/bin/bash
# /opt/zitadel/backup.sh
BACKUP_DIR="/opt/zitadel/backups"
mkdir -p "$BACKUP_DIR"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
docker exec zitadel-db pg_dump -U zitadel -Fc zitadel > "$BACKUP_DIR/zitadel_${TIMESTAMP}.dump"
# храним последние 14 дампов
find "$BACKUP_DIR" -name "zitadel_*.dump" -mtime +14 -delete
Добавляем в cron:
chmod +x /opt/zitadel/backup.sh
(crontab -l 2>/dev/null; echo "0 3 * * * /opt/zitadel/backup.sh") | crontab -
Восстановление:
docker exec -i zitadel-db pg_restore -U zitadel -d zitadel --clean < zitadel_20260815_030000.dump
Обязательно храните копию .env (с ZITADEL_MASTERKEY) отдельно от сервера — например, в зашифрованном хранилище паролей. Дамп базы без мастер-ключа не расшифровать, а без дампа мастер-ключ бесполезен: нужны оба компонента вместе. Если у вас уже настроен централизованный бэкап Docker-томов для других сервисов на этом хосте, тот же подход подойдёт и для ./data/postgres — общие принципы разобраны в статье про бэкап Docker volume на сервере.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверНужны сами нейросети для контента?
Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.
Частые вопросы
Чем ZITADEL принципиально отличается от Keycloak?
Архитектурно — event sourcing вместо CRUD-модели над реляционной схемой, встроенная мультитенантность на уровне организаций, стек на Go вместо Java/Quarkus. Практически: меньше ресурсов на старте, но меньше готовых интеграций, чем у Keycloak. Если у вас уже развёрнут Keycloak и он справляется — сравните варианты в статье Keycloak на Ubuntu 24.04: пошаговая установка.
Можно ли обойтись без домена и TLS для теста?
Технически да — есть режим --tlsMode disabled для локальной разработки, но в такой конфигурации не работают многие защитные механизмы (secure cookies, редиректы), и переносить настройки в прод придётся заново.
Что делать, если я потерял ZITADEL_MASTERKEY?
Расшифровать существующую базу без него нельзя — секреты приложений и часть данных окажутся недоступны. Единственный выход — поднять новый инстанс с новым ключом и заново настроить организации и приложения. Отсюда и требование хранить ключ отдельно с резервной копией.
ZITADEL требует внешний Redis или кэш?
Нет — кэширование он делает средствами PostgreSQL и in-memory на своей стороне. Это упрощает эксплуатацию single-node установки.
Как масштабировать ZITADEL на несколько экземпляров?
Сам процесс stateless и масштабируется горизонтально за балансировщиком — узкое место обычно PostgreSQL. Для серьёзной нагрузки стоит рассмотреть managed PostgreSQL с репликами для чтения либо CockroachDB как альтернативную СУБД именно ради горизонтального масштабирования.
Нужен ли firewall на сервере, если весь трафик и так идёт через Nginx?
Да, закрывать порт PostgreSQL и сам ZITADEL от внешнего доступа стоит явно, а не полагаться только на bind к 127.0.0.1. Быстрый разбор настройки — в статье Firewall UFW на Ubuntu 24.04: пошаговая установка.
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →