MAATRIX / Блог / Бэкап и восстановление ZITADEL

Бэкап и восстановление ZITADEL

MAATRIX

Если ZITADEL — это единственная точка входа для всех ваших приложений, потеря базы означает, что никто никуда не залогинится: ни клиенты, ни админы, ни сервисные аккаунты. В отличие от обычного веб-приложения, здесь бэкап — это не просто дамп таблиц, а сохранение полной истории событий плюс ключа шифрования, без которого восстановленная база бесполезна. Разберём, что именно нужно сохранять, как это автоматизировать и как проверить, что восстановление реально сработает, а не только выглядит рабочим на бумаге.

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

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

Что на самом деле нужно бэкапить в ZITADEL

ZITADEL построен на event sourcing: каждое изменение (создание пользователя, смена пароля, выдача роли, настройка IDP) записывается как событие в таблицу eventstore.events2, а текущее состояние — это проекции, посчитанные из этих событий. Практический вывод: бэкапить нужно не "текущее состояние", а весь event log целиком — если вы восстановите только проекции без событий, ZITADEL не сможет пересчитать состояние при следующей миграции схемы.

Три вещи, без которых восстановленный инстанс не запустится или запустится, но окажется бесполезен:

  • База данных — PostgreSQL (рекомендуемый бэкенд с версии ZITADEL v2) или CockroachDB, со всеми схемами eventstore, projections, system, auth.
  • Masterkey — 32-байтный ключ (ZITADEL_MASTERKEY или флаг --masterkey), которым шифруются секреты в базе: пароли клиентов OIDC, приватные ключи сервисных аккаунтов, SMTP-пароли. Потеряете его — расшифровать эти данные из бэкапа базы уже не получится, придётся заново выпускать все клиентские секреты.
  • Конфигурация и TLSzitadel.yaml (или переменные окружения в docker-compose), сертификаты, если вы их не выпускаете заново через ACME при каждом деплое.

Отдельно стоит вопрос доступа к внешнему SMTP и связанным сервисам (например, если вы используете ZITADEL вместе с Keycloak в смешанной инфраструктуре или мигрируете с него) — но это уже не бэкап самого ZITADEL, а часть общей документации инфраструктуры.

Бэкап PostgreSQL под ZITADEL

Если вы ставили ZITADEL по нашей пошаговой установке на Ubuntu 24.04, у вас, скорее всего, отдельный контейнер или сервис PostgreSQL 14+ с базой zitadel. Для среднего инстанса (до пары тысяч пользователей) логического дампа через pg_dump вполне достаточно — WAL-архивирование и PITR имеют смысл только на высоконагруженных инсталляциях с десятками тысяч событий в час.

Простой дамп с сжатием:

docker exec -t zitadel-db pg_dump \
  -U zitadel -d zitadel --format=custom --compress=6 \
  -f /tmp/zitadel_$(date +%Y%m%d_%H%M%S).dump
docker cp zitadel-db:/tmp/zitadel_$(date +%Y%m%d)*.dump /opt/backups/zitadel/

Формат --format=custom даёт возможность восстанавливать выборочно (например, только схему system без пересборки проекций) и параллельный restore через pg_restore -j. Не используйте pg_dumpall без необходимости — он тянет роли и настройки всего кластера PostgreSQL, что избыточно, если PostgreSQL используется и другими сервисами на том же хосте.

Если база большая (счёт на десятки гигабайт из-за истории событий), логический дамп начинает занимать заметное время и место. В этом случае разумнее физический бэкап через pg_basebackup с последующим WAL-архивированием:

pg_basebackup -h localhost -U replicator -D /opt/backups/zitadel/base_$(date +%Y%m%d) \
  -Ft -z -P --wal-method=stream

Для непрерывного WAL-архивирования в postgresql.conf нужно включить archive_mode = on и archive_command, указывающий, куда копировать сегменты (локальная директория, S3-совместимое хранилище через wal-g или pgbackrest). Это даёт point-in-time recovery — возможность откатиться на конкретную секунду перед инцидентом, а не только на момент последнего дампа за ночь.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Бэкап masterkey и конфигов

Masterkey и конфигурация — маленькие файлы, но именно их чаще всего забывают включить в план бэкапа, потому что "они и так лежат в git" или "запомню". Не полагайтесь на память.

Если ZITADEL запущен через docker-compose, masterkey обычно передаётся переменной окружения или через .env:

services:
  zitadel:
    environment:
      - ZITADEL_MASTERKEY=${ZITADEL_MASTERKEY}

Бэкапьте .env отдельно от репозитория с кодом (даже если репозиторий приватный) — храните его в менеджере секретов или зашифрованным архивом:

tar czf - .env zitadel.yaml docker-compose.yml | \
  gpg --symmetric --cipher-algo AES256 -o /opt/backups/zitadel/config_$(date +%Y%m%d).tar.gz.gpg

Если TLS-сертификаты выпускаются через Traefik или другой reverse proxy с ACME (мы разбирали настройку в статье про Traefik на Ubuntu 24.04), отдельно их можно не бэкапить — Let's Encrypt перевыпустит их за минуту после восстановления DNS и сети. А вот если сертификаты клиентские (mTLS между сервисами) или самоподписанные для внутреннего использования — их обязательно включайте в бэкап конфигов.

Автоматизация: скрипт и расписание

Ниже — рабочий скрипт, который снимает дамп базы и конфиги, шифрует их и складывает с ротацией на 14 дней. Дополнительно можно синхронизировать результат на удалённое S3-совместимое хранилище через rclone или restic (мы отдельно разбирали готовый docker-compose для restic, если хотите дедупликацию и снапшоты вместо плоских файлов).

#!/usr/bin/env bash
set -euo pipefail

BACKUP_DIR="/opt/backups/zitadel"
DATE=$(date +%Y%m%d_%H%M%S)
KEEP_DAYS=14

mkdir -p "$BACKUP_DIR"

# 1. Дамп базы
docker exec -t zitadel-db pg_dump -U zitadel -d zitadel \
  --format=custom --compress=6 > "$BACKUP_DIR/db_${DATE}.dump"

# 2. Конфиги + masterkey, зашифрованные
tar czf - .env zitadel.yaml docker-compose.yml 2>/dev/null | \
  gpg --batch --yes --passphrase-file /root/.zitadel_backup_pass \
  --symmetric --cipher-algo AES256 \
  -o "$BACKUP_DIR/config_${DATE}.tar.gz.gpg"

# 3. Контрольная сумма для проверки целостности
sha256sum "$BACKUP_DIR/db_${DATE}.dump" "$BACKUP_DIR/config_${DATE}.tar.gz.gpg" \
  > "$BACKUP_DIR/checksums_${DATE}.txt"

# 4. Ротация старых бэкапов
find "$BACKUP_DIR" -type f -mtime +${KEEP_DAYS} -delete

echo "[$(date)] Backup done: db_${DATE}.dump" >> /var/log/zitadel-backup.log

Добавьте в cron ночной запуск:

0 3 * * * /opt/scripts/zitadel-backup.sh >> /var/log/zitadel-backup-cron.log 2>&1

Частота зависит от того, сколько изменений теряет бизнес при откате на последний бэкап: для большинства проектов ночного дампа хватает, но если через ZITADEL идёт активная выдача и отзыв API-ключей несколько раз в день, ставьте более частый снимок (каждые 4-6 часов) или переход на WAL-архивирование с PITR, как описано выше.

Восстановление из бэкапа

Порядок действий важен: сначала поднимаете чистую базу, восстанавливаете дамп, затем возвращаете masterkey — именно в этой последовательности, потому что без правильного masterkey расшифровка секретов в восстановленных таблицах не сработает, а ZITADEL при старте начнёт падать с ошибками decrypt на клиентских секретах.

Шаг 1 — новая пустая база:

docker exec -t zitadel-db psql -U postgres -c "DROP DATABASE IF EXISTS zitadel;"
docker exec -t zitadel-db psql -U postgres -c "CREATE DATABASE zitadel OWNER zitadel;"

Шаг 2 — восстановление дампа:

docker cp /opt/backups/zitadel/db_20260828_030001.dump zitadel-db:/tmp/restore.dump
docker exec -t zitadel-db pg_restore -U zitadel -d zitadel \
  --no-owner --no-privileges -j 4 /tmp/restore.dump

Шаг 3 — расшифровка и восстановление конфигов:

gpg --batch --yes --passphrase-file /root/.zitadel_backup_pass \
  -o /tmp/config_restored.tar.gz -d config_20260828_030001.tar.gz.gpg
tar xzf /tmp/config_restored.tar.gz -C /opt/zitadel/

Убедитесь, что значение ZITADEL_MASTERKEY в восстановленном .env совпадает с тем, что использовалось при создании дампа — это самая частая причина неудачного восстановления. Если masterkey утерян отдельно от бэкапа базы, расшифровать существующие секреты не получится — остаётся только принудительный сброс всех клиентских секретов и сервисных ключей после restore.

Шаг 4 — запуск и проверка:

docker compose up -d zitadel
docker compose logs -f zitadel | grep -i "started\|error"

При первом старте после restore ZITADEL проверяет и при необходимости доводит до актуальной версии схему БД (миграции проекций) — на базе с большой историей это может занять несколько минут, не прерывайте процесс.

Тестовое восстановление и проверка бэкапов

Бэкап, который ни разу не восстанавливали, с практической точки зрения не отличается от отсутствия бэкапа. Возьмите за правило раз в месяц (или после каждого крупного релиза) поднимать восстановление на отдельном тестовом сервере или временном VPS с чистой сетью, без доступа к продакшн-DNS, и проверять:

  • логин под существующим пользователем через тестовое приложение с сохранённым client secret;
  • работу сервисного аккаунта (machine user) с сохранённым JSON-ключом — это отдельная проверка расшифровки, machine keys шифруются тем же masterkey;
  • количество пользователей и организаций в восстановленной базе совпадает с продакшеном на момент снятия дампа.

Полезная общая практика по проверке восстановления БД разобрана в статье про восстановление базы данных из бэкапа на практике — те же принципы (тестовый прогон, чек-лист, время восстановления) применимы и к ZITADEL, только добавляется шаг с masterkey.

Держите под рукой метрику RTO (сколько реально занимает полное восстановление — от находки проблемы до рабочего логина) и RPO (сколько данных потеряется между последним бэкапом и инцидентом). Для большинства некрупных инсталляций RTO в 20-40 минут при ночном дампе — реалистичная и приемлемая цифра, но её стоит замерить руками хотя бы раз, а не полагаться на ощущение "наверное, быстро".

Хранение и защита бэкапов

Локальная копия на том же сервере, что и сам ZITADEL, защищает от одной проблемы (человеческая ошибка, повреждение базы), но не от другой (отказ диска, компрометация сервера целиком). Правило "3-2-1" здесь так же применимо, как и везде: минимум три копии, на двух разных носителях, одна — вне основной площадки.

Практическая схема для сервера с ZITADEL:

УровеньЧтоКудаЧастота
ЛокальноДамп + конфиги/opt/backups на том же VPSЕжедневно
УдалённоТе же файлыОтдельный сервер или S3-хранилище в другой локацииЕжедневно (rsync/rclone)
Офлайн/холодная копияПолный снапшот раз в месяцВнешний диск или отдельный аккаунт хранилищаЕжемесячно

Masterkey и дамп базы вместе — это фактически полный доступ ко всем идентичностям в системе. Шифруйте архивы (как в скрипте выше через gpg) и держите passphrase отдельно от бэкапов — ключ рядом с зашифрованным файлом защиту не даёт. Для удалённой копии разумно брать сервер в другой локации, чтобы отказ основной площадки не задел резервную одновременно.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Нужны сами нейросети для контента?

Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.

Частые вопросы

Можно ли бэкапить только проекции, без событий eventstore?

Нет. Проекции — это производные данные, ZITADEL пересчитывает их из событий при миграциях схемы; без полного event log восстановленная база не пройдёт очередное обновление версии.

Что делать, если masterkey утерян, а бэкап базы есть?

Данные структуры (пользователи, организации, роли) восстановятся, но все зашифрованные секреты — client secrets OIDC-приложений, приватные ключи сервисных аккаунтов, пароли SMTP — станут нечитаемыми. Их придётся перевыпустить вручную после restore.

Подходит ли обычный snapshot диска VPS вместо дампа PostgreSQL?

Снапшот диска на живой базе может захватить несогласованное состояние, если файловая система не поддерживает атомарный снимок с fsfreeze. Логический дамп через pg_dump или физический через pg_basebackup безопаснее и предсказуемее для СУБД под нагрузкой.

Как часто реально нужно бэкапить, если ZITADEL используется только для внутренних сервисов компании?

Для низкой активности (десятки логинов в день, редкие изменения ролей) ночного дампа обычно достаточно. Ориентируйтесь на то, сколько изменений вы готовы потерять и пересоздать вручную — это и есть ваш допустимый RPO.

Нужно ли останавливать ZITADEL перед снятием дампа?

Нет, pg_dump снимает консистентный снимок на уровне транзакции без остановки сервиса. Кратковременная нагрузка на I/O возможна на очень больших базах — снимайте дамп в период минимальной активности.

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

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