Сценарий, который повторяется из проекта в проект. На сервере закончилось место. Чтобы освободить его, выполняют уборку неиспользуемых образов и томов Docker — команда известная, делается «на автомате». Через минуту выясняется, что среди удалённого был том с базой данных. Заказы за полтора года, контакты клиентов, история платежей.
Дальше всё зависит от одного: есть ли копия и работает ли она.
Копия, которую ни разу не восстанавливали, — не копия
Это главная мысль статьи, поэтому она в начале.
Настроенный по инструкции бэкап создаёт ощущение защищённости, но проверяет его только авария. А выясняется в этот момент обычно следующее:
- скрипт полгода назад перестал работать, файлы создаются нулевого размера;
- копируется не та база — например, служебная
postgresвместо рабочей; - дамп есть, но он лежит на том же диске, который и вышел из строя;
- восстановление не запускается, потому что версия PostgreSQL в новом контейнере старше той, где делали дамп.
Резервное копирование состоит из двух половин, и вторая — восстановление — важнее первой.
Два способа копировать базу
Логический дамп (pg_dump)
PostgreSQL сам выгружает содержимое в файл: структуру таблиц и данные. Файл можно залить в базу другой версии, на другой машине, целиком или по одной таблице.
Подходит почти всем. Для базы небольшого интернет-магазина или сервиса записи это правильный выбор по умолчанию.
Копия файлов тома
Копируется каталог, в котором PostgreSQL хранит данные. Быстрее на больших объёмах, но есть условие: копировать каталог работающей базы нельзя. Получится копия, снятая «в движении», и при восстановлении она окажется битой. Нужно либо останавливать базу, либо пользоваться специальными средствами вроде pg_basebackup.
Дальше — про логический дамп, потому что в девяти случаях из десяти нужен именно он.
Как снять дамп из Docker-контейнера
Базовая команда:
docker exec db_container pg_dump -U shop -d shop -Fc > /var/backups/shop_2026-09-20.dump
Разберём по частям:
-U shop -d shop— пользователь и имя базы. Проверьте их по своему.env: скопировать не ту базу проще, чем кажется;-Fc— сжатый формат PostgreSQL. Занимает меньше места и восстанавливается выборочно, в отличие от обычного SQL-файла;- перенаправление
>— дамп уходит на диск сервера, а не остаётся внутри контейнера.
Подводный камень: флаг -t
В интернете часто встречается вариант docker exec -t. Для дампа он вреден: -t выделяет псевдотерминал, и Docker подставляет в поток символы возврата каретки. SQL-файл после этого может не примениться, а бинарный дамп ломается гарантированно.
Правило: при перенаправлении вывода в файл -t не используем. А для восстановления, наоборот, нужен -i, чтобы контейнер прочитал данные со стандартного ввода.
Автоматизация: скрипт и cron
Копия, которую делают руками, делается ровно до первого отпуска. Минимальный рабочий скрипт:
#!/bin/sh
set -eu
DIR=/var/backups/postgres
STAMP=$(date +%Y-%m-%d_%H-%M)
mkdir -p "$DIR"
# Пишем во временный файл и переименовываем только после успеха.
TMP="$DIR/.shop_$STAMP.part"
docker exec db_container pg_dump -U shop -d shop -Fc > "$TMP"
mv "$TMP" "$DIR/shop_$STAMP.dump"
# Удаляем только те копии, которые точно не пустые и старше двух недель.
find "$DIR" -name 'shop_*.dump' -mtime +14 -size +1k -delete
Два места, ради которых стоило писать этот скрипт целиком:
Временный файл. Оболочка создаёт файл при перенаправлении > до запуска команды. Если pg_dump упадёт, на диске останется файл нулевого размера с правильным именем — на вид обычная резервная копия. Поэтому пишем в .part и переименовываем только после успешного завершения.
Осторожная ротация. Удаление «всего старше двух недель» опасно в паре с молча ломающимся бэкапом: свежие файлы создаются пустыми, а исправные старые удаляются по расписанию. Через две недели не остаётся ничего. Проверка -size +1k оставляет заведомо пустые файлы на месте, где они заметны.
Запуск раз в сутки ночью:
30 3 * * * /usr/local/bin/backup-db.sh >> /var/log/backup-db.log 2>&1
Обратите внимание на >> ... 2>&1: без записи ошибок в журнал вы не узнаете, что копирование сломалось.
Где хранить: правило 3-2-1
Простое правило, которое закрывает почти все сценарии потери данных:
- 3 копии данных — рабочая база и две резервные;
- 2 разных носителя или хранилища;
- 1 копия — вне сервера.
Последний пункт решающий. Копия на том же сервере спасает от неудачной миграции и от случайного DROP TABLE, но не спасает от выхода из строя диска, блокировки аккаунта у хостера или шифровальщика, который прошёл по всей файловой системе.
Достаточно раз в сутки забирать свежий дамп на отдельное хранилище — по rsync, scp или в объектное хранилище. Важно, чтобы забирал внешний узел, а не сервер отправлял сам: если сервер скомпрометирован, его учётные данные откроют доступ и к резервным копиям.
Репетиция восстановления
Раз в квартал стоит проверять копию целиком. Это занимает десять минут.
# 1. Поднимаем отдельную базу для проверки
docker run -d --name pgcheck -e POSTGRES_PASSWORD=check \
-e POSTGRES_USER=shop -e POSTGRES_DB=shop postgres:15
# 2. Заливаем последний дамп
docker exec -i pgcheck pg_restore -U shop -d shop --clean --if-exists \
< /var/backups/postgres/shop_2026-09-20.dump
# 3. Смотрим, что данные на месте
docker exec pgcheck psql -U shop -d shop -c "SELECT count(*) FROM orders;"
# 4. Убираем за собой
docker rm -f pgcheck
Сверьте количество строк с рабочей базой. Совпало — копия рабочая, и вы это знаете, а не предполагаете.
Версия образа в первой команде должна быть не старше той, где снимали дамп. Дамп из PostgreSQL 15 не встанет в 13-й — об этом узнают обычно в самый неподходящий момент.
База — не единственное, что нужно копировать
Вместе с базой теряется и то, что лежит рядом:
- загруженные файлы — фотографии товаров, документы, вложения к заявкам. Они обычно в отдельном томе и в дамп не попадают;
- переменные окружения — файл
.envс паролями и ключами платёжной системы. Восстановить базу и не суметь запустить приложение — отдельный жанр; - конфигурация nginx и сертификаты, если они не в репозитории.
Код при этом обычно в безопасности: он в системе контроля версий. А вот всё перечисленное — только на сервере.
Чек-лист
- Копия снимается автоматически, по расписанию, без участия человека.
- Копируется именно рабочая база, имя проверено по
.env. - Неудачный дамп не оставляет пустой файл, который выглядит копией.
- Ротация не удаляет исправные копии, когда новые перестали создаваться.
- Ошибки скрипта попадают в журнал, и кто-то его читает.
- Хотя бы одна копия хранится вне сервера, и забирает её внешний узел.
- Восстановление проверено на практике за последние три месяца.
- Загруженные файлы и
.envкопируются тоже. - Доступ к хранилищу копий ограничен — это такие же данные, как в базе.
Коротко
Резервное копирование — недорогая страховка, которая почти всегда настроена «в целом». Разница между «в целом настроено» и «работает» обнаруживается один раз и стоит всей базы. Поэтому в чек-листе выше самый важный пункт — не про создание копий, а про восстановление.

Не уверены, что ваши копии восстановятся? Посмотрите, как мы работаем с данными, или напишите нам — проверим схему копирования и проведём восстановление на тестовом стенде.


