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

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

MAATRIX

Drone CI устроен иначе, чем большинство CI/CD-систем: каждый шаг пайплайна — это отдельный Docker-контейнер, который создаётся под задачу и уничтожается сразу после неё. Из-за этого возникает ложное чувство, что бэкапить особо нечего — контейнеры и так временные. На практике же вся ценность Drone сосредоточена в двух местах: базе данных сервера и небольшом наборе секретов, без которых даже идеально восстановленная база бесполезна. Разберём, что реально нужно копировать, а что можно спокойно пересоздать с нуля.

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

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

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

Архитектура Drone CI: что действительно нужно бэкапить

Self-hosted Drone состоит из двух ролей, и у них принципиально разное отношение к состоянию:

  • Drone Server — веб-интерфейс, API, приёмник webhook от Gitea/GitHub/GitLab, планировщик задач. Именно здесь хранится всё, что представляет ценность: список подключённых репозиториев, история сборок и логи, секреты репозиториев и организаций, пользователи и их токены доступа. Это единственный компонент, требующий полноценного бэкапа.
  • Drone Runner (чаще всего drone-runner-docker) — исполнитель пайплайнов. Он опрашивает сервер по RPC, получает задачу, поднимает Docker-контейнер под каждый шаг .drone.yml, стримит логи обратно на сервер и забывает про job сразу после завершения. Runner не хранит собственного состояния между сборками (кроме локального кеша Docker-образов на диске, который не критичен и восстанавливается сам за счёт повторного pull). Если runner умер — его достаточно поднять заново из того же docker-compose.yml с теми же переменными окружения.

Отдельно стоит сам файл .drone.yml — он лежит в репозитории и версионируется в Git вместе с кодом проекта, поэтому целенаправленно бэкапить его через Drone не нужно: он уже защищён тем же Git-хостингом (Gitea, GitHub, GitLab), для которого имеет смысл настроить свой собственный бэкап отдельно.

Итого зона ответственности бэкапа сужается до трёх вещей: база данных сервера, RPC-секрет с переменными окружения и, если используется, TLS-сертификат сервера.

База данных: SQLite или внешняя PostgreSQL/MySQL

По умолчанию Drone Server хранит всё в SQLite-файле внутри примонтированного volume — обычно это путь /data/database.sqlite внутри контейнера. Проверить точный путь и драйвер можно так:

docker exec drone-server env | grep DRONE_DATABASE

Если переменные DRONE_DATABASE_DRIVER и DRONE_DATABASE_DATASOURCE не заданы явно — используется SQLite по умолчанию, и весь бэкап сводится к копированию файла volume.

ВариантКогда используетсяКак бэкапить
SQLite (по умолчанию)Небольшие и средние команды, один серверСнимок файла через sqlite3 .backup, не голый cp на живой базе
PostgreSQL/MySQLМного активных репозиториев, нужна отказоустойчивость, вынос БД на отдельный серверШтатный pg_dump/mysqldump

Для SQLite копировать файл через обычный cp на работающем сервере рискованно — если в этот момент идёт запись (например, обновляется статус сборки), можно получить повреждённый снимок. Правильный способ — консистентный бэкап через встроенную команду SQLite:

docker exec drone-server sqlite3 /data/database.sqlite \
  ".backup /data/database-backup-$(date +%F).sqlite"

docker cp drone-server:/data/database-backup-$(date +%F).sqlite \
  /opt/backups/drone/

Команда .backup делает атомарный снимок даже на активно используемой базе — SQLite гарантирует консистентность результата независимо от параллельных запросов.

Если сервер настроен на внешнюю PostgreSQL (DRONE_DATABASE_DRIVER=postgres), бэкап снимается штатно, как для любой другой PostgreSQL-базы:

docker exec postgres pg_dump -U drone -Fc drone > \
  /opt/backups/drone/drone-db-$(date +%F).dump

Формат -Fc (custom) даёт сжатый дамп, который можно восстанавливать выборочно и параллельными потоками через pg_restore -j.

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

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

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

RPC-секрет, OAuth-приложение и переменные окружения

Это вторая критичная часть, про которую забывают чаще всего — и именно её отсутствие превращает восстановленный сервер в нерабочий. Drone держит связку сервер-раннер на общем секрете:

DRONE_RPC_SECRET=<общий секрет сервера и всех runner'ов>

Если после восстановления этот секрет не совпадает с тем, что задан в runner'ах, они не смогут забирать задачи с сервера — сборки будут висеть в статусе "pending" бесконечно, а в логах runner'а появится ошибка авторизации. Секрет генерируется один раз (openssl rand -hex 16 — типичный способ) и должен быть одинаковым на сервере и на всех runner'ах, сколько бы их ни было.

Второй важный блок — параметры OAuth-приложения, которое связывает Drone с Git-провайдером:

DRONE_GITEA_SERVER=https://git.example.com
DRONE_GITEA_CLIENT_ID=xxxxxxxx
DRONE_GITEA_CLIENT_SECRET=xxxxxxxx
DRONE_SERVER_HOST=ci.example.com
DRONE_SERVER_PROTO=https

Эти значения выдаются при регистрации OAuth-приложения в настройках Gitea/GitHub/GitLab и без базы данных Drone не хранятся нигде — если их потерять, придётся заново создавать приложение и переподключать все репозитории вручную. Простое решение — держать .env или блок environment из docker-compose.yml в бэкапе рядом с дампом базы, а не только "в голове" или в переписке.

Если сервер работает по HTTPS напрямую (без внешнего reverse-proxy), в бэкап входят и TLS-сертификаты — обычно они уже лежат в том же volume /data, что и SQLite-файл, так что отдельного шага для них не требуется.

Автоматизация бэкапа в cron-скрипте

Ручной бэкап забывается — рабочий вариант снимает и базу, и конфиг одним скриптом под cron:

#!/bin/bash
set -euo pipefail

DATE=$(date +%F)
BACKUP_DIR="/opt/backups/drone"
mkdir -p "$BACKUP_DIR"

# консистентный снимок SQLite (для внешней PostgreSQL — замените на pg_dump)
docker exec drone-server sqlite3 /data/database.sqlite \
  ".backup /data/db-backup-$DATE.sqlite"
docker cp drone-server:/data/db-backup-$DATE.sqlite "$BACKUP_DIR/"
docker exec drone-server rm /data/db-backup-$DATE.sqlite

# конфиг с RPC-секретом и OAuth-параметрами
cp docker-compose.yml .env "$BACKUP_DIR/" 2>/dev/null || true

tar czf "$BACKUP_DIR/drone-full-$DATE.tar.gz" \
  -C "$BACKUP_DIR" "db-backup-$DATE.sqlite" docker-compose.yml .env

rm "$BACKUP_DIR/db-backup-$DATE.sqlite"

# копия за пределы сервера
rclone copy "$BACKUP_DIR/drone-full-$DATE.tar.gz" remote:drone-backups/

find "$BACKUP_DIR" -name "drone-full-*.tar.gz" -mtime +14 -delete

Задача в cron:

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

Локальная копия закрывает быстрый откат ("вчера всё работало, сегодня нет"), копия на удалённом хранилище — сценарий с полной потерей сервера. Про выбор площадки под такие копии — в статье про VPS для бэкапов и архива; если вместо простого rclone copy нужна дедупликация и шифрование на лету для больших объёмов логов сборок, посмотрите restic или BorgBackup — оба хорошо ложатся на такой скрипт вместо rclone copy.

Восстановление на новом сервере

Разворачиваем тот же docker-compose.yml, но пока не поднимаем сервисы:

tar xzf drone-full-2026-08-30.tar.gz -C /opt/drone-restore
cd /opt/drone-restore

Возвращаем базу на место. Для SQLite — просто кладём файл в volume под тем же именем, которое ожидает сервер:

docker compose up -d drone-server
docker compose stop drone-server
docker cp db-backup-2026-08-30.sqlite drone-server:/data/database.sqlite

Обязательно проверьте владельца файла внутри контейнера — официальный образ Drone запускает процесс не от root, и файл, распакованный из tar на хосте, может унаследовать чужой uid:

docker exec -u root drone-server chown drone:drone /data/database.sqlite

Для внешней PostgreSQL восстановление стандартное:

docker exec -i postgres pg_restore -U drone -d drone --clean --if-exists \
  < drone-db-2026-08-30.dump

Возвращаем .env с тем же DRONE_RPC_SECRET, что был на момент бэкапа, — это критично, если runner'ы поднимаются отдельно и не пересоздаются вместе с сервером. Запускаем сервер и runner'ы:

docker compose up -d
docker compose logs -f drone-server drone-runner

Если восстановление идёт на новый домен (сменился DRONE_SERVER_HOST), зайдите в настройки OAuth-приложения на стороне Gitea/GitHub/GitLab и обновите callback URL, иначе авторизация в интерфейсе Drone не сработает даже с правильными ключами. После этого в самом Drone откройте раздел с репозиториями и выполните Resync — это пересоздаст webhook'и с новым адресом, старые ссылки на прежний домен работать не будут.

Частые проблемы при восстановлении Drone CI

  • Сборки висят в статусе "pending" бесконечно. Почти всегда это несовпадение DRONE_RPC_SECRET между сервером и runner'ом после восстановления из разных бэкапов конфига. Сверьте значение в обоих .env побайтово — лишний пробел или перенос строки тоже считается несовпадением.
  • "database is locked" сразу после старта сервера. Файл SQLite восстановлен с некорректными правами или процесс не был полностью остановлен перед копированием файла. Убедитесь, что контейнер drone-server действительно остановлен на момент подмены файла, и владелец файла — пользователь, от которого работает сам процесс.
  • Webhook'и не приходят, хотя сервер поднялся и доступен. Типично при смене домена или IP при восстановлении. Drone не обновляет webhook на стороне Git-провайдера автоматически — нужен ручной Resync репозитория в интерфейсе Drone, который перерегистрирует webhook с актуальным адресом.
  • После восстановления пропали секреты репозиториев. Секреты, заданные через UI Drone (Settings → Secrets), хранятся в самой базе данных, поэтому пропадают только если восстановлен неполный или устаревший дамп. Если использовался внешний секрет-менеджер (например, Vault) — его бэкап нужно вести отдельно, база Drone хранит в этом случае только ссылку на секрет, а не значение.
  • Runner подключился, но контейнеры шагов пайплайна не запускаются. Это уже не про бэкап базы — runner'у для запуска контейнеров нужен доступ к Docker-сокету (/var/run/docker.sock), и после переноса на новый сервер этот том иногда забывают перепробросить в docker-compose.yml.

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

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

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

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

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

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

Нужно ли бэкапить сам .drone.yml?

Нет, он лежит в репозитории и версионируется вместе с кодом на стороне Git-хостинга (Gitea/GitHub/GitLab) — Drone его не хранит и не модифицирует, только читает при каждом запуске пайплайна.

Можно ли бэкапить Drone без остановки сервиса?

Да, но для SQLite используйте команду .backup, а не простое копирование файла — она делает атомарный снимок на живой базе без риска повреждения. Для PostgreSQL pg_dump изначально безопасен для работы на активной базе.

Нужно ли переносить кеш Docker-образов runner'а при миграции?

Нет, это не требуется. Runner при первом запуске сборки на новом сервере просто заново скачает нужные образы — это одноразовая задержка на первых нескольких пайплайнах, не потеря данных.

Как часто снимать бэкап Drone CI?

Для команд с активной разработкой достаточно раз в сутки — история сборок и логи не меняются задним числом, риск потерять данные "за день" некритичен для большинства сценариев, в отличие, скажем, от продовой базы данных приложения.

Что делать, если нужен и Drone, и приватный registry для собранных образов рядом?

Это отдельный сервис со своим бэкапом — подробно логика разобрана в статье про настройку приватного Docker-registry, она хорошо сочетается с Drone CI на одном сервере разработки.

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

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

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