Надежное резервное копирование PostgreSQL с возможностью восстановиться до любой точки во времени (PITR) — базовая гигиена продакшена. Связка PostgreSQL + WAL-G + S3-совместимое object storage закрывает задачу полноценно: регулярные base backup, непрерывное wal archiving и проверяемые сценарии backup / restore. Ниже — практический разбор, ориентированный на эксплуатацию на облачном VDS.
Что и зачем: WAL, WAL-G, S3 и PITR
WAL — журналы предзаписи PostgreSQL. При включенном архивировании каждый завершенный сегмент WAL отправляется в безопасное хранилище. В связке с периодическими полными бэкапами это позволяет восстановить кластер на конкретный момент времени, воспроизведя изменения из WAL. Для краткого обзора терминов и сценариев см. материал про основы PITR и WAL в PostgreSQL.
WAL-G — современный инструмент для холодных/горячих бэкапов PostgreSQL с поддержкой потоковой отправки WAL и хранения в S3-совместимых объектных хранилищах. Он умеет:
- делать base backup каталога данных;
- архивировать WAL-сегменты по мере их завершения;
- управлять политикой хранения (retention, delete before/retain);
- восстанавливать базу и WAL до нужной точки времени (PITR).
S3-совместимое object storage — недорогое, масштабируемое, географически отказоустойчивое хранилище. В связке с версионированием и политиками жизненного цикла оно хорошо подходит для резервных копий. Альтернативный стек — pgBackRest; сравнить подходы можно в статье pgBackRest: бэкапы и восстановление PostgreSQL.
Итог: base backup + непрерывная отправка WAL в S3 через WAL-G = возможность восстановить PostgreSQL к любой секунде между бэкапами (PITR), минимизируя потерю данных.
Предпосылки и минимальная архитектура
Что нужно для старта:
- Рабочий экземпляр PostgreSQL (желательно 12+), доступ root и пользователь postgres.
- Доступ к S3-совместимому object storage: бакет, ключи с минимальными правами (только чтение/запись в нужный префикс; отдельно — право удаления для хоста, который отвечает за ретеншн).
- Синхронизированное время (chrony/systemd-timesyncd), стабильная сеть на VDS.
- Достаточно места на диске под workdir и буферы сжатия; CPU для компрессии (lz4/zstd).
Логическая схема:
- PostgreSQL пишет WAL локально.
archive_commandвызываетwal-g wal-push, отправляя сегменты в S3.- Планировщик (systemd timer/cron) периодически запускает
wal-g backup-pushдля полного бэкапа каталога данных. - Политики хранения
wal-g deleteудаляют устаревшие бэкапы и WAL, оставляя нужное окно для PITR.

Установка и базовая структура конфигурации WAL-G
WAL-G поставляется как единый бинарник. Устанавливайте сборку, соответствующую вашей ОС и архитектуре. Бинарник рекомендуется поместить в /usr/local/bin/wal-g и сделать исполняемым. Запускать его будет пользователь postgres.
Конфигурация читается из окружения и/или YAML. Удобно хранить настройки в /etc/wal-g.d/server.yaml с правами 600 и владельцем postgres:postgres.
# /etc/wal-g.d/server.yaml
WALG_S3_PREFIX: s3://your-bucket/pg/prod
AWS_ACCESS_KEY_ID: YOUR_ACCESS_KEY
AWS_SECRET_ACCESS_KEY: YOUR_SECRET_KEY
AWS_REGION: your-region-1
AWS_ENDPOINT: s3.endpoint.internal
S3_FORCE_PATH_STYLE: true
WALG_COMPRESSION_METHOD: zstd
WALG_UPLOAD_CONCURRENCY: 8
WALG_DOWNLOAD_CONCURRENCY: 8
WALG_S3_STORAGE_CLASS: STANDARD
# При необходимости: серверное шифрование на стороне S3
# WALG_S3_SSE: AES256
# Клиентское шифрование PGP (альтернатива SSE)
# WALG_PGP_KEY: ASCII-армированный публичный ключ
Переменные шифрования выбирайте по вашей политике безопасности: SSE в бакете или шифрование на клиенте PGP. Не храните секреты с правами чтения для других пользователей.
Подготовка PostgreSQL к wal archiving
Включим нужные параметры в postgresql.conf и перезапустим сервис:
# postgresql.conf (фрагмент)
wal_level = replica
archive_mode = on
archive_command = 'wal-g wal-push %p'
archive_timeout = 60s
max_wal_senders = 5
wal_compression = on
Пояснения:
wal_level=replica— минимальный уровень для архивации и репликации.archive_mode=onвключает механизм архивации.archive_commandвызывает WAL-G для отправки каждого завершенного сегмента.archive_timeoutфорсирует завершение сегмента, если мало активности.wal_compression=onуменьшает размер WAL.
Убедитесь, что пользователь postgres видит конфигурацию WAL-G (YAML или переменные окружения). Проще всего положить YAML в /etc/wal-g.d/server.yaml и запускать wal-g от postgres; также можно добавить EnvironmentFile или переменную WALG_CONFIG_FILE в drop-in юнит PostgreSQL.
Первый полный бэкап (base backup)
Сделаем первичный снимок данных. Во время создания бэкапа WAL-G корректно обрабатывает backup start/stop, обеспечивая согласованность.
sudo -u postgres wal-g backup-push /var/lib/postgresql/16/main
Путь к каталогу данных укажите свой. По завершении появится запись в списке бэкапов:
sudo -u postgres wal-g backup-list
Рекомендуется автоматизировать base backup через systemd timers (например, раз в сутки вне пикового окна) с уведомлениями об ошибках. Пример минимальных юнитов:
# /etc/systemd/system/walg-backup.service
[Unit]
Description=WAL-G base backup
[Service]
User=postgres
Environment=WALG_CONFIG_FILE=/etc/wal-g.d/server.yaml
ExecStart=/usr/local/bin/wal-g backup-push /var/lib/postgresql/16/main
# /etc/systemd/system/walg-backup.timer
[Unit]
Description=Daily WAL-G base backup
[Timer] 02:30:00
Persistent=true
[Install]
WantedBy=timers.target
sudo systemctl daemon-reload
sudo systemctl enable --now walg-backup.timer
Непрерывная архивация WAL
После включения archive_mode и настройки archive_command PostgreSQL будет вызывать wal-g wal-push на каждый завершенный сегмент WAL. Проверить статус можно в журналах Postgres и командами WAL-G (например, сверив наличие новых архивов). Для форсирования ротации WAL выполните:
sudo -u postgres psql -c "SELECT pg_switch_wal();"
Типичные признаки проблемы:
- в логах Postgres появляются повторяющиеся попытки архивации с ошибкой;
- локальный каталог pg_wal быстро разрастается — архивирование не работает;
- в бакете не появляются новые объекты под префиксом WAL.
В таких случаях проверьте права на запуск wal-g, видимость переменных окружения, корректность префикса WALG_S3_PREFIX и доступы к бакету.
Политика хранения: сколько держать бэкапов и WAL
Обычно задают окно восстановления (например, 7–14 дней) и частоту base backup (например, каждый день). Тогда держим 7–14 полных бэкапов и соответствующие WAL между ними. Примеры команд:
# Оставить 7 последних полных бэкапов (и связанные WAL)
sudo -u postgres wal-g delete retain FULL 7 --confirm
# Удалить все, что старше указанной точки, выбрав ближайший полный бэкап до нее
sudo -u postgres wal-g delete before FIND_FULL 2025-11-01T00:00:00Z --confirm
# Ограничить количество хранимых WAL за последние 168 часов (пример)
sudo -u postgres wal-g delete wal --retain 168 --confirm
Команды удаления следует запускать только на «хосте-хозяйственнике» бэкапов, чтобы не пересекались роли. Перед внедрением — протестируйте в непроизводственной среде.
PITR: восстановление до точки во времени
Сценарий проверки (рекомендую репетировать раз в квартал): поднимаем тестовую копию из бэкапа и воспроизводим WAL до нужного момента времени (до или прямо перед инцидентом).
1) Подготовка каталога данных
sudo systemctl stop postgresql
mv /var/lib/postgresql/16/main /var/lib/postgresql/16/main.broken-2025-11-07
sudo -u postgres wal-g backup-fetch /var/lib/postgresql/16/main LATEST
sudo -u postgres touch /var/lib/postgresql/16/main/recovery.signal
2) Настройка параметров восстановления
Добавьте в postgresql.conf параметры restore и цель восстановления:
# postgresql.conf (фрагмент для PITR)
restore_command = 'wal-g wal-fetch "%f" "%p"'
recovery_target_time = '2025-11-07 13:54:00+03'
recovery_target_action = promote
recovery_target_timeline = latest
Вместо времени можно указать recovery_target_lsn или recovery_target_name, если вы ставили именованные точки восстановления.

3) Запуск и контроль
sudo systemctl start postgresql
PostgreSQL подтянет недостающие WAL из S3 и остановится на целевой точке. В логах будет видно момент «promote». Проверьте консистентность бизнес-данных тестовыми запросами.
Для восстановления «до последнего доступного момента» достаточно не указывать recovery_target_time (или поставить latest), а затем выполнить промоут при достижении конца имеющихся WAL.
Проверка бэкапов и регулярные drill-тесты
Бэкап, который никогда не восстанавливали, — это предположение, а не гарантия. Обязательные практики:
- Раз в N недель выполнять тестовое восстановление в отдельный каталог на отдельном экземпляре VDS.
- Отдельно проверять способность качать WAL за длительный период (сеть, пропускная способность S3, время восстановления).
- Следить за логами WAL-G и Postgres: отдельные алерты на ошибки архивации.
- Автоматизировать backup-list и метрики (возраст последнего base backup, отставание по WAL).
Если в вашей версии WAL-G есть команда верификации (например, backup-verify), используйте ее дополнительно, но финальным критерием все равно остается успешный restore с запуском Postgres.
Оптимизация производительности на VDS
Чтобы бэкапы не мешали продакшен-нагрузке:
- Компрессия: для CPU-ограниченных VDS используйте
lz4; для диско- или сеть-ограниченных —zstdс умеренным уровнем. - Параллелизм:
WALG_UPLOAD_CONCURRENCYиWALG_DOWNLOAD_CONCURRENCYподбирайте экспериментально (обычно 4–8 потоков хорошо использует S3-мультипарт и канал). - Ограничение IO: запускайте бэкапы в непиковые часы, при необходимости используйте cgroups/ionice.
- Сеть: контролируйте исходящий трафик и лимиты провайдера object storage; при больших базах убедитесь, что окно бэкапа укладывается в ночное.
Безопасность и соответствие
Основные моменты:
- Минимальные права для ключей S3: доступ только к нужному префиксу, разделите роли «загрузка» и «удаление».
- Шифрование: включите SSE на бакете или используйте PGP-шифрование на клиенте. Храните приватные ключи отдельно от VDS.
- Защита секретов: файл
/etc/wal-g.d/server.yaml— права600, владелецpostgres. Не пишите секреты в логи и shell history. - Версионирование в бакете + политики жизненного цикла для защиты от случайных удалений и оптимизации затрат.
Распространенные ошибки и их диагностика
- archive_command возвращает код ошибки. Проверьте путь к
wal-gвPATHпользователя postgres, права на конфиг и корректность переменных. - WAL не уезжают, локальный pg_wal растет. Ошибка в доступах к S3, неверный
WALG_S3_PREFIX, сеть или проблемы с DNS. Временно увеличьтеmax_wal_size, чтобы выиграть время. - restore не находит WAL. Проверьте
restore_command, указанный префикс, временной диапазон и наличие нужных сегментов между base backup и целевой точкой. - Слишком долгое восстановление. Увеличьте параллелизм скачивания, используйте более быстрый алгоритм компрессии, чаще делайте base backup, чтобы сократить длину последовательности WAL.
- Несогласованные конфиги. Следите, чтобы
server.yamlбыл одинаковым на узлах, участвующих в бэкапе/восстановлении, и версионируйте изменения.
Практические рекомендации эксплуатации
- Разделяйте префиксы S3 по средам:
s3://your-bucket/pg/prod,.../stg,.../dev. - Ставьте метки на объекты (tags) для отчетности и ретенции.
- Для крупных баз используйте инкрементальную стратегию с более частыми base backup и отладьте окна.
- Фиксируйте версию WAL-G и PostgreSQL в документации, чтобы воспроизводить окружение при DR.
- Периодически тестируйте восстановления на «холодном» каталоге в отдельном VDS.
Чек-лист внедрения
- Создан бакет и префикс в S3-совместимом хранилище, ключи с минимальными правами.
- Установлен
wal-g, настроен/etc/wal-g.d/server.yaml, права 600. - Включены
wal_level=replica,archive_mode=on, заданarchive_command. - Сделан первичный
backup-push, проверенbackup-list. - Работает отправка WAL, наблюдаются новые объекты в S3.
- Настроены таймеры base backup и ретеншн (
delete retain/delete before). - Выполнен тестовый PITR в непроизводственной среде, задокументированы шаги.
Итого
Связка PostgreSQL + WAL-G + S3 дает предсказуемый и масштабируемый пайплайн резервного копирования: быстрые base backup, непрерывное wal archiving и проверяемый PITR. Для эксплуатации на VDS важно уделить внимание компрессии, параллелизму и сетевым лимитам, а также дисциплине тестовых восстановлений. Один раз аккуратно настроив процесс и автоматизировав ретеншн, вы получите устойчивую защиту от потери данных и человеческих ошибок.


