Резервное копирование и восстановление томов Docker на VPS

10 мин чтения·Matthieu·cronrclones3mysqlpostgresqlbackupdocker-composedocker|

Три стратегии резервного копирования томов Docker на VPS: tar-снапшоты, нативные дампы баз данных и автоматизированные зашифрованные бэкапы с offen/docker-volume-backup. Планирование через cron, удалённые копии на S3 через rclone и полный тест восстановления на чистом сервере.

Именованные тома Docker хранят ваши продакшен-данные: базы данных, загрузки, состояние конфигурации. Контейнеры одноразовые. Тома нет. Если диск выйдет из строя или миграция пойдёт не так, именно тома вам нужно будет восстановить.

Это руководство описывает три стратегии бэкапа, автоматизирует их через cron, отправляет копии за пределы сервера через rclone, а затем доказывает работоспособность восстановления, поднимая всё на чистом VPS. Если вы не тестировали восстановление, у вас нет бэкапов.

Что вы узнаете:

  • Tar-снапшоты томов (просто, универсально)
  • Нативные дампы баз данных через pg_dump и mysqldump (консистентные, без простоя)
  • Автоматизированные зашифрованные бэкапы через offen/docker-volume-backup (по расписанию, с поддержкой S3)
  • Автоматизация через cron с политиками ротации
  • Удалённые копии в S3-совместимое хранилище через rclone
  • Полное аварийное восстановление на чистом VPS

Предварительные требования

Нужен VPS на Debian 12 или Ubuntu 24.04 с установленными Docker и Docker Compose v2. Руководство предполагает, что у вас есть работающий Compose-стек с хотя бы одним именованным томом. Если нужно сначала это настроить, смотрите наше руководство по Docker Compose с несколькими сервисами на VPS.

Проверьте вашу установку:

docker --version
# Docker version 28.x or newer
docker compose version
# Docker Compose version v2.x or newer

Посмотрите существующие тома:

docker volume ls

Вывод покажет каждый именованный том в системе. Определите, какие из них содержат важные данные. Используйте docker system df -v, чтобы узнать, сколько места занимает каждый том. Это поможет оценить размер бэкапов и потребности в хранилище.

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

mkdir -p /opt/backups/docker
chmod 700 /opt/backups/docker

Только root может читать эту директорию. Бэкапы часто содержат учётные данные баз данных, сессионные токены или пользовательские данные.

Как сделать бэкап томов Docker на VPS?

Есть три стратегии с разными компромиссами. Выбирайте в зависимости от содержимого томов и допустимого времени простоя.

Метод Простой Консистентность данных Автоматизация Шифрование Подходит для
Tar-снапшот Кратковременный (контейнер остановлен) Уровень файловой системы Вручную или cron-скрипт Нет (добавьте GPG отдельно) Статические файлы, загрузки, конфиг
Дамп базы данных Нет Транзакционная консистентность Вручную или cron-скрипт Нет (добавьте GPG отдельно) PostgreSQL, MySQL, MariaDB
offen/docker-volume-backup Опционально (настраивается) Уровень файловой системы Встроенный планировщик Встроенный GPG Любой том, автономная работа

Как создать tar-бэкап тома Docker?

Остановите контейнер, использующий том, запустите временный Alpine-контейнер для архивации содержимого тома в tar, затем перезапустите. Для большинства томов это занимает секунды и работает с любым типом данных.

1. Остановите контейнер, пишущий в том:

# Replace "app" with your service name from docker-compose.yml
docker compose stop app

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

2. Создайте tar-архив:

docker run --rm \
  -v myapp_data:/source:ro \
  -v /opt/backups/docker:/backup \
  alpine tar czf /backup/myapp_data-$(date +%Y%m%d-%H%M%S).tar.gz -C /source .

Что делает эта команда: запускает одноразовый Alpine-контейнер, монтирует ваш том как read-only в /source, монтирует директорию бэкапов в /backup и создаёт gzip-сжатый tar-архив. Флаг --rm удаляет контейнер после завершения. Флаг :ro не даёт процессу бэкапа случайно записать что-то в ваши данные.

3. Перезапустите контейнер:

docker compose start app

4. Проверьте архив:

ls -lh /opt/backups/docker/myapp_data-*.tar.gz

Вывод показывает архив разумного размера. Том в 500 МБ обычно сжимается до 60-120 МБ в зависимости от типа данных.

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

tar tzf /opt/backups/docker/myapp_data-20260319-120000.tar.gz | head -20

пути должны начинаться с ./ (без имени директории в начале). Это потому, что мы использовали -C /source . в команде tar. Это важно при восстановлении.

Как сделать бэкап PostgreSQL в Docker?

Используйте pg_dump внутри работающего контейнера. Это даёт транзакционно-консистентный дамп без остановки базы. Формат custom (-Fc) сжимает вывод и поддерживает выборочное восстановление.

docker compose exec -T postgres pg_dump \
  -U "$POSTGRES_USER" \
  -Fc \
  --no-owner \
  --no-acl \
  mydb > /opt/backups/docker/mydb-$(date +%Y%m%d-%H%M%S).dump

Что делает эта команда: exec -T выполняет команду в работающем контейнере postgres без выделения TTY (обязательно для перенаправления вывода). -Fc выбирает формат custom, который сжат и поддерживает pg_restore. --no-owner и --no-acl делают дамп переносимым между разными пользователями базы.

Переменная $POSTGRES_USER должна браться из файла окружения, а не быть захардкоженной. Если ваш Compose-стек использует env-файл:

source /opt/myapp/.env
docker compose exec -T postgres pg_dump \
  -U "$POSTGRES_USER" \
  -Fc \
  --no-owner \
  --no-acl \
  "$POSTGRES_DB" > /opt/backups/docker/"$POSTGRES_DB"-$(date +%Y%m%d-%H%M%S).dump

Проверьте дамп, пропустив его через pg_restore контейнера:

docker compose exec -T postgres pg_restore --list < /opt/backups/docker/mydb-20260319-120000.dump | head -10

Это выводит оглавление без фактического восстановления. Если файл повреждён, pg_restore выдаст ошибку. Мы используем docker compose exec -T, потому что pg_restore находится внутри контейнера, а не на хосте (если только вы не установите postgresql-client отдельно).

Как сделать бэкап MySQL в Docker?

Используйте mysqldump с --single-transaction для таблиц InnoDB. Это даёт консистентный снапшот без блокировки базы.

docker compose exec -T mysql mysqldump \
  -u root \
  -p"$MYSQL_ROOT_PASSWORD" \
  --single-transaction \
  --routines \
  --triggers \
  mydb > /opt/backups/docker/mydb-$(date +%Y%m%d-%H%M%S).sql

У флага -p нет пробела перед паролем. --single-transaction использует согласованное чтение для таблиц InnoDB. --routines и --triggers включают хранимые процедуры и триггеры, которые mysqldump пропускает по умолчанию.

Убедитесь, что дамп не пустой и заканчивается маркером завершения:

tail -5 /opt/backups/docker/mydb-20260319-120000.sql

Вывод показывает -- Dump completed on YYYY-MM-DD HH:MM:SS. Если файл обрезан или пуст, дамп не удался.

Как автоматизировать бэкап томов Docker с offen/docker-volume-backup?

offen/docker-volume-backup работает как sidecar-контейнер в вашем Compose-стеке. Он бэкапит смонтированные тома по расписанию, опционально шифрует архивы через GPG и может загружать напрямую в S3-совместимое хранилище. Также умеет останавливать контейнеры на время бэкапа для обеспечения консистентности.

Добавьте сервис бэкапа в ваш docker-compose.yml:

services:
  # ... your existing services ...

  backup:
    image: offen/docker-volume-backup:v2.47.2
    restart: unless-stopped
    env_file:
      - ./backup.env
    volumes:
      - myapp_data:/backup/myapp_data:ro
      - myapp_db:/backup/myapp_db:ro
      - /opt/backups/docker:/archive
      - /var/run/docker.sock:/var/run/docker.sock
    labels:
      - docker-volume-backup.stop-during-backup=false

Что делает эта конфигурация: монтирует каждый том для бэкапа в read-only под /backup/, монтирует /archive для локального хранения бэкапов и монтирует Docker-сокет, чтобы инструмент мог останавливать и перезапускать контейнеры при соответствующей настройке. Тег образа v2.47.2 фиксирует версию. Не используйте latest в продакшене.

Замечание по безопасности: монтирование Docker-сокета даёт бэкап-контейнеру полный контроль над Docker на хосте. Это необходимо для функции остановки во время бэкапа. Если эта функция не нужна, можно смонтировать сокет в read-only (/var/run/docker.sock:/var/run/docker.sock:ro), что позволит инструменту читать метки контейнеров, но не даст их останавливать или запускать.

Создайте файл окружения с ограниченными правами:

touch /opt/myapp/backup.env
chmod 600 /opt/myapp/backup.env
# /opt/myapp/backup.env
BACKUP_CRON_EXPRESSION=0 3 * * *
BACKUP_RETENTION_DAYS=7
BACKUP_COMPRESSION=gz
BACKUP_FILENAME=backup-%Y%m%dT%H%M%S.tar.gz

# GPG encryption (generate a strong passphrase)
GPG_PASSPHRASE=your-generated-passphrase-here

# S3-compatible storage (optional, see rclone section for alternative)
# AWS_S3_BUCKET_NAME=my-backups
# AWS_S3_PATH=myapp
# AWS_ENDPOINT=s3.eu-central-1.amazonaws.com
# AWS_ACCESS_KEY_ID=
# AWS_SECRET_ACCESS_KEY=

Сгенерируйте GPG-пароль:

openssl rand -base64 32

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

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

services:
  app:
    # ... your config ...
    labels:
      - docker-volume-backup.stop-during-backup=true

Запустите сервис бэкапа:

docker compose up -d backup

Проверьте, что он работает:

docker compose logs backup

Вывод показывает строку лога с подтверждением cron-расписания. Дождитесь первого запуска по расписанию или запустите бэкап вручную:

docker compose exec backup backup

Проверьте, что архив появился:

ls -lh /opt/backups/docker/

Как настроить расписание бэкапов Docker через cron?

Для tar и дампов баз данных планирование и ротацию берёт на себя shell-скрипт с cron. У offen есть свой планировщик; пропустите этот раздел, если используете только его.

Создайте скрипт бэкапа:

touch /opt/backups/docker-backup.sh
chmod 700 /opt/backups/docker-backup.sh
#!/usr/bin/env bash
# /opt/backups/docker-backup.sh
# Backs up Docker volumes and databases, removes old archives.

set -euo pipefail

BACKUP_DIR="/opt/backups/docker"
RETENTION_DAYS=7
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
COMPOSE_DIR="/opt/myapp"

# Load database credentials from env file
source "${COMPOSE_DIR}/.env"

cd "$COMPOSE_DIR"

# --- Tar backup of application data volume ---
docker compose stop app
docker run --rm \
  -v myapp_data:/source:ro \
  -v "${BACKUP_DIR}":/backup \
  alpine tar czf "/backup/myapp_data-${TIMESTAMP}.tar.gz" -C /source .
docker compose start app

# --- PostgreSQL dump (no downtime) ---
docker compose exec -T postgres pg_dump \
  -U "$POSTGRES_USER" \
  -Fc --no-owner --no-acl \
  "$POSTGRES_DB" > "${BACKUP_DIR}/${POSTGRES_DB}-${TIMESTAMP}.dump"

# --- Retention: delete backups older than N days ---
find "$BACKUP_DIR" -type f -name "*.tar.gz" -mtime +${RETENTION_DAYS} -delete
find "$BACKUP_DIR" -type f -name "*.dump" -mtime +${RETENTION_DAYS} -delete

echo "[$(date -Iseconds)] Backup completed successfully"

Проверьте, что скрипт работает без ошибок:

/opt/backups/docker-backup.sh

Проверьте выходные файлы:

ls -lh /opt/backups/docker/

Добавьте запись в cron для ежедневного запуска в 03:00 с логированием:

crontab -e
0 3 * * * /opt/backups/docker-backup.sh >> /var/log/docker-backup.log 2>&1

2>&1 перенаправляет stderr в тот же лог-файл, чтобы ошибки были зафиксированы. Проверьте лог после первого запуска:

cat /var/log/docker-backup.log

Если скрипт упадёт, cron молча проглотит ошибку, если вы не перенаправили вывод. Чтобы получать email-оповещения при сбоях, добавьте такой wrapper:

0 3 * * * /opt/backups/docker-backup.sh >> /var/log/docker-backup.log 2>&1 || echo "Docker backup failed on $(hostname)" | mail -s "BACKUP FAILED" you@example.com

Для этого нужен mailutils или аналогичный пакет. Подставьте свой адрес получателя.

Как скопировать бэкапы Docker в S3-совместимое хранилище через rclone?

Локальные бэкапы защищают от сбоев приложений. Они не защищают от отказа диска или взлома сервера. Нужны удалённые копии. rclone работает с любым S3-совместимым хранилищем: AWS S3, Backblaze B2, Wasabi, MinIO, OVH Object Storage, Scaleway и другими.

Установите rclone:

apt update && apt install -y rclone

Настройте S3-совместимый remote:

rclone config

Следуйте интерактивным подсказкам:

  1. n для нового remote
  2. Назовите его s3backup
  3. Выберите s3 (Amazon S3 Compliant Storage Providers)
  4. Выберите вашего провайдера (или "Any other S3 compatible provider")
  5. Введите access key и secret key
  6. Укажите регион и URL эндпоинта вашего провайдера
  7. Остальные параметры оставьте по умолчанию

Проверьте, что remote работает:

rclone lsd s3backup:

Это покажет ваши бакеты. Если не работает, проблема с учётными данными или эндпоинтом.

Создайте бакет для бэкапов (если ваш провайдер поддерживает это через rclone):

rclone mkdir s3backup:my-docker-backups

Синхронизируйте локальную директорию бэкапов с бакетом:

rclone sync /opt/backups/docker s3backup:my-docker-backups/$(hostname)/ \
  --transfers 4 \
  --checkers 8 \
  --log-file /var/log/rclone-backup.log \
  --log-level INFO

Что делает эта команда: sync приводит remote в соответствие с локальной директорией. Файлы, удалённые локально (при ротации), удаляются и на remote. Префикс $(hostname) разделяет бэкапы, если у вас несколько серверов.

Проверьте загрузку:

rclone ls s3backup:my-docker-backups/$(hostname)/

Вывод показывает ваши файлы бэкапов с размерами, совпадающими с локальными копиями.

Добавьте rclone sync в скрипт бэкапа или как отдельную запись cron, которая запускается после бэкапа:

30 3 * * * rclone sync /opt/backups/docker s3backup:my-docker-backups/$(hostname)/ --transfers 4 --log-file /var/log/rclone-backup.log --log-level INFO

Это запускается в 03:30, давая задаче бэкапа в 03:00 время на завершение.

Защитите конфигурацию rclone: в ней ваши S3-учётные данные.

chmod 600 ~/.config/rclone/rclone.conf
ls -la ~/.config/rclone/rclone.conf

Вывод показывает права -rw-------. Только root может читать этот файл.

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

Зависит от содержимого тома. Ошибка здесь является самой частой причиной повреждённых бэкапов.

Базы данных (PostgreSQL, MySQL, MongoDB): никогда не делайте tar работающего тома базы данных. Файлы на диске представляют незавершённое состояние транзакций. Tar этих файлов подобен ксерокопированию книги, пока кто-то переписывает главы. Результат внутренне несогласован. Используйте pg_dump, mysqldump или mongodump. Эти инструменты создают транзакционно-консистентный снапшот, пока база продолжает работать.

Данные приложения (загрузки, статические файлы, конфиг): tar безопасен, если приложение терпит кратковременную остановку. Если приложение непрерывно пишет и вы не можете его остановить, tar может содержать частично записанные файлы. Для большинства веб-приложений остановка на 2 секунды во время бэкапа в 3 часа ночи вполне приемлема.

Redis, хранилища ключ-значение: Redis периодически записывает RDB-снапшоты на диск. Запустите BGSAVE перед архивацией тома, затем дождитесь завершения. Это даст консистентный снапшот без остановки Redis.

docker compose exec redis redis-cli BGSAVE
# Wait a few seconds
docker compose exec redis redis-cli LASTSAVE

Безопасный вариант по умолчанию: если не уверены, остановите контейнер, сделайте бэкап, перезапустите. Короткий простой лучше повреждённых бэкапов.

Как восстановить тома Docker на новом VPS?

Это процедура, которая доказывает работоспособность ваших бэкапов. Установите Docker на чистый сервер, перенесите файлы бэкапов, пересоздайте тома, восстановите данные и убедитесь, что приложение работает.

1. Установите Docker на новый VPS

apt update && apt install -y ca-certificates curl
install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc
chmod a+r /etc/apt/keyrings/docker.asc

echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] \
  https://download.docker.com/linux/debian $(. /etc/os-release && echo "$VERSION_CODENAME") stable" \
  | tee /etc/apt/sources.list.d/docker.list > /dev/null

apt update && apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

Проверка:

docker --version && docker compose version

2. Перенесите файлы бэкапов на новый сервер

С вашей локальной машины или старого сервера:

rsync -avz --progress /opt/backups/docker/ root@NEW_SERVER_IP:/opt/backups/docker/

Или скачайте из S3:

# On the new server, install and configure rclone first
apt install -y rclone
# Re-run rclone config with the same credentials
rclone copy s3backup:my-docker-backups/OLD_HOSTNAME/ /opt/backups/docker/

Убедитесь, что файлы на месте:

ls -lh /opt/backups/docker/

3. Скопируйте Compose-файлы и env-файлы

rsync -avz /opt/myapp/ root@NEW_SERVER_IP:/opt/myapp/

Или восстановите из Git-репозитория. Ваши docker-compose.yml и .env должны быть под контролем версий. Файл .env должен быть в .gitignore и бэкапиться отдельно.

4. Восстановите том из tar

# Create the volume (Docker Compose will also do this on first `up`,
# but creating it explicitly lets us restore data before starting services)
docker volume create myapp_data

# Restore from archive
docker run --rm \
  -v myapp_data:/target \
  -v /opt/backups/docker:/backup:ro \
  alpine sh -c "cd /target && tar xzf /backup/myapp_data-20260319-030000.tar.gz"

Что делает эта команда: создаёт именованный том, затем запускает временный контейнер, который извлекает архив в него. Директория бэкапов смонтирована как read-only для предотвращения случайных изменений.

Проверьте восстановленные данные:

docker run --rm -v myapp_data:/data:ro alpine ls -la /data/

Вывод показывает те же файлы, что были в исходном томе.

5. Восстановите базу PostgreSQL

Запустите только контейнер базы данных:

cd /opt/myapp
docker compose up -d postgres

Дождитесь готовности:

docker compose logs -f postgres
# Wait until you see "database system is ready to accept connections"

Восстановите дамп:

docker compose exec -T postgres pg_restore \
  -U "$POSTGRES_USER" \
  -d "$POSTGRES_DB" \
  --clean \
  --if-exists \
  --no-owner \
  --no-acl \
  < /opt/backups/docker/mydb-20260319-030000.dump

Что делает эта команда: --clean удаляет существующие объекты перед пересозданием. --if-exists предотвращает ошибки, если объекты ещё не существуют. Это делает восстановление идемпотентным.

Проверьте данные:

docker compose exec postgres psql -U "$POSTGRES_USER" -d "$POSTGRES_DB" -c "\dt"

Вывод показывает свои таблицы. Выполните быстрый подсчёт строк в известной таблице:

docker compose exec postgres psql -U "$POSTGRES_USER" -d "$POSTGRES_DB" -c "SELECT count(*) FROM your_table;"

6. Запустите все сервисы и проверьте

docker compose up -d

Убедитесь, что все контейнеры работают:

docker compose ps

Каждый сервис должен показывать Up или running. Если контейнер перезапускается в цикле, проверьте его логи:

docker compose logs --tail 50 service_name

Проверьте приложение извне сервера. С вашей локальной машины:

curl -I http://NEW_SERVER_IP:PORT

Вы должны получить валидный HTTP-ответ. Если у приложения есть эндпоинт проверки здоровья (health check), обратитесь к нему:

curl http://NEW_SERVER_IP:PORT/health

Подробнее о health check смотрите в нашем руководстве по ограничениям ресурсов и health check в Docker Compose.

Как проверить, что бэкап тома Docker валиден?

Непроверенный бэкап это риск. Выполняйте эти проверки регулярно, а не только при аварийном восстановлении.

Проверьте целостность архива:

# For tar.gz files
gzip -t /opt/backups/docker/myapp_data-20260319-030000.tar.gz && echo "OK" || echo "CORRUPT"

Проверьте содержимое архива:

tar tzf /opt/backups/docker/myapp_data-20260319-030000.tar.gz | wc -l

Сравните количество файлов с заведомо хорошим бэкапом. Резкое падение числа файлов указывает на проблему.

Протестируйте восстановление во временный том:

docker volume create test_restore
docker run --rm \
  -v test_restore:/target \
  -v /opt/backups/docker:/backup:ro \
  alpine sh -c "cd /target && tar xzf /backup/myapp_data-20260319-030000.tar.gz"

# Inspect the restored data
docker run --rm -v test_restore:/data:ro alpine ls -la /data/

# Clean up
docker volume rm test_restore

Проверьте дамп базы данных:

docker compose exec -T postgres pg_restore --list < /opt/backups/docker/mydb-20260319-030000.dump | wc -l

Если возвращается количество объектов (таблицы, индексы, последовательности), дамп читаем. Если ошибка, файл повреждён.

Сгенерируйте контрольные суммы для долгосрочного хранения:

sha256sum /opt/backups/docker/*.tar.gz /opt/backups/docker/*.dump > /opt/backups/docker/checksums-$(date +%Y%m%d).sha256

Загрузите файл контрольных сумм вместе с бэкапами. Перед восстановлением проверьте:

sha256sum -c /opt/backups/docker/checksums-20260319.sha256

Устранение неполадок

"Permission denied" при создании tar-архива: Временный контейнер по умолчанию запускается от root, так что это обычно означает, что директория бэкапов не существует или имеет неправильные права. Выполните ls -la /opt/backups/ и убедитесь, что поддиректория docker существует с правами 700.

pg_dump/pg_restore зависает: Вы, вероятно, забыли флаг -T в docker compose exec. Без -T exec пытается выделить TTY, что блокирует при перенаправлении вывода. Используйте docker compose exec -T.

Файлы бэкапов имеют размер 0 байт: Контейнер записал по другому пути, чем ожидалось. Перепроверьте, что имя тома в docker volume ls совпадает с тем, что вы использовали во флаге -v. Именованные тома чувствительны к регистру.

rclone sync прерывается по таймауту: Большие начальные синхронизации могут превышать таймауты по умолчанию. Добавьте --timeout 30m и --retries 3 к команде rclone.

offen/docker-volume-backup не запускается по расписанию: Проверьте синтаксис BACKUP_CRON_EXPRESSION. Инструмент использует стандартный 5-полевой cron-синтаксис. Выполните docker compose logs backup и поищите ошибки парсинга.

Восстановленная база имеет неправильные права: Вы использовали дамп без --no-owner. Дамп пытается установить владельца на исходного пользователя, которого может не быть на новом сервере. Сделайте дамп заново с --no-owner --no-acl или выполните REASSIGN OWNED BY old_user TO new_user; в psql.

Что дальше

  • Полная настройка Docker в продакшене
  • Проверки здоровья, подтверждающие работоспособность сервисов после восстановления
  • Основы Docker CLI

Авторское право 2026 Virtua.Cloud. Все права защищены. Данный контент является оригинальным произведением команды Virtua.Cloud. Воспроизведение, повторная публикация или распространение без письменного разрешения запрещены.

Готовы попробовать?

Разверните Docker с автоматическим резервным копированием на VPS.

Смотреть тарифы VPS