Сценарий, который повторяется из проекта в проект. На сервере закончилось место. Чтобы освободить его, выполняют уборку неиспользуемых образов и томов 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 копируются тоже.
  • Доступ к хранилищу копий ограничен — это такие же данные, как в базе.

Коротко

Резервное копирование — недорогая страховка, которая почти всегда настроена «в целом». Разница между «в целом настроено» и «работает» обнаруживается один раз и стоит всей базы. Поэтому в чек-листе выше самый важный пункт — не про создание копий, а про восстановление.

EASYSOCHI — резервное копирование и хранение данных

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