Резервное копирование VPS: стратегия 3-2-1 на практике

Настройка сервера
Резервное копирование VPS: стратегия 3-2-1 на практике

Резервное копирование VPS — та настройка, о которой вспоминают в худший момент: после rm -rf не в том каталоге, шифровальщика или умершей базы. Правило 3-2-1 формулируется в одну строку: три копии данных, на двух разных носителях, одна — вне основной площадки. В этой инструкции превратим правило в работающую систему: напишем скрипт бэкапа файлов и MySQL для Ubuntu 24.04 LTS, отправим копии на второй сервер по rsync и в S3-хранилище через restic, настроим ротацию и — самое важное — проверим восстановление. Всё на бесплатных инструментах, все команды рабочие.

Почему именно 3-2-1

Каждый элемент правила закрывает свой сценарий отказа:

  • 3 копии (рабочие данные + два бэкапа) — потому что одиночный бэкап имеет свойство оказываться битым именно тогда, когда нужен.
  • 2 разных носителя/системы — локальный архив на том же NVMe погибнет вместе с диском; вторая копия должна жить на другом железе.
  • 1 копия вне площадки — защита от отказа дата-центра, блокировки аккаунта или человеческой ошибки, задевшей все серверы у одного провайдера.

Типичная ошибка новичка — считать бэкапом снапшоты хостера. Это полезный инструмент, но он хранится в той же инфраструктуре: сценарий «проблема на площадке» им не закрыт. Снапшот — дополнение к 3-2-1, а не замена.

Что копировать: карта данных сервера

Копировать образ всей системы обычно избыточно — ОС и пакеты переустанавливаются за 15 минут, а вот данные невосполнимы. Минимальный набор для типичного веб-сервера:

ЧтоГде лежитКак часто
Файлы сайтов и приложений/var/www, /home/appЕжедневно
Базы данныхДамп mysqldump / pg_dumpЕжедневно, для магазинов — каждые 1–6 часов
Конфиги служб/etc/nginx, /etc/ssh, /etc/letsencrypt, /etc/systemd/systemЕжедневно (места почти не занимают)
Задания cronвывод crontab -l, /etc/cron.dЕжедневно
Список пакетоввывод dpkg --get-selectionsЕжедневно

Важно: базу данных нельзя бэкапить простым копированием файлов из /var/lib/mysql на работающем сервере — файлы меняются в процессе, копия выйдет неконсистентной. Только дамп. Если MySQL у вас ещё не настроен как следует, начните со статьи MySQL на VPS: установка и первичная настройка.

Копия №1: локальный скрипт с ротацией

Создаём каталог и скрипт:

sudo mkdir -p /backup
sudo nano /usr/local/bin/backup.sh

Содержимое скрипта (пути и имя базы поменяйте на свои):

#!/bin/bash
set -euo pipefail

DATE=$(date +%F)
DEST="/backup"

# 1. Дамп базы данных (доступ настроен в ~/.my.cnf)
mysqldump --single-transaction --routines --triggers \
  --databases myapp | gzip > "$DEST/db-$DATE.sql.gz"

# 2. Файлы сайта и конфиги
tar -czf "$DEST/files-$DATE.tar.gz" \
  /var/www \
  /etc/nginx /etc/letsencrypt /etc/ssh /etc/systemd/system

# 3. Cron и список пакетов
crontab -l > "$DEST/crontab-$DATE.txt" 2>/dev/null || true
dpkg --get-selections > "$DEST/packages-$DATE.txt"

# 4. Ротация: удаляем локальные копии старше 7 дней
find "$DEST" -maxdepth 1 -type f -mtime +7 -delete

Пара пояснений. Ключ --single-transaction позволяет mysqldump снять целостный срез InnoDB-таблиц без блокировки базы. Пароль в скрипте не светим: создайте файл ~/.my.cnf с правами 600 и секцией [client], где указаны user и password. Строка set -euo pipefail останавливает скрипт при первой ошибке — молча «наполовину сделанный» бэкап хуже отсутствующего.

Делаем исполняемым, проверяем вручную и ставим в cron на 03:30:

sudo chmod +x /usr/local/bin/backup.sh
sudo /usr/local/bin/backup.sh
sudo crontab -e
# добавить строку:
30 3 * * * /usr/local/bin/backup.sh >> /var/log/backup.log 2>&1

Копия №2: второй сервер через rsync

Локальный архив защитил от rm -rf, но не от гибели самого VPS. Вторую копию отправляем на другой сервер — для этой роли не нужна мощная машина, хватит недорогого NAT VPS: у Cheap-Host тариф NAT Plus (4 vCPU, 8 ГБ RAM, 50 ГБ NVMe) стоит 349 ₽/мес, и его диска хватит на недельную историю бэкапов небольшого проекта. Бонус — сервер физически в Германии, то есть третий пункт правила 3-2-1 (копия вне площадки) закрывается автоматически.

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

ssh-keygen -t ed25519 -f ~/.ssh/backup_key -N ""
ssh-copy-id -i ~/.ssh/backup_key.pub -p 22022 backup@IP_резервного

(Порт подставьте свой: на NAT VPS SSH живёт на выделенном порту из панели. Про настройку ключей подробнее — в статье SSH-ключи вместо пароля.)

Синхронизация каталога бэкапов:

rsync -a --delete -e "ssh -p 22022 -i ~/.ssh/backup_key" \
  /backup/ backup@IP_резервного:/srv/backups/web01/

Ключ -a сохраняет права и время файлов, --delete убирает на приёмнике то, что удалено ротацией на источнике. Добавляем команду в тот же backup.sh последней строкой или отдельной cron-задачей на 04:00 — после завершения локального бэкапа.

Копия №3: S3-хранилище через restic

Третья копия — в независимое S3-совместимое хранилище (подойдёт любой провайдер объектного стораджа). Restic здесь удобнее самодельных скриптов: дедупликация экономит место (хранятся только изменённые блоки), шифрование включено по умолчанию, ротация встроена. Установка из репозитория Ubuntu 24.04:

sudo apt install restic

Инициализируем репозиторий и делаем первый бэкап:

export AWS_ACCESS_KEY_ID="ваш_ключ"
export AWS_SECRET_ACCESS_KEY="ваш_секрет"
export RESTIC_REPOSITORY="s3:https://s3.example.com/my-backups"
export RESTIC_PASSWORD="длинная_фраза_для_шифрования"

restic init
restic backup /backup /var/www /etc/nginx
restic snapshots

Ротация по классической схеме «7 дневных, 4 недельных, 6 месячных»:

restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --prune

Пароль репозитория сохраните в надёжном месте вне сервера: restic шифрует данные, и без пароля восстановить их не сможет никто, включая вас. Подробный разбор restic и rclone с примерами под разные хранилища — в отдельной статье бэкапы в S3-хранилище.

Проверка восстановления: без неё всё зря

Непроверенный бэкап — это лотерейный билет, а не страховка. Классика жанра: год исправно работавший скрипт копировал пустой каталог, потому что путь поменялся. Раз в месяц устраивайте учения:

  1. Разверните чистый сервер (на Cheap-Host он выдаётся за 45–60 секунд — удобно поднять машину на час и удалить).
  2. Распакуйте архив: tar -xzf files-2026-07-17.tar.gz -C / или restic restore latest --target /restore.
  3. Залейте дамп: zcat db-2026-07-17.sql.gz | mysql.
  4. Запустите nginx и приложение, откройте сайт, проверьте свежесть данных.
  5. Засеките время — это ваш реальный RTO (время восстановления).

Заодно проверяйте, что бэкапы вообще выполняются: тишина в логах неотличима от успеха. Простейший вариант — строка в конце backup.sh, отправляющая результат в Telegram; как настроить такие уведомления и мониторинг в целом, разобрано в статье мониторинг VPS своими руками. Кстати, отработанное восстановление из бэкапа — это одновременно и готовый план переезда: см. миграция на другой VPS.

Типичные ошибки резервного копирования

  • Бэкап лежит только на том же сервере. Отказ диска уносит и данные, и «страховку».
  • База копируется файлами, а не дампом. Восстановление из такой копии — рулетка.
  • Нет ротации. Диск заполняется, скрипт падает, и это замечают через месяц.
  • Хранится только вчерашняя копия. Взлом или порчу данных часто находят через недели — нужна глубина истории.
  • Секреты в открытом виде. Дампы с данными пользователей уходят в чужое хранилище без шифрования.
  • Восстановление никогда не проверялось. Самая дорогая ошибка из всех.

FAQ

Достаточно ли снапшотов хостера вместо своих бэкапов?

Нет: снапшоты хранятся в той же инфраструктуре, что и сервер, и не защищают от проблем на стороне площадки или закрытия аккаунта. По правилу 3-2-1 хотя бы одна копия должна жить вне хостинга — на другом сервере, в S3 или локально у вас.

Как часто делать бэкап сервера?

Отталкивайтесь от допустимой потери данных (RPO). Для блога достаточно ежесуточной копии, для интернет-магазина базу стоит выгружать каждые 1–6 часов, файлы — раз в сутки. Конфиги достаточно копировать при изменениях, но проще включить их в ежедневный архив.

Можно ли бэкапить MySQL простым копированием файлов?

Нельзя на работающей базе: файлы InnoDB в момент копирования меняются, и копия почти наверняка окажется неконсистентной. Используйте mysqldump --single-transaction — он делает целостный дамп без остановки базы.

Сколько хранить старые копии?

Разумный минимум — 7 ежедневных, 4 еженедельных и 3–6 ежемесячных. Длинный хвост нужен потому, что порчу данных или взлом иногда замечают через недели, и вчерашний бэкап к этому моменту уже содержит проблему.

Нужно ли шифровать бэкапы?

Копии, покидающие сервер, — обязательно: в дампах лежат данные пользователей, в конфигах — пароли и ключи. Restic шифрует всё по умолчанию, для tar-архивов подойдут age или gpg. Пароль шифрования храните отдельно от бэкапов — без него восстановление невозможно.

Вывод

Стратегия 3-2-1 на VPS реализуется за вечер: скрипт с mysqldump и tar плюс cron — первая копия, rsync на второй сервер — вторая, restic в S3 — третья, вне площадки. Останется ежемесячно проверять восстановление — и потеря сервера превратится из катастрофы в час рутинной работы. Если нужен сервер под основной проект — у Cheap-Host тариф Pro за 750 ₽/мес (12 vCPU, 16 ГБ RAM, 128 ГБ NVMe, безлимитный трафик), а под хранение бэкапов — NAT Plus за 349 ₽/мес с 50 ГБ NVMe в Германии; оба разворачиваются за минуту.

Читайте также