Бэкап PocketBase: pb_data, встроенные бэкапы, S3 и восстановление

PocketBase хранит всё состояние приложения в одной папке pb_data, и это одновременно удобно и опасно: потеряли папку — потеряли пользователей, записи и загруженные файлы. Ниже — что именно нужно бэкапить, как пользоваться встроенными бэкапами, как сделать копию без них и как восстановиться, не испортив базу. Если вы только знакомитесь с платформой, начните с обзора что такое PocketBase и статьи PocketBase как backend.

Что лежит в pb_data

Типичное содержимое каталога:

  • data.db — основная база SQLite: коллекции, записи, пользователи, настройки приложения. Это главное, что нужно сохранить.
  • auxiliary.db — в новых версиях есть также auxiliary.db с логами запросов и служебными данными. Её потеря обычно не критична, но если логи вам нужны для разборов, бэкапьте и её.
  • storage/ — загруженные файлы (аватары, вложения), если вы используете локальное файловое хранилище. Их нет в базе: в data.db лежат только имена файлов.
  • backups/ — архивы встроенных бэкапов, если они хранятся локально.
  • Рядом с .db-файлами могут лежать -wal и -shm: PocketBase работает с SQLite в режиме WAL.

Важно: папка pb_migrations с JS- или Go-миграциями — это описание схемы коллекций, и её место в git. Миграции позволяют воссоздать структуру, но не данные. Репозиторий с миграциями не является бэкапом.

Встроенные бэкапы в дашборде

В админке PocketBase (/_/) в разделе Settings → Backups можно:

  • создать бэкап вручную — это zip-архив содержимого pb_data (без самой папки backups);
  • включить автоматические бэкапы по расписанию, заданному cron-выражением, например 0 3 * * * для ежедневного запуска в 03:00;
  • хранить архивы не на диске сервера, а в S3-совместимом хранилище — у бэкапов отдельные настройки S3, независимые от хранилища файлов.

Плюсы: ничего не нужно ставить, архив включает и базы, и локальные файлы, восстановление делается из того же интерфейса.

Ограничения, которые стоит учитывать:

  • Если архивы хранятся в pb_data/backups на том же диске, они погибнут вместе с сервером. Включите S3 или копируйте архивы наружу.
  • Архив целиком, со всеми файлами из storage/. При большом объёме загрузок каждый бэкап получается тяжёлым и долгим. В этом случае лучше вынести файлы в S3 (см. ниже), а бэкапить только базы.
  • Во время создания архива приложение может ненадолго ограничивать запись — планируйте бэкапы на время низкой нагрузки.

Бэкап через API

Встроенный бэкап можно запустить и снаружи, например из CI перед деплоем. Нужен токен суперпользователя:

PB_URL=https://pb.example.ru

TOKEN=$(curl -s -X POST "$PB_URL/api/collections/_superusers/auth-with-password" \
  -H 'Content-Type: application/json' \
  -d "{\"identity\":\"$PB_ADMIN_EMAIL\",\"password\":\"$PB_ADMIN_PASSWORD\"}" \
  | jq -r .token)

# создать бэкап с заданным именем
curl -s -X POST "$PB_URL/api/backups" \
  -H "Authorization: $TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"name":"pre_deploy_20260924.zip"}'

# список бэкапов
curl -s "$PB_URL/api/backups" -H "Authorization: $TOKEN"

Учётные данные передавайте через переменные окружения или секреты CI, а не храните в скрипте. В версиях до 0.23 администраторы были отдельной сущностью, и путь авторизации отличался; сверяйтесь с документацией своей версии.

Бэкап без встроенного механизма

Встроенные бэкапы — не единственный вариант. Если вы хотите хранить версии базы отдельно от файлов или уже используете cron на сервере, делайте копии сами.

Консистентная копия data.db

PocketBase держит базу открытой и пишет в неё постоянно. Простой cp data.db во время работы может дать копию без последних транзакций из -wal или вовсе повреждённый файл. Используйте онлайн-бэкап SQLite — он безопасен при работающем приложении:

sqlite3 /opt/pocketbase/pb_data/data.db ".backup '/var/backups/pb/data-$(date +%F).db'"
sqlite3 /opt/pocketbase/pb_data/auxiliary.db ".backup '/var/backups/pb/aux-$(date +%F).db'"

Подробнее о .backup, VACUUM INTO и режиме WAL — в статье как сделать бэкап SQLite.

Файлы из storage

Для локального хранилища синхронизируйте pb_data/storage в S3 через rclone. Пример remote для Yandex Object Storage в ~/.config/rclone/rclone.conf:

[yos]
type = s3
provider = Other
endpoint = https://storage.yandexcloud.net
region = ru-central1
access_key_id = ...
secret_access_key = ...
rclone sync /opt/pocketbase/pb_data/storage yos:pb-backups/storage

rclone sync удаляет в бакете то, чего нет в источнике. Если пользователь удалил файл по ошибке, синхронизация удалит и копию — включите версионирование объектов в бакете или используйте rclone copy, если удаления не критичны.

Альтернатива — в настройках PocketBase переключить хранение файлов на S3. Тогда загрузки сразу уходят в бакет, storage/ на сервере не растёт, а защиту файлов обеспечивают версионирование и политика хранения бакета. Бэкапить на сервере остаётся только базы.

Скрипт и таймер

#!/usr/bin/env bash
# /usr/local/bin/backup-pocketbase.sh
set -euo pipefail

PB=/opt/pocketbase/pb_data
DIR=/var/backups/pb
TS=$(date +%Y%m%d-%H%M)
mkdir -p "$DIR"

sqlite3 "$PB/data.db" ".backup '$DIR/data-$TS.db'"
sqlite3 "$DIR/data-$TS.db" "PRAGMA quick_check;" | grep -qx ok
gzip "$DIR/data-$TS.db"

aws s3 cp "$DIR/data-$TS.db.gz" "s3://pb-backups/db/data-$TS.db.gz" \
  --endpoint-url https://storage.yandexcloud.net
rclone sync "$PB/storage" yos:pb-backups/storage

find "$DIR" -name 'data-*.db.gz' -mtime +14 -delete
# /etc/systemd/system/backup-pocketbase.timer
[Unit]
Description=PocketBase backup

[Timer]
OnCalendar=*-*-* 03:15:00
Persistent=true

[Install]
WantedBy=timers.target

К таймеру нужен одноимённый backup-pocketbase.service с Type=oneshot и ExecStart=/usr/local/bin/backup-pocketbase.sh, затем sudo systemctl enable --now backup-pocketbase.timer. Для Selectel замените эндпоинт на https://s3.ru-1.storage.selcloud.ru. Старые объекты в бакете удобнее чистить правилом жизненного цикла в панели провайдера.

Если PocketBase запущен в Docker, pb_data — это volume или bind mount. Скрипт можно запускать на хосте по пути к bind mount; нюансы с именованными volume и Coolify разобраны в статье бэкап базы в Docker и Coolify.

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

Из встроенного бэкапа

В Settings → Backups выберите архив и запустите восстановление. PocketBase заменит содержимое pb_data и перезапустит приложение. Перед этим сделайте свежий бэкап текущего состояния: если вы ошиблись архивом, будет куда вернуться. Если PocketBase работает под supervisor или в контейнере с нестандартным запуском, проверьте, что после перезапуска процесс действительно поднялся.

Вручную

sudo systemctl stop pocketbase

# сохранить текущее состояние на всякий случай
sudo mv /opt/pocketbase/pb_data /opt/pocketbase/pb_data.broken-$(date +%F)

# вариант 1: распаковать zip встроенного бэкапа
sudo mkdir /opt/pocketbase/pb_data
sudo unzip /tmp/pb_backup.zip -d /opt/pocketbase/pb_data

# вариант 2: собрать pb_data из своих копий
# gunzip -c data-20260924-0315.db.gz > /opt/pocketbase/pb_data/data.db
# rclone copy yos:pb-backups/storage /opt/pocketbase/pb_data/storage

sqlite3 /opt/pocketbase/pb_data/data.db "PRAGMA integrity_check;"
sudo chown -R pocketbase:pocketbase /opt/pocketbase/pb_data
sudo systemctl start pocketbase

Если подменяете только data.db в существующей папке, удалите рядом старые data.db-wal и data.db-shm — иначе SQLite попытается применить к новой базе журнал от старой. После запуска зайдите в админку, проверьте число записей в ключевых коллекциях и войдите тестовым пользователем. Общий порядок действий и проверок описан в статье как восстановить базу из бэкапа, а регулярный тест восстановления — в материале как проверить, что бэкап рабочий.

Чек-лист перед обновлением PocketBase

До версии 1.0 между релизами бывают несовместимые изменения (например, в 0.23 администраторы стали системной коллекцией _superusers). При старте новая версия применяет свои миграции к data.db, и откатить их назад простой заменой бинарника нельзя.

  1. Прочитайте changelog всех версий между текущей и целевой.
  2. Сделайте бэкап pb_data и подпишите его версией PocketBase, например pre-upgrade-0.x.
  3. Сохраните старый бинарник рядом с новым.
  4. Проверьте обновление на копии: скопируйте бэкап на тестовую машину, запустите новую версию и пройдите основные сценарии.
  5. Обновите прод, проверьте логи и ключевые коллекции.
  6. Если что-то пошло не так: остановите сервис, верните старый бинарник и pb_data из бэкапа шага 2.

Как это сделать в dbsend

dbsend хранит версии data.db как базы SQLite: показывает схему, таблицы и число строк в каждой версии и сравнивает любые две версии (на платных тарифах). Удобно для вопроса «что изменилось в коллекциях после обновления». CLI сам делает консистентную копию через VACUUM INTO, отдельный .backup не нужен:

npm install --global @dbsend/sdk
dbsend login --key sqv_pk_…

dbsend backup /opt/pocketbase/pb_data/data.db -d "$DBSEND_DATABASE_ID" -e sqlite -l "daily"

# перед обновлением
dbsend backup /opt/pocketbase/pb_data/data.db -d "$DBSEND_DATABASE_ID" -e sqlite -l "pre-upgrade"
dbsend log
dbsend pin <snapshotId>

# восстановление: остановите PocketBase, затем
dbsend restore <snapshotId> /opt/pocketbase/pb_data/data.db --yes

dbsend restore проверяет sha256 и PRAGMA integrity_check и с --yes сам убирает устаревшие -wal/-shm рядом с целевым файлом. Если бэкап не пришёл по расписанию, на платных тарифах придёт алерт в Telegram, на email или в webhook. Файлы из storage/ dbsend не инспектирует — для них оставьте S3 или rclone. Подробнее: бэкапы SQLite и документация CLI. Про выбор между репликацией и периодическими версиями — в сравнении Litestream, cron и dbsend. Если PocketBase служит бэкендом для Telegram-бота, посмотрите также бэкап базы Telegram-бота.

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

Достаточно ли встроенных бэкапов PocketBase?

Для небольшого проекта — да, если архивы уходят в S3, а не лежат на том же диске. Если загруженных файлов много или нужна история изменений базы отдельно от файлов, дополните их своей копией data.db и синхронизацией файлов.

Можно ли просто скопировать папку pb_data?

Только при остановленном PocketBase. На работающем сервере копируйте data.db через sqlite3 ... ".backup ..." или используйте встроенный бэкап — простой cp может пропустить данные из -wal.

Нужно ли бэкапить auxiliary.db?

Если там только логи и вы ими не пользуетесь — можно обойтись без неё. Если логи нужны для расследований или аудита, бэкапьте её так же, как data.db.

Миграции в git — это бэкап?

Нет. pb_migrations восстанавливает структуру коллекций, но не записи, пользователей и файлы. Миграции и бэкап данных нужны вместе.

Как бэкапить PocketBase, если файлы хранятся в S3?

Бэкапьте data.db (и при необходимости auxiliary.db), а для бакета с файлами включите версионирование объектов и правила хранения у провайдера. Встроенный бэкап в этом режиме получается компактнее, потому что файлы в архив не попадают.