Бэкапы сайтов и проектов: как делать правильно и нужен ли отдельный S3

В этой статье разберём, как правильно делать резервные копии сайтов и проектов. Что именно копировать, сколько копий нужно и где их хранить, почему бэкапов хостинга недостаточно и зачем нужно отдельное S3-хранилище у другого провайдера. Покажем, как защитить копии от взлома и случайного удаления и как всё это автоматизировать. В конце — готовый скрипт, пример для Laravel и чек-лист. Примеры рассчитаны на сайты на VPS или хостинге с SSH, например на Рег.ру или Timeweb Cloud, но подход одинаковый для любого провайдера.

От чего защищает бэкап

Резервная копия нужна не «на случай поломки диска» — это как раз самый редкий сценарий. Чаще данные теряют так:

  • Ошибка человека. DROP TABLE не на той базе, неудачная миграция, удалённая папка с загрузками.
  • Ошибка в коде. Скрипт, который неделю тихо портил данные, пока никто не заметил.
  • Взлом. Злоумышленник шифрует или удаляет данные — и заодно бэкапы, если до них можно дотянуться с того же сервера.
  • Проблемы у провайдера. Сбой в дата-центре, блокировка аккаунта, неоплаченный счёт, ошибка поддержки. Известный пример — пожар в дата-центре OVH в Страсбурге в марте 2021 года: часть клиентов потеряла и серверы, и копии, которые хранились там же.

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

Что копировать, а что нет

Что Копировать Почему
База данных (MySQL, PostgreSQL) Да, обязательно Самое ценное и то, что нельзя восстановить из других источников
Загруженные файлы (uploads, storage/app) Да Картинки и документы пользователей есть только на сервере
Конфигурация (.env, конфиги Nginx, cron) Да, но отдельно и зашифровано Без неё восстановление растянется на часы; внутри пароли и ключи
Код Он уже в Git Но Git-репозиторий тоже нужно где-то копировать, если он у одного провайдера
vendor/, node_modules/, кэш, логи Нет Пересобираются командами composer install, npm ci; только раздувают копию

Отдельно — секреты: пароли от S3, ключ шифрования бэкапов, доступы к хостингу. Их храните в менеджере паролей, а не только на сервере. Если сервер пропал вместе с ключом шифрования, зашифрованный бэкап бесполезен.

Правило 3-2-1

Классическое правило резервного копирования:

  • 3 копии данных: рабочая и две резервные;
  • 2 разных типа хранения или площадки: например, диск сервера и объектное хранилище;
  • 1 копия — вне основной площадки: у другого провайдера, в другом аккаунте.

Сейчас его часто дополняют до 3-2-1-1-0:

  • ещё 1 копия — неизменяемая (immutable) или отключённая от сети: её нельзя удалить даже с полным доступом к серверу;
  • 0 ошибок при проверке восстановления: бэкап считается рабочим, только когда из него реально восстановились.

Для небольшого проекта это выглядит так: бэкапы хостинга (копия 1) плюс собственные ежедневные дампы в S3 у другого провайдера с версионированием (копия 2, вне площадки и защищённая от удаления).

Почему бэкапов хостинга недостаточно

Рег.ру, Timeweb Cloud и другие провайдеры делают резервные копии VPS и хостинга — платно или в составе тарифа. Это хороший первый уровень: быстро откатить сервер целиком после неудачного обновления. Но полагаться только на них нельзя:

  • копии лежат у того же провайдера и в том же аккаунте. Заблокировали аккаунт, взломали личный кабинет, случилась авария в дата-центре — пропадает всё сразу;
  • обычно хранится несколько последних копий. Если порчу данных заметили через месяц, нужной версии уже нет;
  • снимок диска с работающей базой не всегда консистентен. Дамп базы надёжнее;
  • вы не контролируете расписание, срок хранения и не проверяете восстановление.

Нужен ли независимый S3

Да, и лучше у другого провайдера, чем хостинг. Объектное хранилище S3 — самый удобный способ хранить копии вне площадки:

  • платите только за объём, а бэкапы небольших сайтов занимают единицы гигабайт;
  • данные хранятся с репликацией внутри провайдера, вам не нужно следить за дисками;
  • S3 API понимают все инструменты бэкапа: restic, rclone, Kopia, spatie/laravel-backup, плагины WordPress;
  • есть версионирование и блокировка объектов (Object Lock) — защита от удаления.

S3-совместимые хранилища есть у многих российских провайдеров: у Timeweb Cloud, Рег.ру (Рег.облако), Selectel, Yandex Cloud (Object Storage) и других. Логика выбора простая — разнесите сайт и бэкапы по разным компаниям:

  • сайты на Рег.ру → бэкапы в S3 Timeweb Cloud или Selectel;
  • сайты в Timeweb Cloud → бэкапы в S3 Рег.ру, Selectel или Yandex Cloud.

Тогда проблема с одним провайдером не затронет второй. Если в базе есть персональные данные россиян, выбирайте хранилище с дата-центром в России — этого требует закон 152-ФЗ.

Как настроить S3 под бэкапы

  1. Создайте отдельный приватный бакет только для бэкапов, без публичного доступа.
  2. Включите версионирование. Если файл перезапишут или удалят, предыдущая версия останется.
  3. Если провайдер поддерживает Object Lock (например, в Timeweb Cloud он есть), включите его со сроком хранения 14–30 дней. Заблокированную версию нельзя удалить до конца срока даже с ключом администратора — это и есть неизменяемая копия.
  4. Создайте для сервера отдельный ключ доступа с правами только на этот бакет. Ключ от основного аккаунта на сервер класть нельзя.
  5. Настройте правила жизненного цикла (lifecycle), чтобы старые версии удалялись автоматически и хранилище не росло бесконечно.

Названия пунктов в панели и доступные режимы у провайдеров отличаются — сверяйтесь с документацией своего хранилища.

Куда отправлять копии: push или pull

  • Push — сервер сам отправляет бэкап в хранилище. Просто, но на сервере лежит ключ от хранилища. Взломщик может им воспользоваться — поэтому нужны версионирование, Object Lock и ключ с минимальными правами.
  • Pull — отдельный сервер забирает бэкапы с рабочих серверов по SSH. Рабочий сервер ничего не знает о хранилище, и взлом сайта не даёт доступа к копиям. Надёжнее, но нужен ещё один сервер.

Для нескольких небольших сайтов обычно достаточно push в S3 с версионированием. Pull стоит рассмотреть, когда проектов много или данные критичные.

Как делать дамп базы правильно

Не копируйте файлы базы (/var/lib/mysql) с работающего сервера — получите неконсистентную копию. Делайте логический дамп.

MySQL / MariaDB:

mysqldump --single-transaction --quick --routines --triggers --no-tablespaces \
  my_database | gzip > /var/backups/db/my_database-$(date +%F).sql.gz
  • --single-transaction — консистентный снимок таблиц InnoDB без блокировки сайта;
  • --quick — построчная выгрузка, не загружает всю таблицу в память;
  • --routines --triggers — вместе с данными сохранит процедуры и триггеры;
  • --no-tablespaces — нужен в MySQL 8, если у пользователя нет привилегии PROCESS (типично для хостинга); иначе дамп падает с ошибкой.

Пароль не передавайте в командной строке — его видно в списке процессов. Положите его в ~/.my.cnf с правами 600:

[mysqldump]
user=backup_user
password=секретный_пароль

PostgreSQL:

pg_dump --format=custom my_database > /var/backups/db/my_database-$(date +%F).dump

Формат custom уже сжат и позволяет восстанавливать отдельные таблицы через pg_restore.

Для бэкапов заведите отдельного пользователя БД только с правами на чтение (SELECT, SHOW VIEW, TRIGGER, LOCK TABLES, EVENT), а не используйте пользователя приложения или root.

Шифрование

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

Проще всего взять инструмент, который шифрует сам. restic шифрует всё по умолчанию, умеет дедупликацию (повторяющиеся данные хранятся один раз), ротацию копий и работает с любым S3-совместимым хранилищем. Альтернативы — Kopia и Borg (Borg работает по SSH, а не с S3 напрямую); для простых случаев — gpg или age поверх rclone.

Ротация: сколько хранить

Хранить все копии вечно дорого, а только последнюю — опасно. Обычно используют схему «дед-отец-сын» (GFS):

  • 7 ежедневных копий;
  • 4 еженедельных;
  • 12 ежемесячных.

Так можно откатиться и на вчера, и на состояние трёхмесячной давности, если порчу заметили поздно. А места это занимает немного — особенно с дедупликацией.

Как часто делать копии, зависит от того, сколько данных не жалко потерять. Это называют RPO. Блог с одной статьёй в неделю — раз в сутки достаточно. Интернет-магазин с заказами каждый час — дампы каждый час или репликация базы. Второй параметр — RTO: за сколько нужно поднять сайт. От него зависит, хватит ли дампа или нужен готовый резервный сервер.

Пример: скрипт бэкапа на restic

Скрипт для VPS: делает дамп базы, отправляет его вместе с загрузками в S3, удаляет старые копии и сообщает об успехе в сервис мониторинга.

Сначала установите restic (sudo apt install restic в Ubuntu/Debian) и создайте файл с настройками /root/.backup-env с правами 600:

export AWS_ACCESS_KEY_ID='ключ_доступа_к_бакету'
export AWS_SECRET_ACCESS_KEY='секретный_ключ'
export RESTIC_REPOSITORY='s3:https://<endpoint-провайдера>/<имя-бакета>/my-site'
export RESTIC_PASSWORD_FILE='/root/.restic-password'

Адрес endpoint возьмите из панели S3 своего провайдера. В /root/.restic-password положите длинный случайный пароль (например, из openssl rand -base64 32) и сохраните его копию в менеджере паролей — без него восстановить данные невозможно. Затем один раз инициализируйте репозиторий:

source /root/.backup-env && restic init

Сам скрипт /usr/local/bin/backup-site.sh:

#!/usr/bin/env bash
set -euo pipefail

source /root/.backup-env

DUMP_DIR=/var/backups/db
mkdir -p "$DUMP_DIR"

# 1. Дамп базы (пароль — в /root/.my.cnf)
mysqldump --single-transaction --quick --routines --triggers --no-tablespaces \
  my_database | gzip > "$DUMP_DIR/my_database.sql.gz"

# 2. Отправка дампа, загрузок и конфигов в S3 (шифрует restic)
restic backup "$DUMP_DIR" /var/www/my-site/storage/app /var/www/my-site/.env \
  --tag my-site

# 3. Ротация: 7 дневных, 4 недельных, 12 месячных
restic forget --tag my-site --keep-daily 7 --keep-weekly 4 --keep-monthly 12 --prune

# 4. Сообщить мониторингу, что бэкап прошёл
curl -fsS --retry 3 https://hc-ping.com/<ваш-uuid> > /dev/null

Файл дампа каждый день перезаписывается под одним именем — это нормально: историю версий хранит restic. Из-за set -euo pipefail скрипт остановится на первой ошибке и не отправит сигнал об успехе.

Запуск каждую ночь в 03:30 — через sudo crontab -e:

30 3 * * * /usr/local/bin/backup-site.sh >> /var/log/backup-site.log 2>&1

Учтите: forget --prune удаляет старые данные из хранилища, поэтому ключу на сервере нужны права на удаление. Это ещё одна причина включить версионирование и Object Lock: даже если взломщик воспользуется ключом, заблокированные версии останутся. Для большей защиты ротацию можно запускать с отдельной машины другим ключом, а серверу дать ключ только на запись.

Если сайт на Laravel или WordPress

  • Laravel. Пакет spatie/laravel-backup делает дамп базы и архив файлов, отправляет их на любой диск Laravel (в том числе S3), чистит старые копии и присылает уведомления о проблемах. Запуск — через планировщик Laravel (backup:run, backup:clean, backup:monitor). Учтите: шифрование архива нужно включить отдельно, паролем в настройках пакета.
  • WordPress. Плагины вроде UpdraftPlus умеют отправлять копии в S3 по расписанию (произвольное S3-совместимое хранилище — в платной версии). На хостинге без SSH это самый простой вариант. Но плагин работает внутри взламываемого сайта — поэтому оставьте и копии хостинга, и версионирование в бакете.

Мониторинг: бэкап, который молча сломался

Самая частая история — бэкапы «настроены», но уже три месяца падают из-за смены пароля или переполненного диска. Чтобы этого не случилось:

  • используйте «сигнал жизни» (dead man's switch): скрипт после успеха отправляет запрос в сервис вроде Healthchecks.io или Uptime Kuma, а тот пишет вам в Telegram или на почту, если сигнала нет в ожидаемое время. Сообщения об ошибках не спасают, когда cron вообще не запустился;
  • раз в неделю запускайте restic check — он проверяет целостность репозитория;
  • следите за размером копий: резкое уменьшение — признак того, что что-то перестало попадать в бэкап.

Проверка восстановления

Бэкап, из которого ни разу не восстанавливались, — это надежда, а не бэкап. Раз в месяц или раз в квартал:

  1. Достаньте последнюю копию:
    source /root/.backup-env
    restic restore latest --tag my-site --target /tmp/restore-test
    
  2. Разверните дамп в отдельную тестовую базу, не в рабочую:
    mysql -e 'CREATE DATABASE restore_test'
    gunzip < /tmp/restore-test/var/backups/db/my_database.sql.gz | mysql restore_test
    
  3. Проверьте, что данные свежие: последние записи, количество строк в главных таблицах, открываются ли загруженные файлы.
  4. Запишите, сколько времени заняло восстановление, и коротко опишите шаги в документации проекта. В момент аварии эта инструкция сэкономит часы.

Типичные ошибки

  • Бэкапы лежат на том же сервере или у того же провайдера, что и сайт.
  • На сервере ключ от основного аккаунта хранилища, а версионирование выключено — взломщик удалит и сайт, и все копии.
  • Нет проверки восстановления: бэкап оказывается пустым или битым в самый нужный момент.
  • Хранится только последняя копия, и порча данных, замеченная через неделю, уже в ней.
  • Ключ шифрования есть только на сервере, который пропал.
  • В бэкап попадают vendor/ и кэш, а загрузки пользователей — нет.
  • Пароль базы передаётся в командной строке, а архив с .env лежит в публичной папке сайта.

Чек-лист

  1. Определите, что копировать: база, загрузки, конфиги; код — в Git.
  2. Оставьте бэкапы хостинга как первый уровень.
  3. Заведите S3 у другого провайдера: отдельный приватный бакет, версионирование, Object Lock, ключ с доступом только к этому бакету.
  4. Делайте дамп базы (mysqldump --single-transaction или pg_dump) отдельным пользователем с правами на чтение.
  5. Шифруйте копии до отправки (restic) и храните пароль шифрования в менеджере паролей.
  6. Автоматизируйте через cron и ротацию 7 / 4 / 12.
  7. Подключите мониторинг «сигнала жизни» и restic check.
  8. Регулярно проверяйте восстановление на тестовой базе и записывайте шаги.