Утилита резервирования pgBackRest перестала поддерживаться, стало актуальным найти ей замену. Главными альтернативами называют WAL-G и Barman. Можно использовать стандартные pg_basebackup+pg_receivewal. Преимущество WAL-G в том, что резервирование возможно по протоколу S3, WAL-G обеспечивает более высокую скорость резервирования и сжатия, имеет неплохие перспективы развития. Кроме протокола S3, WAL-G может резервировать и восстанавливать из директории в файловой системе, работает с Patroni. Директория не обязательно должна находиться на локальном диске, можно смонтировать любую файловую систему, например, NFS. Утилита свободно распространяемая.

В статье рассматриваются примеры команд, которыми можно резервировать и восстанавливать PostgreSQL утилитой WAL-G с защитой от потери транзакций (zero data loss).

WAL-G можно собрать из исходников или установить из пакета. Инсталляционный пакет содержит единственный файл wal-g и устанавливается аккуратно - не копирует себя в стандартные директории linux. Чтобы не писать каждый раз путь к утилите, можно её скопировать или создать символическую ссылку в какой-нибудь директории, которая есть в переменной окружения $PATH:

root@host:~$ dpkg -i wal-g-*.deb
root@host:~$ ln -s /opt/tantor/usr/bin/wal-g /usr/local/bin/wal-g

Настройки утилиты хранятся в одном файле. По умолчанию, местоположение файла параметров $HOME/.walg.json Можно иметь несколько файлов с разными настройками и указывать нужный файл параметром --config.

Для бэкапа в директорию файловой системы достаточно создать файл:

root@host:~$ su - postgres
postgres@host:~$ cat > $HOME/.walg.json << EOF
{
    "WALG_FILE_PREFIX": "/var/lib/postgresql/backup",
    "WALG_COMPRESSION_METHOD": "brotli",
    "WALG_DELTA_MAX_STEPS": "5",
    "PGHOST": "/var/run/postgresql",
    "PGDATA": "/var/lib/postgresql/tantor-se-server-18/data"
}
EOF

Резервирование будет выполняться в директорию, на которую указывает WALG_FILE_PREFIX. Алгоритм brotli обеспечивает лучшее и быстрое сжатие (лучше чем LZ4, быстрее чем LZMA). Благодаря brotli и многопоточности, WAL-G чрезвычайно быстро резервирует.

Скрытый текст

Для работы не с директорией, а по протоколу S3 достаточно поменять параметры в файле $HOME/.walg.json, команды резервирования и восстановления остаются теми же самыми. Или создать ещё один файл и добавить параметр --config к параметрам утилиты WAL-G.

Пример файла конфигурации для протокола S3:

postgres@host:~$ cat > .walg.json << EOF
{
   "AWS_ENDPOINT": "http://127.0.0.1:9000",
   "WALG_S3_PREFIX": "s3://bucket1",
   "AWS_ACCESS_KEY_ID": "rustfsadmin",
   "AWS_SECRET_ACCESS_KEY": "rustfsadmin",
   "AWS_S3_FORCE_PATH_STYLE": "true",
   "WALG_COMPRESSION_METHOD": "brotli",
   "WALG_DELTA_MAX_STEPS": "5",
   "PGDATA": "/var/lib/postgresql/tantor-se-18/data",
   "PGHOST": "/var/run/postgresql"
}
EOF

Пример установки S3-сервера, чтобы попробовать работу WAL-G по протоколу S3:

root@host:~$ curl -O https://rustfs.com/install_rustfs.sh && bash install_rustfs.sh
Service port: 9000,  Console port: 9001,  Data directory: /data/rustfs0
root@host:~$ mkdir /data/rustfs0/bucket1

У S3-сервера RUSTFS есть веб-консоль управления http://localhost:9001 (rustfsadmin/rustfsadmin)

Приведённые в статье примеры можно попробовать на виртуальной машине Tantor DBA1-18.ova

Пример для Yandex Cloud и Patroni описаны в базе знаний.

Для проверки того, что wal-g может работать с настройками в файле параметров, можно выполнить команду:

postgres@host:~$ wal-g backup-list
INFO: 2026/04/28 10:33:54.281318 List backups from storages: [default]
INFO: 2026/04/28 10:33:54.281702 No backups found

Команда не выдала ошибки, значит, файл параметров корректный. Бэкапов нет, так как мы их ещё не делали.

Конфигурирование PostgreSQL для архивирования WAL

Чтобы с бэкапа можно было полностью восстановиться, нужно сохранять все WAL-файлы с начала бэкапа до самого последнего, в который писали процессы экземпляра в момент падения экземпляра. WAL файлы можно вытягивать по протоколу репликации или передавать командой, указанной в параметре конфигурации archive_command. Самый последний WAL-файл, в который ещё пишут процессы экземпляра, команда не может передать. Для передачи WAL-файлов утилитой WAL-G в архив достаточно установить параметры:

postgres=# 
alter system set archive_command = 'wal-g wal-push "%p" >> $PGDATA/log/archive_command.log 2>&1';
alter system set restore_command = 'wal-g wal-fetch "%f" "%p" >> $PGDATA/log/restore_command.log 2>&1';
alter system set archive_mode=on;

Параметр archive_command задаёт команду, которая будет выполняться после переключения на следующий WAL-файл («сегмент»). %p - переменная, которая инициализируется названием и путём к WAL-файлу, в который завершена запись и который должен быть передан в архив.

Параметр restore_command указывает, какая команда будет выполнена процессом startup, который восстанавливает кластер после запуска экземпляра и определяет по файлу backup_label или pg_control, какой WAL-файл нужен для продолжения восстановления (будет накатываться следующим). Эта команда должна создать WAL-файл в директории $PGDATA/pg_wal.

Параметр archive_mode включает действие параметра archive_command.

WAL-G и pg_receivewal

WAL-G можно совместить с pg_receivewal, получив преимущества высокоскоростного создания сжатых бэкапов, которые делает WAL-G и отсутствие потерь транзакций (zero data loss), которое обеспечивает pg_receivewal. При использовании pg_receivewal нужно задать в параметре restore_command команду копирования из директории, куда утилита pg_receivewal вытягивала журналы. Пример команды запуска pg_receivewal:

postgres@host:~$ rm -rf $HOME/archivelog
mkdir $HOME/archivelog
psql -c "alter system set max_slot_wal_keep_size = '1TB';"
psql -c "select pg_reload_conf();"
pg_receivewal --drop-slot --slot=arch
pg_receivewal --create-slot --slot=arch
pg_receivewal -D $HOME/archivelog --slot=arch --synchronous

Утилиту pg_receivewal можно запускать как службу через systemd (так же как и экземпляр PostgreSQL).

Скрытый текст

Файл службы не поставляется. Пример команд, которые автоматизируют запуск pg_receivewal как службы:

postgres@host:~$ mkdir $HOME/archivelog
touch $HOME/archivelog/pg_receivewal.log
sudo chown postgres:postgres /var/lib/postgresql/archivelog/pg_receivewal.log
sudo chmod 660 /var/lib/postgresql/archivelog/pg_receivewal.log

cat > $HOME/pg_receivewal.service << EOF
[Unit]
# имя для systemctl service status
Description=postgres pg_receivewal
# запустится после того, как поднимется сеть
After=network.target
[Service]
# работает в foreground
Type=simple
# переменная окружения
Environment=PGDATA=/var/lib/postgresql/tantor-se-18/data
# директория запуска
WorkingDirectory=/var/lib/postgresql/archivelog
# до запуска выполнить команду
ExecStartPre=/opt/tantor/db/18/bin/pg_receivewal --create-slot --if-not-exists --slot=arch
# команда запуска
ExecStart=/opt/tantor/db/18/bin/pg_receivewal -D /var/lib/postgresql/archivelog --slot=arch --synchronous -w -v
Restart=on-failure
RestartSec=10s
User=postgres
Group=postgres
# для создаваемых процессом файлов, umask это инвертированный chmod
UMask=047
#вывод stdout в файл
StandardOutput=append:/var/lib/postgresql/archivelog/pg_receivewal.log
#вывод stderr в файл
StandardError=inherit
[Install]
# чтобы не запускалось в single mode
WantedBy=multi-user.target
EOF

sudo cp $HOME/pg_receivewal.service /usr/lib/systemd/system/pg_receivewal.service
sudo chmod 644 /usr/lib/systemd/system/pg_receivewal.service
sudo systemctl daemon-reload
sudo systemctl enable pg_receivewal
sudo systemctl start pg_receivewal
sudo systemctl status pg_receivewal

Если нужна гарантия отсутствия потерь транзакций, то нужно задать параметр конфигурации synchronous_standby_names=pg_receivewal, тогда транзакции будут подтверждаться только после того, как pg_receivewal получит запись о фиксации транзакции (и все предыдущие журнальные записи) и сохранит её в файле .partial. Однако, команды фиксации транзакций могут выполняться с большей задержкой.

Малоизвестно, но это важно: чтобы файл *.partial применялся при запуске экземпляра, нужно использовать следующее значение для параметра конфигурации restore_command:

alter system set restore_command = 'wal-g wal-fetch "%f" "%p" >> $PGDATA/log/restore_command.log 2>&1 || cp $HOME/archivelog/%f %p || cp $HOME/archivelog/%f.partial %p';

Файлы с расширением .partial не применяются при восстановлении. Без применения этого файла последние транзакции будут потеряны. Чтобы файл .partial применился, важно убрать расширение у файла, что учтено в вышеприведённой команде. Первая часть команды восстанавливает журнал, заархивированный WAL-G. Если в архиве его нет, то журнал копируется из архива pg_receivewal. Если такого файла нет, то копируется файл .partial (в котором и находятся самые последние изменения) и убирается расширение у файла.

Стоит протестировать, применяется ли (накатывается) в ваших скриптах и утилитах резервирования/восстановления файл .partial. В документации к Barman о файле .partial, хоть и излишне сложно, но написано (причина написания описана здесь). Более просто про файл .partial написал Лоренц Альбе. При использовании утилиты WAL-G, копирование и переименование файла .partial можно установить в restore_command заранее, что минимизирует ошибки во время восстановления.

Скрытый текст

На языке Go написан аналог pg_receivewal, который называется pgrwl. Он работает с S3 и файловой системой, но с файлом .partial работает точно так же, как pg_receivewal. Так как pg_receivewal входит в стандартную поставку и поддерживается сообществом, то имеет смысл использовать pg_receivewal.

То, что в документации к Barman файл .partial описан, это плюс Barman. В pgBackRest пишут: "Насколько мне известно, в документации PostgreSQL нет информации о файлах с расширением .partial. ... хотя могут быть случаи, когда он вам понадобится. Впрочем, если вы ищете данные, лучше использовать исходный WAL-файл с мастера, если он доступен. Это не значит, что файл .partial бесполезен, просто в тех немногих случаях, когда он появляется, по нашему опыту, есть более удачное решение, чем его использование." Nuff said.

Push или Pull

Альтернатива архивированию с помощью параметров конфигурации archive_* (режим "push", где инициатором передачи файлов является резервируемый хост) - запустить wal-g как службу, чтобы она получала ("pull", вытягивала) файлы журналов:

postgres@host:~$ wal-g wal-receive
INFO: 2026/04/28 21:44:41.565679 FILE PATH: 00000002.history.

При использовании этого способа архивирования WAL, нужно установить только параметр restore_command, параметр archive_mode нужно вернуть к значению по умолчанию (off). Поскольку, даже в этом режиме, WAL-G принимает только сформированные WAL-файлы (запись в которые завершена), то с WAL-G проще использовать archive_command, а не режим “pull”. Единственно, надо проследить за тем, чтобы с резервируемого хоста нельзя было стирать или менять файлы в директории, куда резервируются файлы, иначе злоумышленник, завладевший резервируемым хостом, сможет стереть или испортить бэкапы.

WAL-G может не только вытягивать журналы, но и вытягивать файлы PGDATA (бэкапить кластер PostgreSQL в режиме “pull”) по репликационному протоколу. Эта возможность уже готова и будет включена в следующую версию WAL-G. При использовании репликационного протокола пока нет многопоточной передачи и дельта-бэкапов и бэкап кластера будет создаваться дольше. Это касается только создания бэкапов в режие «pull», вытягивание WAL не имеет таких ограничений. Также, ни в режиме «push», ни в режиме «pull» WAL-G не принимает текущий WAL-файл и при полной потере директории PGDATA/pg_wal нельзя будет восстановить транзакции из текущего WAL-файла. Это не является проблемой, так как WAL-G может архивировать и восстанавливать журналы совместно с утилитой pg_receivewal, которая способна вытягивать журнальные записи из текущего WAL-файла и сохранять их в файл .partial.

Отсутствие потерь (zero data loss, RPO=0) способны обеспечить: утилита pg_receivewal, pgrwl, процесс walreceiver (на физических репликах), Polar DataMax (в Tantor XData).

Создание бэкапа утилитой WAL-G

Чтобы изменения подействовали, нужно остановить и запустить (перезапустить недостаточно) экземпляр.

Для проверки того, что мы правильно настроили архивирование журналов, переключимся на новый WAL-файл и убедимся, что archive_command выполнена:

postgres@host:~$ psql -c "select pg_switch_wal();"
 pg_switch_wal 
---------------
 0/30002BC
(1 row)
postgres@host:~$ cat $PGDATA/log/archive_command.log 
INFO: 2026/04/28 11:31:34.145668 Files will be uploaded to storage: default
INFO: 2026/04/28 11:31:34.192862 FILE PATH: 000000010000000000000003.br

В файле archive_command.log, путь к которому был указан в параметре archive_command, сохраняются сообщения утилиты WAL-G.

Зарезервируем директорию кластера. Первый бэкап всегда полный, от него отсчитываются «дельты» (дифференциальные инкрементальные и кумулятивные инкрементальные бэкапы, и те и те WAL-G делает) В команде создания бэкапа нужно передать название директории кластера $PGDATA:

postgres@host:~$ wal-g backup-push $PGDATA
INFO: 2026/04/28 11:40:37.903727 Backup will be pushed to storage: default
INFO: 2026/04/28 11:40:37.912409 Couldn't find previous backup. Doing full backup.
...
INFO: 2026/04/28 11:40:38.357616 Wrote backup with name base_000000010000000000000005 to storage default

Проверка, что бэкап появился:

postgres@host:~$ wal-g backup-list
INFO: 2026/04/28 11:47:02.111831 List backups from storages: [default]
backup_name                   modified                  wal_file_name            storage_name
base_000000010000000000000005 2026-04-28T11:40:38+03:00 000000010000000000000005 default

В директории для бэкапов (WALG_FILE_PREFIX) появятся файлы, которые и являются бэкапом кластера баз данных:

postgres@host:~$ ls -al /var/lib/postgresql/backup/basebackups_005/*/tar_partitions
total 2760
-rw-r--r-- 1 postgres postgres     269 Apr 28 11:40 backup_label.tar.br
-rw-r--r-- 1 postgres postgres 2806055 Apr 28 11:40 part_001.tar.br
-rw-r--r-- 1 postgres postgres     300 Apr 28 11:40 pg_control.tar.br

Следующие WALG_DELTA_MAX_STEPS бэкапов будут сделаны (если не переопределить в команде) в режиме DELTA:

postgres@host:~$ wal-g backup-push $PGDATA
INFO: 2026/04/28 11:59:41.614186 Backup will be pushed to storage: default
INFO: 2026/04/28 11:59:41.628244 LATEST backup is: 'base_000000010000000000000005'
INFO: 2026/04/28 11:59:41.629001 Delta backup from base_000000010000000000000005 with LSN 0/5000028.
...
INFO: 2026/04/28 11:59:41.833147 Wrote backup with name base_000000010000000000000007_D_000000010000000000000005 to storage default

Полезна команда удаления мусора в архиве - неудачные бэкапы, ненужные журнальные файлы:

postgres@host:~$ wal-g delete garbage --confirm
INFO: 2026/04/28 20:49:20.660407 Backup to delete will be searched in storages: [default]
INFO: 2026/04/28 20:49:20.660540 retrieving permanent objects
INFO: 2026/04/28 20:49:20.661749 Running in default mode. Will remove outdated WAL files and leftover backup files.
INFO: 2026/04/28 20:49:20.662257 Start delete
INFO: 2026/04/28 20:49:20.663400 Objects in folder:
INFO: 2026/04/28 20:49:20.663440        will be deleted: wal_005/00000002000000000000002A.br, from storage: default

Если WAL-файлы вытягиваются утилитой pg_receivewal, то после создания бэкапа изменится файл $PGDATA/pg_wal/*.backup. После архивирования командой в параметре archive_command создаются файлы $PGDATA/pg_wal/archive_status/*000000??.done. Имена этих файлов можно использовать для удаления WAL-файлов, которые вытягивает pg_receivewal. Пример команды удаления файлов, созданных pg_receivewal, если они уже заархивированы:

pg_archivecleanup $HOME/archivelog $(basename $(ls $PGDATA/pg_wal/archive_status/*000000??.done | sort -r | head -n 1) .done)
Скрытый текст

Для примера, команда для удаления файлов, которые не нужны последнему бэкапу:

pg_archivecleanup $HOME/archivelog $(basename $(ls $PGDATA/pg_wal/*.backup))

Команду pg_archivecleanup можно запускать по расписанию с желаемой частотой, которую выбрать исходя из объема генерируемых WAL и свободного места в директории (в примере $HOME/archivelog).

Восстановление из бэкапа, созданного WAL-G

Остановим экземпляр и удалим директорию PGDATA:

postgres@host:~$ pg_ctl stop
waiting for server to shut down.... done
server stopped
postgres@host:~$ rm -rf $PGDATA/*

Команда симулирует полную потерю кластера баз данных («disaster»). Команда удаляет, в том числе и текущий WAL-файл, который не был записан в архив утилитой WAL-G, но если работала утилита pg_receivewal, то она сохранила записи текущего WAL-файла в файле .partial. Директория, в которую будет выполняться восстановление из бэкапа должна быть пустой.

Команда восстановления директории кластера из бэкапа:

postgres@host:~$ wal-g backup-fetch $PGDATA LATEST
INFO: 2026/04/28 12:56:37.402035 Selecting the latest backup...
INFO: 2026/04/28 12:56:37.402219 Backup to fetch will be searched in storages: [default]
INFO: 2026/04/28 12:56:37.402360 LATEST backup is: 'base_000000010000000000000007_D_000000010000000000000005'
INFO: 2026/04/28 12:56:37.406190 Delta from base_000000010000000000000005 at LSN 0/5000028 
...
INFO: 2026/04/28 12:56:38.500783 base_000000010000000000000005 fetched. Upgrading from LSN 0/5000028 to LSN 0/7000028 
...
Backup extraction complete.

Директория PGDATA восстановлена. Проверим содержимое директории журналов:

postgres@host:~$ ls $PGDATA/pg_wal

Директория пуста, как и директория с диагностическими файлами PGDATA/log, что нормально, файлы WAL восстанавливаются отдельно командой, указанной в параметре конфигурации restore_command.

Вместо управляющего файла будет использоваться файл backup_label:

postgres@host:~$ cat $PGDATA/backup_label 
START WAL LOCATION: 0/7000028 (file 000000010000000000000007)
CHECKPOINT LOCATION: 0/7000098
BACKUP METHOD: streamed
BACKUP FROM: primary
START TIME: 2026-04-28 11:59:41 MSK
LABEL: 2026-04-28 11:59:41.640728 +0300 MSK m=+0.075156967
START TIMELINE: 1

В файле указан WAL-файл, с которого должен начаться накат журналов и адрес журнальной записи о завершении контрольной точки (CHECKPOINT LOCATION), после наката этой журнальной записи, восстанавливаемая директория PGDATA станет согласованной и может открываться экземпляром.

Для копирования из архива журналов и их наката нужно создать файл recovery.signal, который укажет, что нужно выполнять команды из параметра конфигурации restore_command и накатывать полученные журнальные файлы. После создания файла запустить экземпляр:

postgres@host:~$ touch $PGDATA/recovery.signal
postgres@host:~$ pg_ctl start
 done
server started

Процесс экземпляра startup будет выполнять команду, указанную в параметре конфигурации restore_command и применять WAL-файлы, которые эта команда скопирует в директорию PGDATA/pg_wal. Экземпляр будет переведён в состяние, в соответствии со значениями параметров конфигурации (recovery_target_*), которыми можно указать, до какого момента выполнять восстановление и что делать после достижения этого момента. Мы не устанавливаем параметры recovery_target_*, так как, по умолчанию, накатываются все WAL-файлы, которые сможет найти команда в restore_command, после чего кластер открывается в режиме чтения-записи.

Первая часть restore_command (wal-g wal-fetch “%f” “%p”) вынет из архива WAL-G и накатит журналы вплоть до текущего WAL-файла. Если запускался pg_receivewal, то последняя часть restore_command скопирует и накатит файлы, которые не смог скопировать WAL-G (например, из-за временной недоступности S3 хранилища) и файл .partial, который содержит журнальные записи из текущего WAL-файла. Журнальные файлы, которые не попали в архивы wal-g и pg_receivewal, не применятся, и транзакции, которые могли бы в них быть, потеряются. Вероятность того, что две утилиты не смогут или не успеют заархивировать WAL файлы мала. Для полной защиты от такой ситуации используется параметр synchronous_standby_names.

После восстановления слоты репликации удалены и, если они используются, то их надо снова создать. Если использовалась утилита pg_receivewal (которую мы не останавливали и она продолжит вытягивать WAL уже с восстановленного кластера), то после восстановления для неё нужно снова создать слот репликации:

postgres@host:~$ pg_receivewal --create-slot --slot=arch

Сравнение WAL-G с Barman есть в комментариях к статье и самой статье.

Заключение

После прекращения развития утилиты pgBackRest, стал актуальным выбор перспективных утилит резервирования. Там, где нужна простота, высокая скорость резервирования и сжатия, WAL‑G неплохой выбор. В статье описано как использовать WAL-G для резервирования и восстанновления кластера PostgreSQL, а также на что обратить внимание, чтобы не было потерь транзакций при восстановлении (zero data loss).