Логический бэкап PostgreSQL через pg_dump — один из самых удобных способов перенести базу между серверами, подготовить копию для staging-окружения, проверить обновление приложения или быстро откатиться после неудачной миграции схемы. На VDS это особенно удобно: у вас есть root-доступ, можно контролировать I/O, ставить нужную версию клиентских утилит и запускать восстановление в несколько потоков.
В этой статье разберём практический сценарий: как сделать дамп в custom format, почему это не то же самое, что обычный SQL-файл, как проверить архив до аварии, как восстановить его через pg_restore с --jobs и где обычно ломаются права, расширения, владельцы объектов и производительность.
Короткая идея:
pg_dump -F cсоздаёт гибкий архив, аpg_restore -jумеет восстанавливать его параллельно. Это хороший формат для миграций и регулярных логических бэкапов небольших и средних PostgreSQL-баз на VDS.
Когда выбирать custom format, а когда нет
У pg_dump есть несколько форматов. Самый привычный — plain SQL, когда результат можно открыть в редакторе и выполнить через psql. Он прост, но плохо подходит для выборочного и параллельного восстановления: вы получаете линейный скрипт, который выполняется сверху вниз.
custom format, включаемый опцией -F c или --format=custom, хранит данные в архивном формате PostgreSQL. Такой файл обычно имеет расширение .dump или .backup. Его нельзя просто выполнить через psql; для восстановления используется pg_restore. Зато можно посмотреть содержимое архива, восстановить отдельные схемы или таблицы, исключить владельцев и ACL, а главное — запустить parallel restore.
Есть важная тонкость: custom format поддерживает параллельное восстановление, но не поддерживает параллельное создание дампа. Если нужна именно параллельная выгрузка, используйте directory format: pg_dump -F d -j 4. Для большинства типовых миграций на VDS custom format остаётся удобным компромиссом: один файл проще хранить, копировать, проверять и передавать между серверами.
Не стоит путать логический дамп с физическим бэкапом. pg_dump сохраняет структуру и данные на уровне SQL-объектов, но не является заменой PITR, base backup и архивации WAL. Если нужно восстановление на конкретную секунду после аварии, посмотрите отдельный разбор про PITR и WAL-бэкапы PostgreSQL. Логический дамп, в свою очередь, отлично подходит для переносов, тестовых окружений, смены версии PostgreSQL в разумных пределах и восстановления отдельных объектов.
Подготовка на VDS: версия клиента, место на диске и нагрузка
Первое правило PostgreSQL-бэкапов: версия pg_dump должна быть не старше версии сервера PostgreSQL, с которого вы снимаете дамп. Например, клиент PostgreSQL 16 обычно может снять дамп с сервера 15, но клиент 14 не стоит использовать против сервера 16. При миграции на новый VDS удобно ставить клиентские утилиты той же мажорной версии, что и исходная база, либо новее.
Проверьте версии:
psql --version
pg_dump --version
pg_restore --version
Для установки клиентских утилит используйте пакет PostgreSQL из репозитория вашей ОС или официальный репозиторий PostgreSQL, если нужна конкретная мажорная версия. Ниже — базовые команды из стандартных репозиториев.
Debian и Ubuntu
sudo apt update
sudo apt install postgresql-client
AlmaLinux, Rocky Linux, CentOS Stream, Oracle Linux и Fedora
sudo dnf install postgresql
Перед дампом оцените размер базы и свободное место. Архив custom format с компрессией часто меньше самой базы, но это не гарантия: многое зависит от типов данных, индексов, TOAST-данных и уже сжатого содержимого. Для восстановления нужно место не только под дамп, но и под новую базу, временные файлы, индексы и WAL.
psql -U postgres -d appdb -c 'select pg_size_pretty(pg_database_size(current_database()));'
df -h
free -h
На небольшом VDS не запускайте тяжёлый дамп в час пик без ограничения приоритета. pg_dump читает много данных и может конкурировать с рабочим приложением за диск и CPU. Для мягкого запуска пригодятся nice и ionice:
nice -n 10 ionice -c2 -n7 pg_dump -h 127.0.0.1 -U app_backup -d appdb -F c -Z 6 -f /backup/appdb_$(date +%F_%H%M).dump
Параметр -Z задаёт уровень сжатия. Чем выше уровень, тем меньше файл, но тем больше CPU. На VDS с ограниченным числом vCPU обычно разумно начинать с -Z 4 или -Z 6, а не сразу с максимума.

Пользователь для бэкапа: меньше прав, меньше сюрпризов
Технически дамп можно снять от имени суперпользователя PostgreSQL, но для регулярных задач лучше завести отдельную роль. Она должна уметь подключаться к базе и читать нужные схемы, таблицы и последовательности. Если в базе много схем, выдавайте права осознанно, а не только на public.
CREATE ROLE app_backup WITH LOGIN PASSWORD 'replace_with_long_password';
GRANT CONNECT ON DATABASE appdb TO app_backup;
GRANT USAGE ON SCHEMA public TO app_backup;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO app_backup;
GRANT SELECT ON ALL SEQUENCES IN SCHEMA public TO app_backup;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO app_backup;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON SEQUENCES TO app_backup;
Если дамп должен включать расширения, функции, типы, схемы и все объекты без ошибок доступа, иногда проще выполнять его владельцем базы или ролью с более широкими правами. Но для автоматического ночного бэкапа лучше начать с минимальной роли и один раз проверить журнал дампа. Ошибка прав не всегда означает полный провал, но в бэкапе могут отсутствовать важные объекты.
Пароль не стоит передавать прямо в командной строке: он попадёт в историю shell или список процессов. Используйте файл .pgpass. На Debian/Ubuntu домашний каталог системного пользователя PostgreSQL обычно /var/lib/postgresql, на RHEL-based системах — /var/lib/pgsql. Для отдельного пользователя, от которого запускается бэкап, путь будет его домашним каталогом.
install -m 600 /dev/null ~/.pgpass
printf '127.0.0.1:5432:appdb:app_backup:replace_with_long_password' > ~/.pgpass
chmod 600 ~/.pgpass
Создаём дамп PostgreSQL в custom format
Базовая команда выглядит так:
pg_dump -h 127.0.0.1 -p 5432 -U app_backup -d appdb -F c -Z 6 -f /backup/appdb_$(date +%F_%H%M).dump
Разберём важные параметры. -h и -p задают адрес и порт сервера. -U — роль подключения. -d — база. -F c включает custom format. -Z задаёт компрессию. -f указывает файл архива.
Если база большая, добавьте подробный вывод:
pg_dump -h 127.0.0.1 -U app_backup -d appdb -F c -Z 6 --verbose -f /backup/appdb_$(date +%F_%H%M).dump
Для миграции между серверами часто полезно не включать владельцев и права доступа в восстановление, но сами эти параметры задаются на этапе pg_restore, а не обязательно на этапе pg_dump. Это удобно: один и тот же архив можно восстановить в production-like окружение с владельцами или в staging без них.
Если нужно выгрузить только одну схему:
pg_dump -h 127.0.0.1 -U app_backup -d appdb -n public -F c -Z 6 -f /backup/appdb_public.dump
Если нужно исключить тяжёлую таблицу с временными или аналитическими данными:
pg_dump -h 127.0.0.1 -U app_backup -d appdb -F c -Z 6 --exclude-table-data=public.event_log -f /backup/appdb_without_event_log.dump
Опция --exclude-table-data исключает только данные, но оставляет структуру таблицы. Это полезно для staging: приложение видит таблицу, миграции проходят, но восстановление не тратит часы на исторический лог.
Проверяем архив до того, как он понадобится
Бэкап без проверки — это предположение, а не защита. Минимальная проверка custom-архива — прочитать его оглавление через pg_restore --list. Если архив повреждён или создан несовместимой версией, вы узнаете об этом сразу.
pg_restore --list /backup/appdb_2026-01-15_0300.dump
Список можно сохранить в файл. Потом его удобно использовать для выборочного восстановления или исключения объектов:
pg_restore --list --file=/backup/appdb_restore.list /backup/appdb_2026-01-15_0300.dump
Ещё лучше — регулярно выполнять тестовое восстановление в отдельную базу. Это не обязательно делать каждую ночь на маленьком VDS, но после изменения схемы, обновления PostgreSQL, установки новых расширений и перед крупной миграцией такая проверка окупается.
createdb -h 127.0.0.1 -U postgres appdb_restore_test
pg_restore -h 127.0.0.1 -U postgres -d appdb_restore_test --verbose /backup/appdb_2026-01-15_0300.dump
psql -h 127.0.0.1 -U postgres -d appdb_restore_test -c 'select count(*) from public.users;'
dropdb -h 127.0.0.1 -U postgres appdb_restore_test
Для автоматической проверки можно восстанавливать дамп на отдельном VDS или в отдельном PostgreSQL-кластере. Важно не запускать тестовое восстановление в рабочую базу и не использовать имя production-базы в скриптах проверки.
Восстановление через pg_restore: безопасный базовый сценарий
Самый спокойный способ восстановления — создать новую пустую базу и развернуть архив туда. Так вы не рискуете случайно удалить рабочие таблицы. После проверки можно переключить приложение, переименовать базы или выполнить миграцию данных.
createdb -h 127.0.0.1 -U postgres -O app_owner appdb_new
pg_restore -h 127.0.0.1 -U postgres -d appdb_new --verbose /backup/appdb.dump
Если на новом сервере нет тех же ролей, что были на старом, восстановление может ругаться на владельцев и привилегии. Для миграции между VDS часто используют --no-owner и --no-acl, а владельца задают через --role или через владельца базы.
createdb -h 127.0.0.1 -U postgres -O app_owner appdb_new
pg_restore -h 127.0.0.1 -U postgres -d appdb_new --no-owner --no-acl --role=app_owner --verbose /backup/appdb.dump
Если нужно восстановить поверх существующей базы, используйте --clean и --if-exists. Будьте осторожны: команда удаляет объекты, которые есть в архиве, перед повторным созданием.
pg_restore -h 127.0.0.1 -U postgres -d appdb --clean --if-exists --no-owner --no-acl --verbose /backup/appdb.dump
Для production-восстановления сначала прогоните команду на тестовой базе с тем же архивом. Большинство неприятных ошибок — роли, расширения, collation, права на схемы — лучше увидеть до окна работ.
Parallel restore: как ускорить восстановление custom format
Главная причина выбирать custom format для крупных логических бэкапов — pg_restore --jobs. Параллельное восстановление позволяет одновременно загружать данные, создавать индексы и выполнять независимые части архива. На VDS это может дать заметный выигрыш, особенно если база содержит много таблиц и индексов.
createdb -h 127.0.0.1 -U postgres -O app_owner appdb_new
pg_restore -h 127.0.0.1 -U postgres -d appdb_new --jobs=4 --no-owner --no-acl --role=app_owner --verbose /backup/appdb.dump
Число потоков не должно быть «чем больше, тем лучше». Начните с количества vCPU или чуть меньше. Для VDS с 2 vCPU обычно разумно --jobs=2. Для 4 vCPU — --jobs=3 или --jobs=4. Если диск начинает упираться в I/O wait, увеличение потоков только ухудшит ситуацию.
Параллельное восстановление потребляет больше соединений к PostgreSQL. Проверьте max_connections и не запускайте восстановление рядом с нагруженным приложением в той же базе. Также учитывайте память: при одновременном создании индексов может умножаться потребление maintenance_work_mem. Если задать слишком большое значение и включить много jobs, сервер уйдёт в swap или получит OOM.
На время восстановления в новую базу можно аккуратно увеличить параметры, которые влияют на создание индексов и запись WAL. Делайте это осознанно и возвращайте значения после работ:
psql -U postgres -d postgres -c 'alter system set maintenance_work_mem = ''512MB'';'
psql -U postgres -d postgres -c 'alter system set checkpoint_timeout = ''15min'';'
psql -U postgres -d postgres -c 'alter system set max_wal_size = ''4GB'';'
psql -U postgres -d postgres -c 'select pg_reload_conf();'
Если VDS небольшой, лучше не ставить агрессивные значения. Например, при 2 ГБ RAM maintenance_work_mem в 512 МБ и --jobs=4 уже может быть рискованной комбинацией. Смотрите на реальное потребление через htop, vmstat, iostat и логи PostgreSQL.
Нельзя одновременно рассчитывать на все гарантии сразу. Например, --single-transaction полезен для атомарного восстановления, но плохо сочетается с параллельным режимом: для ускорения обычно выбирают --jobs, а для максимально строгого единичного применения — транзакционный сценарий без параллелизма.

Восстановление отдельных таблиц, схем и объектов
Custom format удобен тем, что не заставляет восстанавливать всё сразу. Можно достать одну таблицу, схему или данные без полного разворота базы. Например, восстановим только таблицу public.orders в уже подготовленную базу:
pg_restore -h 127.0.0.1 -U postgres -d appdb_recovery --table=public.orders --data-only --verbose /backup/appdb.dump
Для восстановления схемы:
pg_restore -h 127.0.0.1 -U postgres -d appdb_recovery --schema=public --verbose /backup/appdb.dump
Более гибкий способ — отредактировать список объектов. Сначала создайте list-файл, затем закомментируйте ненужные строки и восстановите только оставшееся:
pg_restore --list --file=/backup/appdb_restore.list /backup/appdb.dump
pg_restore -h 127.0.0.1 -U postgres -d appdb_recovery --use-list=/backup/appdb_restore.list --verbose /backup/appdb.dump
Этот подход особенно полезен, когда нужно вернуть одну случайно удалённую таблицу, но у неё есть зависимости: последовательности, индексы, constraints. В list-файле видно порядок объектов, и можно точнее управлять восстановлением.
Миграция базы между двумя VDS
Для переноса между серверами можно сначала создать дамп на старом VDS, затем скопировать его на новый. Перед копированием проверьте контрольную сумму, чтобы исключить повреждение файла в пути.
sha256sum /backup/appdb.dump
scp /backup/appdb.dump admin@new-vds:/backup/appdb.dump
На новом сервере снова проверьте сумму:
sha256sum /backup/appdb.dump
Если окно простоя должно быть коротким, логический дамп большого production может оказаться недостаточным: пока вы снимаете дамп, данные продолжают меняться. Для небольших проектов обычно делают так: заранее переносят файлы приложения, настраивают PostgreSQL, DNS, веб-сервер и SSL, затем в окно работ переводят приложение в read-only или maintenance-режим, снимают финальный дамп, восстанавливают его и переключают трафик. Для крупных систем лучше рассмотреть логическую репликацию или физическую репликацию с последующим switchover.
После восстановления обязательно проверьте расширения и настройки локали. Некоторые расширения должны быть установлены на уровне ОС до восстановления, иначе pg_restore не сможет создать их в базе.
psql -U postgres -d appdb_new -c 'select extname, extversion from pg_extension order by extname;'
psql -U postgres -d appdb_new -c 'select datname, datcollate, datctype from pg_database where datname = current_database();'
Автоматизация через systemd timer
На VDS лучше не полагаться только на ручные команды. Для регулярного логического бэкапа можно использовать простой shell-скрипт и systemd timer. Ниже пример без сложной ротации: он создаёт custom-дамп, пишет лог и удаляет архивы старше 14 дней. Если хотите выносить копии на отдельное хранилище, пригодится материал про бэкапы в S3-совместимое хранилище через restic и Borg.
sudo install -d -m 750 -o postgres -g postgres /backup/postgresql
sudo install -m 750 -o postgres -g postgres /dev/null /usr/local/sbin/pgdump-appdb.sh
#!/bin/sh
set -eu
BACKUP_DIR=/backup/postgresql
DB_NAME=appdb
DB_USER=app_backup
STAMP=$(date +%F_%H%M)
FILE=${BACKUP_DIR}/${DB_NAME}_${STAMP}.dump
pg_dump -h 127.0.0.1 -U ${DB_USER} -d ${DB_NAME} -F c -Z 6 --verbose -f ${FILE}
pg_restore --list ${FILE} >/dev/null
sha256sum ${FILE} > ${FILE}.sha256
find ${BACKUP_DIR} -type f -name '*.dump' -mtime +14 -delete
find ${BACKUP_DIR} -type f -name '*.sha256' -mtime +14 -delete
После создания скрипта назначьте владельца и права:
sudo chown postgres:postgres /usr/local/sbin/pgdump-appdb.sh
sudo chmod 750 /usr/local/sbin/pgdump-appdb.sh
Unit-файл /etc/systemd/system/pgdump-appdb.service:
[Unit]
Description=PostgreSQL logical backup for appdb
[Service]
Type=oneshot
User=postgres
Group=postgres
ExecStart=/usr/local/sbin/pgdump-appdb.sh
Timer-файл /etc/systemd/system/pgdump-appdb.timer:
[Unit]
Description=Run PostgreSQL logical backup for appdb daily
[Timer] 03:20:00
Persistent=true
RandomizedDelaySec=10min
[Install]
WantedBy=timers.target
Включите таймер и проверьте журнал:
sudo systemctl daemon-reload
sudo systemctl enable --now pgdump-appdb.timer
systemctl list-timers pgdump-appdb.timer
journalctl -u pgdump-appdb.service -n 100 --no-pager
Если бэкапы хранятся только на том же диске, что и PostgreSQL, это защита от логической ошибки, но не от потери VDS или диска. Минимально разумная схема — держать свежую локальную копию для быстрого восстановления и внешнюю копию на отдельном сервере или объектном хранилище. Периодически проверяйте не только наличие файлов, но и возможность восстановления.
Типовые ошибки pg_dump и pg_restore
Ошибка: input file does not appear to be a valid archive
Обычно это означает, что вы пытаетесь открыть plain SQL-файл через pg_restore или файл повреждён. Если дамп создавался без -F c, восстанавливайте его через psql. Если файл должен быть custom-архивом, проверьте размер, контрольную сумму и повторите копирование.
Ошибки владельцев и ролей
Сообщения о несуществующих ролях часто появляются при переносе между серверами. Если точное сохранение владельцев не требуется, используйте --no-owner и --no-acl. Если требуется, заранее создайте роли на новом сервере и только потом запускайте восстановление.
Ошибки расширений
Если в базе использовались postgis, pg_trgm, uuid-ossp или другие расширения, убедитесь, что соответствующие пакеты установлены на новом VDS. Простое наличие записи в дампе не гарантирует, что PostgreSQL сможет создать extension без файлов расширения на сервере.
Медленное восстановление индексов
Создание индексов часто занимает больше времени, чем загрузка данных. Помогают быстрый диск, умеренный --jobs, достаточный maintenance_work_mem и отсутствие конкурирующей нагрузки. Но не разгоняйте все параметры сразу: на маленьком VDS избыток параллелизма легко превращается в swap и деградацию.
Дамп зависает или идёт слишком долго
pg_dump работает в согласованном снимке данных, но всё равно читает таблицы и может долго идти по большим объектам. Проверьте активность через pg_stat_activity, нагрузку на диск и наличие долгих транзакций. Если база очень большая, возможно, для регулярных бэкапов лучше использовать физический backup-подход, а логический дамп оставить для миграций и выборочного восстановления.
Практический чек-лист перед боевым восстановлением
- Проверьте, что версия
pg_restoreсовместима с архивом и целевым PostgreSQL. - Убедитесь, что на VDS хватает места под дамп, базу, индексы, временные файлы и WAL.
- Создайте нужные роли, базу, владельца и установите расширения.
- Сначала восстановите архив в отдельную тестовую базу.
- Для миграции между серверами используйте
--no-ownerи--no-acl, если роли отличаются. - Подберите
--jobsпо vCPU, памяти и I/O, а не по максимальному числу. - После восстановления выполните
ANALYZE, если статистика неактуальна или приложение строит плохие планы запросов. - Проверьте приложение, миграции, фоновые воркеры и права подключения до переключения трафика.
После большого восстановления полезно обновить статистику:
vacuumdb -h 127.0.0.1 -U postgres -d appdb_new --analyze-in-stages
--analyze-in-stages помогает быстрее получить первичную статистику для планировщика, а затем уточнить её. Это особенно заметно после переноса базы, когда приложение сразу начинает выполнять сложные запросы.
Итоги
pg_dump в custom format — практичный инструмент для PostgreSQL на VDS: один архив, гибкое восстановление, выбор объектов и поддержка parallel restore через pg_restore --jobs. Он не заменяет физические бэкапы и PITR, но отлично закрывает задачи миграции, тестового восстановления, staging-копий и регулярных логических снимков.
Главное — не ограничиваться командой создания дампа. Проверяйте архив через pg_restore --list, периодически восстанавливайте его в тестовую базу, следите за версиями утилит, правами, расширениями и местом на диске. Тогда в реальный момент восстановления у вас будет не надежда на бэкап, а проверенная процедура.


