PITR (Point-In-Time Recovery) — это возможность восстановить физический кластер PostgreSQL до состояния на конкретный момент времени. В реальной эксплуатации PITR защищает от человеческих ошибок, неудачных миграций и некорректных изменений данных, дополняя регулярные «полные» бэкапы. В статье разберём, как включить архивацию WAL, корректно снимать base backup, выстроить политику хранения и отработать тест восстановления.
Как работает PITR вкратце
PostgreSQL записывает все изменения в журналы предзаписи WAL (Write-Ahead Log). Если у нас есть:
- актуальный «срез» кластера — base backup;
- непрерывная последовательность WAL-сегментов с момента снятия этого среза;
— то мы можем «прокрутить» состояние данных до выбранной точки: по времени, по метке восстановления или по конкретному LSN.
PITR — физическое восстановление всего кластера: вы получаете консистентное состояние всех БД кластера. Логические бэкапы (дампы) — отдельная тема.
Подготовка: требования и план
Перед стартом определите RPO/RTO. RPO (сколько данных допустимо потерять) определяет частоту base backup и размер окна хранения WAL. RTO задаёт сценарий и скорость восстановления (куда, как быстро, чем автоматизировать).
Минимум потребуется:
- Дисковое пространство под архив WAL и несколько поколений «полных» бэкапов.
- Синхронизированное время (NTP), чтобы точно указывать
recovery_target_time
и сопоставлять события. - Выделенная директория для архива WAL (локально или во внешнем хранилище) и права пользователя
postgres
. - Понимание версии PostgreSQL (для 12+ используется файл-сигнал
recovery.signal
, а неrecovery.conf
).
Тестовое восстановление безопаснее выполнять на отдельном стенде. Удобно поднять его на VDS, чтобы не рисковать продуктивом.
Включаем архивацию WAL
Необходимо непрерывно сохранять все завершённые WAL-сегменты в отдельное устойчивое хранилище. Управляют этим параметры archive_mode
и archive_command
.
Создаём директорию для архива
sudo mkdir -p /var/lib/postgresql/wal_archive
sudo chown -R postgres:postgres /var/lib/postgresql/wal_archive
sudo chmod 700 /var/lib/postgresql/wal_archive
Правим конфигурацию PostgreSQL
Откройте postgresql.conf
и проверьте/добавьте ключевые параметры:
wal_level = replica
archive_mode = on
archive_command = 'test ! -f /var/lib/postgresql/wal_archive/%f && cp %p /var/lib/postgresql/wal_archive/%f'
archive_timeout = 60s
logging_collector = on
log_checkpoints = on
log_connections = on
log_destination = 'stderr'
Пояснения:
wal_level = replica
— минимально необходимый уровень для PITR.archive_mode = on
— включает архивацию завершённых WAL-сегментов.archive_command
— должен быть идемпотентным. В примере мы не перезаписываем уже сохранённые файлы. Команда обязана возвращать0
при успехе, иначе PostgreSQL будет повторять попытки.archive_timeout
— принудительно «закрывает» сегмент WAL, если он долго не заполняется (актуально при низкой нагрузке).
Примените конфигурацию перезапуском:
sudo systemctl restart postgresql
Проверяем, что сегменты уходят в архив
sudo -u postgres psql -c "SELECT * FROM pg_stat_archiver;"
Следите за полями archived_count
и last_archived_wal
. Убедитесь, что в директории архива появляются файлы вида 0000000100000000000000A1
.
Снимаем base backup
Base backup — консистентная копия каталога данных. Обычно его снимают утилитой pg_basebackup
. Для сетевого бэкапа потребуется роль с правами репликации.
Создаём роль для бэкапов (опционально)
sudo -u postgres psql -c "CREATE ROLE bkp REPLICATION LOGIN PASSWORD 'strongpass';"
Если бэкап запускается локально от пользователя postgres
, дополнительная роль не обязательна.
Вариант: tar-архивы
sudo -u postgres pg_basebackup -D /backups/pg/base/2025-01-15 -F tar -X stream -c fast -z -P
-F tar
— формат tar (удобно хранить и переносить).-X stream
— включить WAL в поток бэкапа, гарантируя консистентность без отдельного копирования текущих WAL.-c fast
— быстрый checkpoint перед началом копии.-z
— сжатие tar.-P
— прогресс.
Вариант: «плоская» директория
sudo -u postgres pg_basebackup -D /backups/pg/base/2025-01-15 -F p -X stream -c fast -P
Этот формат удобен для локального восстановления, так как не требует распаковки tar.
Проверка целостности бэкапа
Проверьте, что бэкап содержит файл backup_manifest
(в новых версиях) и каталоги/файлы системных баз (base
, global
, pg_wal
и т. д.). Для tar — убедитесь, что архив читается без ошибок:
sudo -u postgres tar -tf /backups/pg/base/2025-01-15/base.tar.gz | head -n 20
Политика хранения: сколько держать WAL и бэкапов
Чтобы восстановиться в любую точку между двумя base backup, нужно хранить все WAL за этот период. Типичный подход:
- Полный бэкап раз в сутки (или чаще для более строгого RPO).
- Хранить N полных бэкапов (например, 7–14 дней — зависит от объёма и требований).
- Хранить все WAL-сегменты, начиная с самого старого из сохранённых бэкапов.
Ротация выполняется внешними скриптами/утилитами: удаляем старые base backup и соответствующие им WAL. Нельзя удалять WAL, которые ещё нужны для любого из оставшихся бэкапов. Удобно хранить для каждого бэкапа метку с минимальным требуемым сегментом или LSN.

Практический тест восстановления до точки (PITR)
Регулярный тест — единственный надёжный способ убедиться, что PITR работает и соответствует RPO/RTO. Ниже — воспроизводимый сценарий для PostgreSQL 12+.
1) Создаём метку восстановления
Перед потенциально опасными изменениями создайте явную метку восстановления:
sudo -u postgres psql -d postgres -c "SELECT pg_create_restore_point('before_migration');"
Запомните метку и время. Для контроля можно посмотреть текущий LSN:
sudo -u postgres psql -d postgres -c "SELECT pg_current_wal_lsn();"
2) Имитируем изменения
sudo -u postgres psql -d postgres -c "CREATE TABLE pitr_demo(id int primary key, note text);"
sudo -u postgres psql -d postgres -c "INSERT INTO pitr_demo VALUES (1, 'bad change');"
Считайте это ошибочным изменением, от которого мы захотим уйти при восстановлении.
3) Готовим чистое место восстановления
Остановите кластер и подготовьте каталог данных для восстановления (используем отдельную директорию, чтобы не рисковать продуктивом):
sudo systemctl stop postgresql
sudo mkdir -p /var/lib/postgresql/restore_datadir
sudo chown -R postgres:postgres /var/lib/postgresql/restore_datadir
sudo chmod 700 /var/lib/postgresql/restore_datadir
4) Разворачиваем base backup
Если бэкап в tar:
sudo -u postgres tar -xzf /backups/pg/base/2025-01-15/base.tar.gz -C /var/lib/postgresql/restore_datadir
Если бэкап снят в «плоский» каталог — скопируйте содержимое внутрь /var/lib/postgresql/restore_datadir
с сохранением прав.
5) Настраиваем восстановление
Добавьте в postgresql.conf
(внутри каталога восстановления) параметры восстановления:
restore_command = 'cp /var/lib/postgresql/wal_archive/%f %p'
recovery_target_name = 'before_migration'
recovery_target_action = 'promote'
recovery_target_timeline = 'latest'
Создайте файл-сигнал восстановления:
sudo -u postgres touch /var/lib/postgresql/restore_datadir/recovery.signal
Если в архиве WAL используется сжатие, укажите соответствующую команду, например для gzip:
restore_command = 'gzip -dc /var/lib/postgresql/wal_archive/%f > %p'
6) Стартуем восстановление
Запускаем кластер, указав на новый каталог данных. В зависимости от пакета сервиса может понадобиться отдельный юнит или временная подмена PGDATA
. Для демонстрации используем pg_ctl
:
sudo -u postgres pg_ctl -D /var/lib/postgresql/restore_datadir -l /var/lib/postgresql/restore_datadir/logfile start
PostgreSQL начнёт применять WAL из архива. Когда будет достигнута метка before_migration
, произойдёт автоматический promote благодаря recovery_target_action = 'promote'
.
7) Проверяем результат
sudo -u postgres psql -d postgres -c "SELECT pg_is_in_recovery();"
sudo -u postgres psql -d postgres -c "\\dt"
sudo -u postgres psql -d postgres -c "SELECT * FROM pitr_demo;"
Если восстановление корректно сработало к метке до внесения «плохих» изменений, таблица/строки, созданные после метки, отсутствуют. Также проверьте таймлайн:
sudo -u postgres psql -d postgres -c "SELECT timeline_id FROM pg_control_checkpoint();"
Мониторинг и диагностика
Полезные источники:
pg_stat_archiver
— статус архивации WAL.- Логи PostgreSQL — сообщения о неудачных попытках
archive_command
/restore_command
. pg_waldump
— просмотр содержимого WAL для расследования.
sudo -u postgres pg_waldump /var/lib/postgresql/wal_archive/0000000100000000000000A1 | head -n 20
Типичные ошибки и как их избежать
- Неидемпотентный
archive_command
. При повторных вызовах можно перезаписать или повредить целевой файл. Всегда проверяйте существование файла и код возврата. - Недостаточная ротация. Диск под архивом заполняется внезапно. Введите политику хранения и мониторинг свободного места.
- Преждевременное удаление WAL. Нельзя удалять WAL до того, как он перестанет быть нужен самому старому сохранённому base backup.
- Несовместимость версий при восстановлении. Нельзя восстанавливать данные, снятые на одной мажорной версии, в другую. Используйте ту же мажорную версию PostgreSQL.
- Права и безопасность. Директории бэкапов и архива должны принадлежать
postgres
и быть закрыты от чтения посторонними. При необходимости используйте шифрование хранения. - Ожидания от
archive_timeout
. На низкой нагрузке безarchive_timeout
WAL может долго не «закрываться» — точки восстановления размываются.
Автоматизация: расписания и очистка
Минимальная автоматизация: периодический base backup и ротация старых бэкапов/журналов. Примеры cron:
# Ежедневный base backup (tar + gzip)
0 2 * * * postgres pg_basebackup -D /backups/pg/base/$(date +\%F) -F tar -X stream -c fast -z -P
# Очистка бэкапов старше 14 дней
30 3 * * * root find /backups/pg/base -maxdepth 1 -type d -mtime +14 -exec rm -rf {} \;
# Очистка WAL старше 14 дней (настраивайте под свою политику)
0 4 * * * root find /var/lib/postgresql/wal_archive -type f -mtime +14 -delete
Прежде чем удалять WAL, убедитесь, что не затронете диапазон, необходимый для самого старого оставшегося бэкапа. Полезно хранить файл-«маяк» с указанием минимального нужного сегмента для каждого бэкапа.
Продвинутые опции
- Сжатие и шифрование WAL. Вместо простого
cp
используйте потоковое сжатие (gzip
,lz4
,zstd
) и при необходимости шифрование. Согласуйте это сrestore_command
. - Альтернативные инструменты. Специализированные утилиты для архивации WAL и управления репозиторием дают дедупликацию, параллелизм и удобную политику хранения.
- pg_receivewal. Для непрерывного приёма WAL по сети можно использовать потоковый приёмник — иногда удобнее, чем
archive_command
. - Оффсайт-копии. Храните как минимум одну копию бэкапов и WAL вне основного узла/ДЦ. См. практику резервного копирования в объектное хранилище в материале «Restic и Borg для S3-совместимых бэкапов» по ссылке резервное копирование в объектное хранилище.
Контрольный чек‑лист PITR
- Включены
wal_level = replica
,archive_mode = on
, настроен безопасныйarchive_command
. - Архив WAL стабильно пополняется,
pg_stat_archiver
без ошибок. - Есть свежий base backup, проверенный на целостность.
- Определена и реализована политика хранения: base backup + все WAL между ними.
- Регулярно проводится тест восстановления на стенде: восстановление до метки/времени работает.
- Описана процедура «поднять из бэкапа» с проверками и таймингами (RTO).
Итоги
PITR в PostgreSQL — это не только настройка пары параметров, но и дисциплина: непрерывная архивация WAL, предсказуемые полные бэкапы, прозрачная ротация и периодические тесты восстановления. Следуя шагам из руководства, вы получите воспроизводимую схему с понятными RPO/RTO и уверенность, что в критический момент база будет восстановлена до нужной точки.