Бэкап баз данных и файлов в S3 с помощью Restic + Healthchecks
В этой статье разберём практический и надёжный способ бэкапа:
- баз данных (PostgreSQL / MariaDB)
- конфигурационных и пользовательских файлов
- с хранением в S3 / MinIO
- с шифрованием
- с уведомлениями о сбоях через Healthchecks
Решение подходит для:
- одного сервера
- Docker / Docker Compose
- cron или systemd
- продакшена
Почему Restic, а не rclone sync
rclone sync — это зеркалирование, а не бэкап.
|
Возможность |
Restic |
rclone sync |
|---|---|---|
|
Версии файлов |
✅ |
❌ |
|
Защита от удаления |
✅ |
❌ |
|
Защита от ransomware |
✅ |
❌ |
|
Шифрование |
✅ |
⚠️ вручную |
|
Retention |
✅ |
❌ |
Вывод:
Restic — это backup-система, rclone — транспорт.
Архитектура решения
PostgreSQL / MariaDB
↓ (dump)
Файл дампа
↓
Restic
↓
S3 / MinIO
↓
Healthchecks (OK / FAIL)Что мы будем бэкапить
- База данных (через dump)
- Файлы и конфиги
- Хранение в S3
- Уведомления о статусе
1 Установка Restic
Ubuntu / Debian
apt update
apt install -y resticПроверка:
restic version2 Подготовка пароля Restic
Пароль не хранится в открытом виде — только в файле.
mkdir -p /root/.config/restic
openssl rand -base64 48 > /root/.restic-pass
chmod 600 /root/.restic-passПотеря пароля = потеря всех бэкапов.
3 Настройка доступа к S3 / MinIO
Создаём env-файл:
/root/.config/restic/backup.env
RESTIC_PASSWORD_FILE=/root/.restic-pass
RESTIC_REPOSITORY=s3:https://minio.example.com/backups/project-name
AWS_ACCESS_KEY_ID=XXXXXXXX
AWS_SECRET_ACCESS_KEY=YYYYYYYY
AWS_DEFAULT_REGION=us-east-1chmod 600 /root/.config/restic/backup.env4 Инициализация репозитория (1 раз)
set -a
source /root/.config/restic/backup.env
set +a
restic init5 Healthchecks (опционально, но очень рекомендовано)
Создаём чек на https://healthchecks.io
Получаем URL вида:
https://healthchecks.example.com/ping/UUID- Успех → обычный URL
- Ошибка → /ping/UUID/1
6 Полный скрипт бэкапа (PostgreSQL + Restic + Healthchecks)
Путь: /opt/project/backup.sh
#!/usr/bin/env bash
set -euo pipefail
### CONFIG
BASE_DIR="/opt/project"
RESTIC_ENV="/root/.config/restic/backup.env"
COMPOSE_FILE="${BASE_DIR}/docker-compose.yml"
COMPOSE_ENV="${BASE_DIR}/.env"
DB_SERVICE="db"
BACKUP_DIR="/var/backups/project"
KEEP_LOCAL_DAYS=7
HC_URL="https://healthchecks.example.com/ping/UUID"
### ERROR HANDLER
fail() {
curl -fsS --retry 3 --max-time 10 "${HC_URL}/1" >/dev/null 2>&1 || true
}
trap fail ERR
### LOAD RESTIC ENV
set -a
source "$RESTIC_ENV"
set +a
### READ DB CREDS SAFELY
env_get() {
grep -E "^$1=" "$2" | tail -n1 | cut -d= -f2- | tr -d '"'
}
POSTGRES_USER="$(env_get POSTGRES_USER "$COMPOSE_ENV")"
POSTGRES_PASSWORD="$(env_get POSTGRES_PASSWORD "$COMPOSE_ENV")"
POSTGRES_DB="$(env_get POSTGRES_DB "$COMPOSE_ENV")"
mkdir -p "$BACKUP_DIR"
chmod 700 "$BACKUP_DIR"
DATE="$(date +%F_%H%M%S)"
HOST="$(hostname -s)"
DUMP_FILE="${BACKUP_DIR}/postgres_${POSTGRES_DB}_${HOST}_${DATE}.dump"
### DATABASE DUMP
docker compose -f "$COMPOSE_FILE" --env-file "$COMPOSE_ENV" exec -T \
-e PGPASSWORD="$POSTGRES_PASSWORD" \
"$DB_SERVICE" \
sh -lc "pg_dump -U '$POSTGRES_USER' -d '$POSTGRES_DB' -Fc --no-owner --no-acl" \
> "$DUMP_FILE"
### RESTIC BACKUP (относительные пути)
(
cd "$BACKUP_DIR"
restic backup . \
--tag project \
--tag postgres \
--tag prod \
--tag "$HOST"
)
### RETENTION
restic forget \
--keep-within 7d \
--keep-daily 14 \
--keep-weekly 8 \
--keep-monthly 12 \
--prune
### LOCAL CLEANUP
find "$BACKUP_DIR" -type f -mtime +"$KEEP_LOCAL_DAYS" -delete
### SUCCESS
curl -fsS --retry 3 --max-time 10 "$HC_URL" >/dev/null
echo "[OK] backup successful"chmod 700 /opt/project/backup.sh7 Cron: запуск каждые 6 часов
0 */6 * * * root cd /opt/project && ./backup.sh >> /var/log/project-backup.log 2>&17.1 Cron vs systemd timer: почему лучше timer и как настроить
Создаём файл:
/etc/systemd/system/backup.service
[Unit]
Description=Backup job via restic
Wants=network-online.target
After=network-online.target docker.service
Requires=docker.service
[Service]
Type=oneshot
WorkingDirectory=/opt/project
ExecStart=/opt/project/backup.sh
# не ограничиваем время выполнения
TimeoutStartSec=0
User=root
Group=root
[Install]
WantedBy=multi-user.targetTimer определяет расписание запуска.
Создаём файл:
/etc/systemd/system/backup.timer
[Unit]
Description=Run backup every 6 hours
[Timer]
# каждые 6 часов: 00:00, 06:00, 12:00, 18:00
OnCalendar=*-*-* 00,06,12,18:00
# если сервер был выключен — выполнить после старта
Persistent=true
# небольшая задержка после загрузки системы
OnBootSec=5min
[Install]
WantedBy=timers.targetАктивируем timer:
systemctl daemon-reload
systemctl enable --now backup.timerПроверяем расписание:
systemctl list-timers | grep backupЗапуск вручную (для теста):
systemctl start backup.service
systemctl status backup.serviceПросмотр логов выполнения:
journalctl -u backup.service -n 100 --no-pagerSystemd timer — более надёжная и управляемая замена cron для бэкапов.
8 Проверка состояния
Список снапшотов
restic snapshotsПосмотреть содержимое
restic ls latest9 Восстановление базы данных
restic restore latest --target /restorepg_restore -d mydb /restore/postgres_mydb_*.dumpПолитика хранения (retention)
--keep-within 7d # все бэкапы за 7 дней (каждые 6 часов)
--keep-daily 14
--keep-weekly 8
--keep-monthly 12Это идеальный баланс между:
- детальностью
- экономией места
- возможностью отката
Итог
✔ шифрованные бэкапы
✔ S3 / MinIO
✔ дедупликация
✔ восстановление на любую дату
✔ мониторинг через healthchecks
✔ готово для продакшена
Что можно добавить дальше
- второй off-site S3 (3-2-1+)
- immutable bucket / object lock
- systemd timer вместо cron
- авто-тест восстановления
- бэкап volume’ов и конфигов
Restic — один из самых надёжных способов бэкапа сегодня.
Если вы используете Docker и S3 — это практически идеальный вариант.