Шаг 7 · Данные · 35 минут

Бэкапы без самообмана: создаём и восстанавливаем

Архив на том же диске исчезнет вместе с VPS. Поэтому сделаем локальную ежедневную копию для скорости, отправим её во внешнее хранилище и обязательно проверим восстановление.

Что считать хорошим бэкапом

Правило 3‑2‑1: три экземпляра данных, два разных типа хранения, одна копия вне основного сервера. Копируем только то, что нельзя получить заново: базу, загрузки пользователей, конфигурацию и секреты в защищённом виде. Код уже есть в Git, образ можно пересобрать.

1. Создаём закрытую папку

sudo mkdir -p /var/backups/ratehost
sudo chown deploy:deploy /var/backups/ratehost
chmod 700 /var/backups/ratehost

Права 700 дают доступ только владельцу.

2. Архивируем файлы

tar -czf /var/backups/ratehost/files-$(date +%F-%H%M).tar.gz \
  /home/deploy/app/uploads \
  /home/deploy/app/compose.yaml \
  /etc/nginx/sites-available/app

tar собирает файлы, z сжимает gzip, $(date ...) добавляет дату к имени. Предупреждение про удаление начального слеша нормально.

3. Сохраняем PostgreSQL

Копировать живые файлы базы обычным архивом нельзя: можно получить несогласованное состояние. Используем pg_dump через контейнер:

cd /home/deploy/app
docker compose exec -T db pg_dump -U app app | gzip \
  > /var/backups/ratehost/db-$(date +%F-%H%M).sql.gz

Первое app после -U — имя пользователя БД, второе — имя базы. Замените их значениями вашего Compose.

4. Собираем сценарий

sudo nano /usr/local/sbin/backup-ratehost
#!/usr/bin/env bash
set -euo pipefail

DEST=/var/backups/ratehost
STAMP=$(date +%F-%H%M)
mkdir -p "$DEST"

tar -czf "$DEST/files-$STAMP.tar.gz" /home/deploy/app/uploads
cd /home/deploy/app
docker compose exec -T db pg_dump -U app app | gzip > "$DEST/db-$STAMP.sql.gz"

find "$DEST" -type f -mtime +7 -delete
echo "Backup completed: $STAMP"
sudo chmod 700 /usr/local/sbin/backup-ratehost
sudo /usr/local/sbin/backup-ratehost
ls -lh /var/backups/ratehost

set -euo pipefail останавливает сценарий при ошибке вместо создания ложного «успешного» бэкапа.

5. Запускаем по расписанию

sudo crontab -e

Добавьте ежедневный запуск в 03:30:

30 3 * * * /usr/local/sbin/backup-ratehost >> /var/log/backup-ratehost.log 2>&1

Cron использует время сервера. На следующий день проверяйте не только наличие файла, но и журнал.

6. Уносим копию с VPS

Подключите S3‑совместимое хранилище через rclone и добавьте после создания файлов команду копирования. Ключу выдайте доступ только к отдельному bucket бэкапов. Включите версионирование или защиту от удаления, если хранилище это поддерживает.

7. Репетируем восстановление

gzip -t /var/backups/ratehost/db-ФАЙЛ.sql.gz
tar -tzf /var/backups/ratehost/files-ФАЙЛ.tar.gz | head

Это проверяет целостность, но не содержание. Настоящий тест базы:

createdb app_restore
gunzip -c db-ФАЙЛ.sql.gz | psql -d app_restore

В Docker восстановите дамп в отдельную тестовую базу, не поверх рабочей. Откройте несколько ключевых записей и только затем удаляйте тест.

Что обычно забывают

  • бэкап создаётся, но никогда не покидает VPS;
  • скрипт падает, а никто не читает журнал;
  • копируется база «как папка» во время записи;
  • есть архив, но нет инструкции восстановления;
  • ключ от хранилища лежит в публичном репозитории.
Бэкап считается рабочим не после создания файла, а после успешного тестового восстановления.

Что дальше

Осталось вовремя узнавать о сбоях: настроим мониторинг и алерты →