Как установить и настроить ZITADEL на VPS
Если вам нужен единый вход для десятка внутренних сервисов, а тащить в стек Auth0 или Okta не хочется из-за цены и привязки к чужому облаку — ZITADEL закрывает этот вопрос на собственном VPS. Это cloud-native identity-платформа с честной мультитенантностью «из коробки»: одна инсталляция спокойно обслуживает несколько организаций и проектов, не превращаясь в свалку конфигов, как бывает с самодельными связками на OAuth-библиотеках. Дальше — рабочая установка на сервере: от подготовки машины до первого OIDC-клиента.
Содержание
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →Что такое ZITADEL и когда он оправдан
ZITADEL написан на Go, хранит состояние в PostgreSQL (или CockroachDB) и построен вокруг event sourcing — каждое изменение в системе (создание пользователя, смена роли, выпуск токена) это событие в журнале, из которого проекции строят читаемые представления. На практике это означает предсказуемый аудит: видно, кто и когда что поменял, без сторонних плагинов логирования.
Сравнение с ближайшими self-hosted альтернативами:
| Платформа | Мультитенантность | Хранилище | Порог входа |
|---|---|---|---|
| ZITADEL | Есть изначально (instance → organization → project) | PostgreSQL/CockroachDB | Средний, много терминов из мира IAM |
| Keycloak | Через realms, менее гибко для SaaS-сценариев | Своя БД (обычно PostgreSQL) | Средний, но экосистема шире |
| Authentik | Слабее, скорее одна организация на инсталляцию | PostgreSQL | Ниже, интерфейс проще для старта |
Если вы строите SaaS и каждому клиенту нужна изолированная организация со своими пользователями и брендингом логина — ZITADEL спроектирован именно под это. Если задача проще — единый SSO для внутренних админок команды — присмотритесь также к Keycloak или Authentik: у обоих ниже порог входа для базового сценария.
Требования к серверу и подготовка
ZITADEL сам по себе легковесный (это компилированный Go-бинарник в контейнере), но PostgreSQL под ним требователен к диску и памяти по мере роста базы событий. Для старта:
- 2 vCPU, 4 ГБ RAM — комфортный минимум для продакшена с несколькими организациями и десятками пользователей;
- SSD/NVMe диск от 20 ГБ — event-store растёт быстрее, чем кажется, особенно при активном логировании входов;
- Ubuntu 24.04 LTS — берём как базовый образ, инструкция ориентирована на неё;
- открытые порты 80 и 443 наружу, домен с A-записью на IP сервера — без домена и TLS ZITADEL нормально не заработает: OIDC-провайдер требует HTTPS даже для внутренних сценариев.
Подготовка сервера стандартная:
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" > /etc/apt/sources.list.d/docker.list
apt update
apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
Подойдёт любой VPS с чистой Ubuntu — на такой задаче не нужны экзотические ядра или специфичное железо, важнее стабильный канал и SSD.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверУстановка через Docker Compose
Официальный путь — Docker Compose с PostgreSQL рядом. Создаём рабочую директорию и генерируем мастер-ключ — он должен быть ровно 32 символа, ZITADEL шифрует им чувствительные данные в базе:
mkdir -p /opt/zitadel && cd /opt/zitadel
openssl rand -base64 24 | cut -c1-32 > masterkey.txt
cat masterkey.txt
Сохраните этот ключ отдельно и надёжно — без него после потери базы данные не восстановить даже из бэкапа PostgreSQL. Дальше docker-compose.yml:
services:
zitadel:
image: ghcr.io/zitadel/zitadel:latest
command: 'start-from-init --masterkeyFile /masterkey.txt --tlsMode external'
restart: unless-stopped
environment:
ZITADEL_EXTERNALDOMAIN: auth.example.com
ZITADEL_EXTERNALPORT: '443'
ZITADEL_EXTERNALSECURE: 'true'
ZITADEL_DATABASE_POSTGRES_HOST: db
ZITADEL_DATABASE_POSTGRES_PORT: '5432'
ZITADEL_DATABASE_POSTGRES_DATABASE: zitadel
ZITADEL_DATABASE_POSTGRES_USER_USERNAME: zitadel
ZITADEL_DATABASE_POSTGRES_USER_PASSWORD: ${DB_PASSWORD}
ZITADEL_DATABASE_POSTGRES_USER_SSL_MODE: disable
ZITADEL_DATABASE_POSTGRES_ADMIN_USERNAME: postgres
ZITADEL_DATABASE_POSTGRES_ADMIN_PASSWORD: ${DB_ADMIN_PASSWORD}
ZITADEL_DATABASE_POSTGRES_ADMIN_SSL_MODE: disable
ZITADEL_FIRSTINSTANCE_ORG_HUMAN_USERNAME: admin
ZITADEL_FIRSTINSTANCE_ORG_HUMAN_PASSWORD: ${ADMIN_PASSWORD}
ZITADEL_FIRSTINSTANCE_ORG_HUMAN_EMAIL_ADDRESS: admin@example.com
ZITADEL_FIRSTINSTANCE_ORG_HUMAN_EMAIL_VERIFIED: 'true'
volumes:
- ./masterkey.txt:/masterkey.txt:ro
depends_on:
db:
condition: service_healthy
ports:
- '127.0.0.1:8080:8080'
db:
image: postgres:16-alpine
restart: unless-stopped
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: ${DB_ADMIN_PASSWORD}
POSTGRES_DB: zitadel
healthcheck:
test: ['CMD-SHELL', 'pg_isready -U postgres']
interval: 5s
timeout: 5s
retries: 10
volumes:
- db-data:/var/lib/postgresql/data
volumes:
db-data:
Набор переменных ZITADEL_* между релизами иногда меняется — сверьтесь с актуальной страницей конфигурации в документации перед продакшен-деплоем, здесь приведён рабочий костяк. Пароли выносим в .env:
cat > .env <<'EOF'
DB_ADMIN_PASSWORD=замените-на-случайный-пароль
DB_PASSWORD=ещё-один-случайный-пароль
ADMIN_PASSWORD=пароль-для-первого-администратора
EOF
chmod 600 .env
docker compose up -d
Первый запуск создаёт схему в PostgreSQL и инициализирует первую организацию — это может занять минуту-другую, следите за логами: docker compose logs -f zitadel.
Домен и TLS через реверс-прокси
ZITADEL сам не терминирует TLS в этой схеме — порт 8080 слушает только 127.0.0.1, наружу его отдаёт реверс-прокси. Если у вас уже есть Traefik в стеке, добавьте лейблы:
labels:
- 'traefik.enable=true'
- 'traefik.http.routers.zitadel.rule=Host(`auth.example.com`)'
- 'traefik.http.routers.zitadel.entrypoints=websecure'
- 'traefik.http.routers.zitadel.tls.certresolver=letsencrypt'
- 'traefik.http.services.zitadel.loadbalancer.server.port=8080'
- 'traefik.http.services.zitadel.loadbalancer.server.scheme=h2c'
Важный нюанс: ZITADEL использует gRPC-Web и HTTP/2 для части API, поэтому у прокси должна быть включена поддержка HTTP/2 и, если это Traefik, схема h2c до бэкенда — иначе часть запросов консоли администратора будет падать с непонятными ошибками. Если предпочитаете более простой прокси — подойдёт связка из статьи про Caddy с авто-SSL, у Caddy HTTP/2 включён по умолчанию, конфиг короче:
auth.example.com {
reverse_proxy 127.0.0.1:8080 {
transport http {
versions h2c
}
}
}
После первого удачного TLS-хендшейка откройте https://auth.example.com/ui/console — должна открыться страница входа в консоль администратора.
Первый вход, организации и мультитенантность
Логинитесь тем e-mail и паролем, что задали в ZITADEL_FIRSTINSTANCE_ORG_HUMAN_*. Система сразу попросит сменить пароль и настроить второй фактор — не пропускайте это для аккаунта администратора, у него доступ к управлению всеми организациями инстанса.
Модель мультитенантности в ZITADEL трёхуровневая:
- Instance — сама инсталляция, которую вы подняли на сервере;
- Organization — изолированный «клиент» внутри инстанса, со своими пользователями, ролями, брендингом страницы логина и провайдерами входа;
- Project — приложения и API внутри организации, к которым выдаются роли и OIDC-клиенты.
Для SaaS-сценария каждая новая компания-клиент — это новая организация: Console → Organizations → New. Пользователи одной организации по умолчанию не видят и не могут авторизоваться в другой, даже если это один и тот же инстанс — это ровно то, чего трудно добиться в Keycloak без ручной настройки realm-изоляции. Внутри организации создаётся проект (Projects → New), а в нём — приложение нужного типа (Web, Native, API, SPA).
Подключение приложений и продакшен-эксплуатация
Для веб-приложения с сервером (например, классический backend на сессиях) создаём OIDC-клиент типа Web с Authorization Code + PKCE:
Console → Project → New Application
Type: Web
Auth method: Code
PKCE: включено
Redirect URI: https://app.example.com/auth/callback
Post Logout Redirect URI: https://app.example.com/
ZITADEL выдаст Client ID (секрет не нужен при confidential-клиенте с PKCE, если приложение публичное — SPA). Discovery-эндпоинт стандартный:
https://auth.example.com/.well-known/openid-configuration
Его достаточно подставить в любую библиотеку с поддержкой OIDC — от next-auth до Spring Security — остальное клиент вытянет автоматически.
По эксплуатации — три вещи, которые стоит закрыть сразу, а не после инцидента:
- Бэкапы. База в PostgreSQL — это единственный источник правды, отдельно от мастер-ключа бэкап бесполезен. Настройте
pg_dumpпо расписанию и храните мастер-ключ вне сервера (менеджер паролей, отдельное зашифрованное хранилище). - Обновления. Перед апдейтом образа
ghcr.io/zitadel/zitadelчитайте changelog — миграции схемы БД накатываются автоматически при старте, откатить их назад не всегда тривиально, поэтому снимайте снапшот диска перед апдейтом. - Ресурсы БД. По мере роста числа событий (входы, смены токенов, аудит) PostgreSQL начинает есть больше диска и CPU на индексы — заложите запас минимум вдвое от стартовых 20 ГБ, если планируете тысячи активных пользователей.
Если PostgreSQL уже крутится у вас отдельно под другие сервисы, полезно заранее прочитать про настройку PostgreSQL на VPS и про тюнинг PostgreSQL — параметры shared_buffers и work_mem по умолчанию рассчитаны на слабое железо и почти всегда просят правки под реальную нагрузку identity-провайдера.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверНужны сами нейросети для контента?
Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.
Частые вопросы
Можно ли обойтись без домена, использовать только IP?
Формально можно указать IP в ZITADEL_EXTERNALDOMAIN, но OIDC-провайдеры (Google, GitHub) для внешних логинов требуют валидный HTTPS-домен, да и сертификат Let's Encrypt на голый IP не выпустить — на практике домен нужен почти всегда.
Чем ZITADEL принципиально отличается от Keycloak, если оба self-hosted?
Главное — архитектура мультитенантности. В Keycloak realm — плоская единица, изоляция и брендинг настраиваются вручную и не всегда полностью герметичны между клиентами. В ZITADEL иерархия instance → organization → project заложена в модель данных изначально, это удобнее именно для SaaS с десятками отдельных клиентов.
Нужен ли обязательно CockroachDB, или PostgreSQL хватит?
PostgreSQL полностью официально поддерживается и для 90% инсталляций его достаточно. CockroachDB имеет смысл только при горизонтальном масштабировании на несколько дата-центров — для одного VPS это избыточно.
Что делать, если консоль администратора открывается, но API-запросы падают с ошибками?
В девяти случаях из десяти причина — прокси без HTTP/2 или без поддержки gRPC-Web между прокси и контейнером ZITADEL. Проверьте, что реверс-прокси настроен именно на h2c/HTTP/2 до бэкенда, а не только на входящий HTTPS.
Как перенести ZITADEL на другой сервер?
Переносится вместе связка: дамп PostgreSQL плюс файл мастер-ключа. Без мастер-ключа расшифровать секреты (client secrets, часть пользовательских данных) в перенесённой базе не получится — это осознанная защита, а не баг.
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →