База данных — обычно самое дорогое, что есть в проекте. Код лежит в Git и восстанавливается за минуту, а потерянные данные пользователей не возвращаются ниоткуда. Резервная копия существует ровно до первого инцидента — а проверенная резервная копия выручает уже во время него.
Ниже — практическое руководство по бэкапам PostgreSQL: какие бывают, как их автоматизировать, куда складывать и как убедиться, что из них действительно можно восстановиться.
Логический и физический бэкап
У PostgreSQL два принципиально разных подхода к резервным копиям.
Логический бэкап (pg_dump, pg_dumpall) — это, по сути, набор SQL-команд или сжатый архив, который воссоздаёт схему и данные. Он переносим между версиями и архитектурами, удобен для одной базы и читаем. Минус — медленнее на больших объёмах и не хранит точное физическое состояние.
Физический бэкап (pg_basebackup, копия каталога данных) — это побайтовая копия файлов кластера. Быстрее на больших базах и нужен для Point-in-Time Recovery, но привязан к версии и платформе.
Для большинства проектов до десятков гигабайт логического бэкапа через pg_dump достаточно. С него и начнём.
Форматы pg_dump
У pg_dump есть несколько форматов вывода, и выбор формата важнее, чем кажется.
- plain (
-Fp) — обычный SQL-файл. Читаемый, но восстанавливается только целиком и без параллелизма. - custom (
-Fc) — сжатый бинарный формат. Позволяет выборочное восстановление таблиц и параллельную загрузку. Рекомендуемый вариант по умолчанию. - directory (
-Fd) — каталог файлов, поддерживает параллельный дамп (-j), что заметно ускоряет выгрузку больших баз.
Базовая команда для одной базы в custom-формате:
pg_dump -Fc -h localhost -U appuser -d appdb -f appdb.dump
Для большой базы — directory-формат с параллелизмом по числу ядер:
pg_dump -Fd -j 4 -h localhost -U appuser -d appdb -f appdb_backup/
Роли, права и список баз pg_dump не выгружает. Чтобы сохранить глобальные объекты кластера (пользователей, табличные пространства), используется pg_dumpall --globals-only:
pg_dumpall -h localhost -U postgres --globals-only -f globals.sql
Восстановление
Custom- и directory-форматы восстанавливаются через pg_restore, plain-дамп — через psql.
# создать чистую базу
createdb -h localhost -U postgres appdb_restored
# восстановить из custom-дампа в 4 потока
pg_restore -h localhost -U postgres -d appdb_restored -j 4 appdb.dump
Флаг --clean добавит команды удаления объектов перед восстановлением — удобно при накатывании дампа поверх существующей базы, но опасно, если перепутать целевую базу. Восстанавливать всегда стоит в отдельную базу, а не поверх боевой.
Автоматизация через cron
Ручной бэкап забывают сделать ровно в тот день, когда он нужен. Минимальный скрипт, который снимает дамп, сжимает его и удаляет копии старше N дней:
#!/usr/bin/env bash
set -euo pipefail
BACKUP_DIR=/var/backups/postgres
KEEP_DAYS=14
STAMP=$(date +%Y-%m-%d_%H-%M)
mkdir -p "$BACKUP_DIR"
pg_dump -Fc -h localhost -U appuser -d appdb \
-f "$BACKUP_DIR/appdb_$STAMP.dump"
# удалить копии старше KEEP_DAYS
find "$BACKUP_DIR" -name 'appdb_*.dump' -mtime +"$KEEP_DAYS" -delete
Пароль не стоит передавать в команде — для неинтерактивного запуска используется файл ~/.pgpass с правами 600:
localhost:5432:appdb:appuser:secret
Запуск каждую ночь в 3:30 через crontab -e:
30 3 * * * /usr/local/bin/pg_backup.sh >> /var/log/pg_backup.log 2>&1
Флаг set -euo pipefail важен: без него скрипт «успешно» завершится даже после ошибки pg_dump, и в логе останется ноль предупреждений, а в каталоге — битый файл.
Правило 3-2-1: бэкап на том же сервере — не бэкап
Если дамп лежит на том же диске, что и база, то отказ диска или потеря сервера уносит и базу, и копию. Классическое правило надёжного хранения — 3-2-1:
- 3 копии данных,
- на 2 разных носителях,
- 1 из них — за пределами площадки (offsite).
На практике для небольшого проекта это означает: рабочая база, локальный дамп и выгрузка в объектное хранилище. Загрузить готовый дамп в S3-совместимое хранилище можно одной командой:
# через aws-cli (подходит и для Yandex Object Storage, Backblaze B2, MinIO)
aws s3 cp "$BACKUP_DIR/appdb_$STAMP.dump" \
s3://my-backups/postgres/ --endpoint-url https://storage.yandexcloud.net
Шифровать дамп перед отправкой стоит, если в нём есть персональные данные — например, через gpg или restic, который умеет и инкрементальные шифрованные бэкапы, и ротацию.
Самое важное: проверка восстановления
Непроверенный бэкап — это бэкап в состоянии кота Шрёдингера: неизвестно, рабочий он или нет, пока не понадобится. Типичные сюрпризы вскрываются именно при восстановлении: дамп снимался без нужной роли, кончилось место, версия pg_restore несовместима, в копию не попала схема.
Минимальная регулярная проверка — развернуть свежий дамп в одноразовую базу и убедиться, что данные на месте:
createdb appdb_test
pg_restore -d appdb_test -j 4 appdb.dump
psql -d appdb_test -c "SELECT count(*) FROM users;"
dropdb appdb_test
Это стоит делать по расписанию, а не один раз после настройки. Бэкап, который ни разу не разворачивали, — это гипотеза, а не страховка.
Point-in-Time Recovery — кратко
Логический дамп возвращает состояние на момент снятия. Если нужно восстановиться на произвольную секунду (например, «за минуту до ошибочного DELETE»), требуется PITR: базовая физическая копия плюс непрерывная архивация WAL-журналов (archive_mode, archive_command). Восстановление накатывает WAL до указанной точки во времени.
PITR мощнее, но и заметно сложнее в эксплуатации: нужно хранить и ротировать WAL, следить за местом, регулярно делать новые базовые копии. Для большинства продуктов разумная стратегия — ежедневный логический бэкап с offsite-копией, а PITR подключать, когда допустимая потеря данных измеряется минутами, а не часами.
Чеклист
- Бэкап снимается автоматически по расписанию, а не руками.
- Используется
-Fcили-Fd, а не plain-дамп. - Глобальные роли сохраняются через
pg_dumpall --globals-only. - Пароль — в
~/.pgpassс правами600, не в команде. - Старые копии ротируются (
find -mtime). - Минимум одна копия лежит за пределами сервера с базой.
- Восстановление проверяется по расписанию, а не теоретически.
- Дампы с персональными данными шифруются.
Всё перечисленное — это инфраструктура, которую нужно настроить, а потом не забывать обслуживать: следить за местом, за тем, что cron не отвалился, что offsite-выгрузка проходит, что восстановление всё ещё работает.
На Hostim managed-базы (Postgres, MySQL, MongoDB, ClickHouse и другие) поставляются с ежедневными бэкапами и восстановлением одной командой — без cron-скриптов, .pgpass и ручной ротации. Это снимает с проекта целый класс задач, в которых ошибаются именно тогда, когда копия нужнее всего.