MAATRIX / Блог / Как установить и настроить ZITADEL на VPS

Как установить и настроить ZITADEL на VPS

MAATRIX

Если вам нужен единый вход для десятка внутренних сервисов, а тащить в стек 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 ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →