MAATRIX / Блог / pgBackRest на сервере: частые ошибки и решения

pgBackRest на сервере: частые ошибки и решения

MAATRIX

pgBackRest — промышленный инструмент бэкапа PostgreSQL с инкрементальными и дифференциальными копиями, параллельным сжатием и восстановлением на момент времени. На бумаге всё звучит гладко, но в реальной эксплуатации почти каждый, кто внедряет его впервые, натыкается на одни и те же три-четыре ошибки: архивация WAL останавливается, stanza отказывается создаваться, бэкапы конфликтуют друг с другом или репозиторий незаметно съедает весь диск. Ниже — разбор этих ситуаций с конкретными командами и логикой, почему так происходит.

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →

Установка и первая настройка: с чего начинаются проблемы

pgBackRest ставится из репозитория PGDG вместе с самим PostgreSQL:

apt install -y pgbackrest

Базовая конфигурация лежит в /etc/pgbackrest/pgbackrest.conf и состоит из двух частей — глобальной секции и секции конкретной stanza (набора настроек для одного кластера БД):

[global]
repo1-path=/var/lib/pgbackrest
repo1-retention-full=2
process-max=4
log-level-console=info
log-level-file=debug

[main]
pg1-path=/var/lib/postgresql/16/main
pg1-port=5432

Здесь main — произвольное имя stanza, которое должно совпадать в конфиге и во всех командах (pgbackrest --stanza=main ...). Первая частая ошибка новичков — рассинхрон имени stanza между секцией конфига и вызовом команды: получаете ERROR: [029]: unable to find stanza просто потому, что в одном месте написано main, а в другом maindb. Проверяется командой:

pgbackrest --stanza=main check

Она честно скажет, чего не хватает — это первый диагностический инструмент, к которому стоит возвращаться после любой правки конфига.

Ошибка "WAL archiving is required" и настройка archive_command

Самая частая проблема на старте — pgBackRest требует, чтобы PostgreSQL был настроен на архивацию WAL, а без неё ни один полный бэкап не завершится корректно. В postgresql.conf нужно:

archive_mode = on
archive_command = 'pgbackrest --stanza=main archive-push %p'
max_wal_senders = 3
wal_level = replica

После правки — обязательный рестарт (не reload, archive_mode меняется только рестартом):

systemctl restart postgresql

Если после этого pgbackrest --stanza=main check всё равно ругается на WAL archiving is required to be enabled — почти всегда причина в одном из трёх:

  • postgresql.conf, который редактировали, — не тот, что реально подключен (SHOW config_file; в psql покажет актуальный путь);
  • права на каталог /var/lib/pgbackrest не дают пользователю postgres писать туда — проверьте chown postgres:postgres рекурсивно;
  • в archive_command опечатка в имени stanza или пути к бинарнику pgbackrest (полезно указывать абсолютный путь /usr/bin/pgbackrest, если сервер использует нестандартный PATH для systemd-юнита postgres).

Отдельно стоит следить за отставанием архивации при высокой нагрузке на запись. Если pg_stat_archiver показывает растущий failed_count, смотрите лог pgBackRest в /var/log/pgbackrest/main-archive-push.log — там обычно видна конкретная ошибка (нет места, нет прав, репозиторий недоступен). Тема разрастания WAL при проблемах с архивацией разобрана подробнее в статье про рост размера WAL в PostgreSQL — механика там та же: пока архив не подтверждён, PostgreSQL не может удалить старый сегмент.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

"unable to find a valid repository" и проблемы stanza

Ошибка ERROR: [055]: unable to find a valid repository появляется чаще всего в двух сценариях. Первый — stanza ещё не создана. Это разовая операция, которую забывают выполнить перед первым бэкапом:

pgbackrest --stanza=main stanza-create

Второй, более коварный сценарий — репозиторий существует, но его формат или путь изменился после переезда сервера или смены диска, а конфиг ссылается на старый repo1-path. pgBackRest хранит метаданные о версии PostgreSQL и структуре внутри репозитория, и при рассинхроне выдаёт repository is not empty или WAL segment version mismatch. Проверьте, что путь в repo1-path реально указывает на том же диске, где вы ожидаете данные:

pgbackrest --stanza=main info

Команда info — второй по важности диагностический инструмент после check. Она показывает список бэкапов, их тип (full/diff/incr), размер и статус последней архивации. Если вывод пустой при непустой директории репозитория — почти наверняка проблема в правах доступа или в том, что stanza создавалась под другим системным пользователем (обычно всё должно выполняться от имени postgres, а не от root или sudo-пользователя с иным UID).

Если репозиторий действительно повреждён и восстановить его нельзя, безопаснее не чинить руками, а пересоздать с нуля через stanza-create --force — но только после того, как убедились, что старые бэкапы вам не нужны, потому что force перезатирает метаданные:

pgbackrest --stanza=main --force stanza-create

Блокировки и параллельные бэкапы: "backup lock is already held"

pgBackRest защищает от одновременного запуска двух операций бэкапа над одной stanza файловым локом в /tmp/pgbackrest (или в lock-path, если он переопределён). Ошибка ERROR: [050]: unable to acquire lock on file означает, что предыдущий процесс либо ещё выполняется, либо упал, оставив мёртвый лок-файл.

Типичная причина в продакшене — бэкап по cron запускается чаще, чем успевает завершиться предыдущий (например, полный бэкап большой базы занимает 3 часа, а cron стоит на "каждый час"). Решение — не увеличивать частоту крона вслепую, а либо развести полные и инкрементальные бэкапы по расписанию, либо добавить проверку блокировки перед запуском:

#!/bin/bash
if pgrep -f "pgbackrest --stanza=main backup" > /dev/null; then
    echo "Backup already running, skip" | logger -t pgbackrest-cron
    exit 0
fi
pgbackrest --stanza=main --type=incr backup

Пример разумного расписания в crontab пользователя postgres:

0 2 * * 0 pgbackrest --stanza=main --type=full backup
0 2 * * 1-6 pgbackrest --stanza=main --type=incr backup

Если лок остался от процесса, который точно уже мёртв (сервер перезагружался посреди бэкапа), удалите файл вручную из lock-path — по умолчанию это /tmp/pgbackrest/main-backup.lock. Но прежде чем удалять, убедитесь через ps aux | grep pgbackrest, что процесса действительно нет — удаление лока при живом процессе бэкапа приведёт к гонке и повреждённой копии.

Куда девается место: retention и разрастание репозитория

Вторая по частоте проблема после архивации — репозиторий бэкапов незаметно съедает весь диск. pgBackRest хранит full, diff и incr бэкапы, и без настроенной retention-политики они копятся бесконечно. Ключевые параметры:

[global]
repo1-retention-full=2
repo1-retention-full-type=count

Это значит "хранить последние 2 полных бэкапа и всё, что от них зависит (diff/incr)". Частая ошибка — задать repo1-retention-full, но не понимать, что удаление старого full-бэкапа автоматически удаляет и все инкрементальные копии, построенные поверх него — expire идёт по цепочке зависимостей, а не по возрасту отдельного файла. Проверить, что реально будет удалено при следующей ротации, можно через dry-run:

pgbackrest --stanza=main expire --dry-run

Если retention настроен верно, а диск всё равно заполняется — почти всегда виновата компрессия или её отсутствие. По умолчанию pgBackRest использует gz, но zst (Zstandard) даёт заметно лучшее соотношение скорость/размер на современных серверах:

compress-type=zst
compress-level=3
ПараметрЗначение по умолчаниюЧто даёт изменение
compress-typegzzst быстрее сжимает при сравнимом размере
process-max1больше параллельных потоков — быстрее бэкап/сжатие
repo1-retention-full-typecounttime удобнее для политики "храни 14 дней"
repo1-bundleny объединяет мелкие файлы, снижает нагрузку на inode

Отдельно проверяйте место не только под сами бэкапы, но и под временный каталог spool-path, если используется асинхронная архивация (archive-async=y) — очередь недоставленных WAL-сегментов тоже занимает диск и при долгом сбое сети до репозитория (актуально для S3-подобных хранилищ) может забить раздел раньше, чем сам репозиторий.

Ошибки восстановления и PITR

Восстановление — момент, когда цена ошибки в конфиге максимальна, поэтому стоит заранее прогонять его на тестовом сервере, а не только в момент реального инцидента. Базовая команда полного восстановления:

systemctl stop postgresql
rm -rf /var/lib/postgresql/16/main/*
pgbackrest --stanza=main --delta restore
systemctl start postgresql

Флаг --delta заметно экономит время: pgBackRest сверяет контрольные суммы существующих файлов вместо восстановления с нуля, что полезно, если данные повреждены частично, а не полностью.

Восстановление на точку во времени (PITR) — то, ради чего чаще всего и держат pgBackRest вместо обычного pg_dump:

pgbackrest --stanza=main --type=time \
  --target="2026-08-30 14:25:00+03" \
  --delta restore

Частая ошибка здесь — забыть, что после restore PostgreSQL стартует в режиме recovery и не завершит его, пока не найдёт нужные WAL-сегменты в архиве. Если recovery_target указан неверно (например, время раньше самого старого доступного бэкапа) — сервер зависнет в бесконечном ожидании сегмента, которого не существует, и в логе будет FATAL: requested WAL segment ... has already been removed. Проверяйте доступный диапазон восстановления заранее командой pgbackrest --stanza=main info — она показывает временной диапазон каждого бэкапа.

Ещё одна ловушка: если целевая машина отличается от исходной (миграция на новый сервер), не забудьте, что archive_command в восстановленном postgresql.conf всё ещё указывает на ту же stanza — если это тестовый сервер, отключите архивацию до момента, пока не решите, нужен ли ему собственный независимый репозиторий, иначе рискуете перезаписать боевые бэкапы тестовыми WAL-сегментами. Общие принципы отработки восстановления из копии, включая проверку целостности перед переключением трафика, разобраны в статье про восстановление базы данных из бэкапа на практике.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Нужны сами нейросети для контента?

Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.

Частые вопросы

pgBackRest лучше, чем связка pg_dump + cron?

Для баз меньше нескольких гигабайт разница некритична, но pgBackRest даёт инкрементальные копии, параллелизм и честный PITR, чего pg_dump в принципе не умеет — он снимает логический слепок, а не бинарный уровень с WAL.

Можно ли хранить репозиторий на S3-совместимом хранилище вместо локального диска?

Да, через repo1-type=s3 с указанием repo1-s3-bucket, repo1-s3-endpoint и ключей доступа — это снимает вопрос локального места, но добавляет зависимость от сетевой доступности хранилища во время восстановления, что стоит учитывать при расчёте RTO.

Как понять, что бэкап реально рабочий, а не просто "завершился без ошибок"?

Периодически прогоняйте restore на отдельный тестовый сервер — это единственный надёжный способ. Статус "success" в pgbackrest info подтверждает только то, что процесс дошёл до конца, а не то, что данные восстановятся консистентно.

Что делать, если archive-push не успевает за темпом записи?

Включите archive-async=y и увеличьте process-max — асинхронная архивация с пулом воркеров разгружает основной поток WAL и снижает риск накопления неотправленных сегментов.

Нужен ли отдельный диск под репозиторий бэкапов?

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

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →