VPS

Резервная копия PostgreSQL: скрипт pg_dump и проверка восстановления

Готовый скрипт дампа PostgreSQL, контрольная сумма и восстановление в отдельную базу. Проверенный опыт: три записи из копии и нетронутая рабочая база.

Резервная копия PostgreSQL: скрипт pg_dump и проверка восстановления

Резервная копия PostgreSQL полезна тогда, когда из неё действительно можно получить работающую базу. В этом руководстве сделаем логический дамп одной базы, проверим контрольную сумму, восстановим его в отдельную пустую базу и сравним данные. Рабочую базу при проверке не заменяем.

Проверено 29 октября 2026 года: Ubuntu 24.04.5, PostgreSQL 16.15, локальный кластер на порту 5432. Перед опытом в базе было три записи. После создания копии мы добавили четвёртую: восстановление вернуло три исходные записи, а исходная база сохранила все четыре.

Когда подходит pg_dump

Этот сценарий подходит небольшой или средней базе, для которой приемлемо восстановление из последнего логического дампа. pg_dump одной базы не сохраняет весь VPS, системные роли кластера, настройки PostgreSQL и файлы приложения. Для восстановления на точный момент времени между копиями нужна другая схема с физическими копиями и WAL.

Пример продолжает установку PostgreSQL: база hf_app, владелец hf_app, таблица lessons. Если у вас другое приложение, замените имена и заранее выберите собственные проверки данных. Не копируйте каталог работающего PostgreSQL обычным cp как замену штатному резервированию.

Скачать скрипт и проверить содержимое

Скачать hostfera-pg-backup-1.0.0.sh. Скрипт работает через sudo на сервере с локальным PostgreSQL и установленными pg_dump/pg_restore. Пароль он не запрашивает: запускает клиент от системного пользователя postgres через Unix-сокет. Это рассчитано на стандартную локальную аутентификацию Ubuntu.

curl --fail --show-error --location \
  https://hostfera.ru/static/downloads/hostfera-pg-backup-1.0.0.sh \
  --output hostfera-pg-backup-1.0.0.sh
sha256sum hostfera-pg-backup-1.0.0.sh
less hostfera-pg-backup-1.0.0.sh

SHA-256 версии 1.0.0:

6910855c038d98c2344168d2cfead05a23677990d9f5624f4739199d8f80dad1

Сначала прочитайте скрипт. Сверка с хешем на этой же странице помогает заметить повреждение или другую версию файла; она не заменяет независимую проверку происхождения. Не запускайте скачанное сразу через curl | sudo bash.

Полный код скрипта

#!/usr/bin/env bash
# Hostfera: custom-format dump of one local PostgreSQL database.
set -Eeuo pipefail
umask 077
[[ $EUID -eq 0 ]] || { echo 'Запустите через sudo bash.' >&2; exit 1; }
[[ $# -eq 1 && $1 =~ ^[a-z][a-z0-9_]{0,62}$ ]] || {
  echo 'Использование: sudo bash hostfera-pg-backup-1.0.0.sh имя_базы' >&2; exit 1;
}
DB=$1
BASE=/var/backups/hostfera-postgresql
[[ ! -L $BASE ]] || { echo 'Каталог копий не должен быть символической ссылкой.' >&2; exit 1; }
if [[ -e $BASE ]]; then
  [[ -d $BASE && $(stat -c %u "$BASE") == 0 ]] || { echo 'Проверьте владельца каталога копий.' >&2; exit 1; }
fi
command -v pg_dump >/dev/null
command -v pg_restore >/dev/null
id postgres >/dev/null
install -d -m 0700 -o root -g root "$BASE"
DIR=$(mktemp -d "$BASE/${DB}_$(date -u +%Y%m%dT%H%M%SZ)_XXXXXXXX")
trap 'rm -f -- "$DIR/database.dump.partial"' EXIT
runuser -u postgres -- pg_dump --host=/var/run/postgresql --port=5432 \
  --format=custom --dbname="$DB" > "$DIR/database.dump.partial"
pg_restore --list < "$DIR/database.dump.partial" > "$DIR/contents.txt"
mv -- "$DIR/database.dump.partial" "$DIR/database.dump"
(cd "$DIR" && sha256sum database.dump > SHA256SUMS && sha256sum --check SHA256SUMS)
{
  printf 'database=%s\ncreated_utc=%s\n' "$DB" "$(date -u +%FT%TZ)"
  pg_dump --version
  stat -c 'dump_bytes=%s' "$DIR/database.dump"
} > "$DIR/metadata.txt"
printf 'Резервная копия: %s\n' "$DIR"
printf 'Оглавление: %s/contents.txt\nПроверка SHA-256: успешно\n' "$DIR"
printf 'Теперь проверьте восстановление в отдельную пустую базу.\n'

Скрипт принимает одно простое имя базы: строчная латинская буква, затем буквы, цифры или подчёркивания, всего до 63 символов. Имена с пробелами, дефисами и кавычками эта версия намеренно не поддерживает. Сокет /var/run/postgresql и порт 5432 заданы явно. При другом порте или нестандартной аутентификации сначала адаптируйте и проверьте копию скрипта.

Создать копию и понять результат

pg_dump --version
sudo -u postgres psql -X -c 'SHOW server_version;'
sudo bash hostfera-pg-backup-1.0.0.sh hf_app

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

Копия появится в новом закрытом каталоге /var/backups/hostfera-postgresql/hf_app_.... Внутри — database.dump в custom-формате, contents.txt, SHA256SUMS и metadata.txt. При ошибке pg_dump незавершённый .partial удаляется, успешный database.dump не создаётся. Уже созданные копии скрипт не удаляет.

pg_restore --list читает оглавление архива, а SHA-256 позволяет обнаружить изменение байтов относительно сохранённой суммы. Обе проверки не заменяют восстановление: оглавление может читаться при проблеме с данными, а корректный хеш ничего не говорит о пригодности схемы для приложения.

Восстановить в новую пустую базу

Ниже команды для собственной доверенной копии. Замените путь на тот, который напечатал скрипт. Роль hf_app должна уже существовать. Имя hf_restorecheck зарезервируйте только для этой проверки.

sudo -i
BACKUP_DIR=/var/backups/hostfera-postgresql/ЗАМЕНИТЕ_НА_ВАШ_КАТАЛОГ
cd "$BACKUP_DIR"
sha256sum --check SHA256SUMS
sudo -u postgres createdb --template=template0 --owner=hf_app hf_restorecheck
sudo -u postgres pg_restore \
  --dbname=hf_restorecheck \
  --no-owner --no-privileges --role=hf_app \
  --exit-on-error --single-transaction < database.dump
exit

Выполняйте команды последовательно и продолжайте только после успешного завершения каждой. Если createdb сообщает, что база уже существует, остановитесь и выберите другое новое имя. Не запускайте восстановление в существующую базу. Не добавляйте --clean и не подставляйте имя рабочей базы ради повторного опыта.

Перенаправление < database.dump выполняет root-оболочка: закрытый каталог копий не приходится открывать другим пользователям. Параметр --role=hf_app назначает создаваемые объекты роли приложения, а сохранённые владельцы и разрешения не переносятся. Такой упрощённый вариант подходит нашей базе с одним владельцем. Для нескольких ролей, расширений и сложных ACL заранее спроектируйте восстановление прав.

--exit-on-error --single-transaction прекращает восстановление при ошибке и откатывает изменения этой операции. Команда может потребовать заметного времени и ресурсов на большой базе; учебный пример не является замером скорости восстановления вашего проекта.

Проверить данные и приложение

sudo -u postgres psql -X -d hf_restorecheck \
  -c 'SELECT id, title FROM lessons ORDER BY id;'
sudo -u postgres psql -X -d hf_app \
  -c 'SELECT count(*) FROM lessons;'
Три записи в hf_restorecheck после pg_restore и четыре записи в исходной hf_app
Проверка восстановления: три записи из копии, четыре в исходной базе

На снимке — фактический результат нашего стенда: три записи из снимка в hf_restorecheck и четыре в исходной hf_app. В своём проекте проверьте не только число строк, но и критичные связи, несколько объектов и чтение данных приложением, подключённым именно к тестовой базе. Не направляйте туда рабочие платежи, письма и фоновые задания.

После проверки можно удалить только созданную для неё базу. Сначала сверьте имя и убедитесь, что тестовое приложение отключено:

sudo -u postgres dropdb --interactive hf_restorecheck

Где хранить копии и как автоматизировать

Каталог на том же VPS помогает при ошибке приложения, но не защищает от потери сервера. Нужна независимая копия, например в приватном объектном хранилище S3, со своими правами доступа и политикой хранения. Дамп содержит данные приложения: не кладите его в каталог сайта и не делайте бакет публичным.

Прежде чем добавлять расписание, добейтесь успешного ручного восстановления. Затем настройте отдельную службу и таймер по руководству о ежедневных копиях, заменив команду создания архива на этот скрипт. Установите скрипт в каталог, доступный для изменения только root. Контролируйте код завершения, возраст последней копии, свободное место и результат отправки во внешнее хранилище. Эта версия сама не ставит таймер, не отправляет файлы и не удаляет старые дампы.

Первоисточники

Нашли неточность?

Сообщите адрес статьи и фрагмент через контакты редакции.