Привет, это Вася из Fastfox. Сегодня соберём практичную схему резервного копирования для Debian и Ubuntu на базе restic: без лишней магии, но с теми деталями, из-за которых бэкапы действительно восстанавливаются, а не просто «где-то лежат». Статья рассчитана на админов, devops-инженеров и вебмастеров, которые держат сайты, базы, конфиги и пользовательские загрузки на VDS или обычном Linux-сервере.
Ключевая идея простая: restic делает дедуплицированные, шифрованные снапшоты, а команды forget и prune управляют сроками хранения и очисткой неиспользуемых данных. Добавим к этому systemd timer, блокировку от параллельного запуска, проверку репозитория и понятные логи — получится production-friendly backup, который можно поддерживать годами.
Главное правило бэкапов: пока вы не проверили восстановление, у вас есть не бэкап, а надежда.
Что умеет restic и где он особенно удобен
restic хорош там, где нужно регулярно сохранять файловые данные: директории сайтов, конфиги из /etc, дампы баз, каталоги приложений, домашние директории пользователей, артефакты CI/CD. Он шифрует данные на стороне клиента, хранит их в репозитории и переиспользует одинаковые блоки между снапшотами. Поэтому ежедневные копии большого сайта обычно занимают не «размер сайта умножить на количество дней», а существенно меньше.
Для Debian/Ubuntu restic удобен ещё и тем, что ставится из репозиториев дистрибутива, не требует демона и хорошо дружит с systemd. Запуск можно описать обычным unit-файлом, а расписание — timer-файлом. В логах journalctl будет видно, что именно случилось: сколько файлов изменилось, сколько данных добавлено, когда начался prune, была ли ошибка доступа к репозиторию.
Важно понимать границы инструмента. restic отлично сохраняет файлы, но не делает сам по себе согласованные дампы работающих баз данных. Для MySQL/MariaDB, PostgreSQL, Redis и других сервисов сначала готовят корректный дамп или снапшот, а уже потом складывают результат в restic-репозиторий. Если просто бэкапить «живой» каталог БД, можно получить набор файлов, который не поднимется после восстановления.
Подготовка: каталоги, пакет restic и секреты
В небольших установках backup job часто запускают от root, потому что нужно прочитать /etc, сайты, дампы и данные нескольких пользователей. Это допустимо, но пароль репозитория и доступ к хранилищу нужно защищать особенно внимательно. Если инфраструктура крупнее, заведите отдельного системного пользователя backup и выдавайте права точечно через группы, ACL или ограниченный sudo.
sudo apt update
sudo apt install restic jq
sudo install -d -m 0750 -o root -g root /etc/restic
sudo install -d -m 0750 -o root -g root /var/backups/restic-work
sudo install -d -m 0750 -o root -g root /var/log/restic
sudo install -d -m 0750 -o root -g root /var/cache/restic
Пароль репозитория храните в отдельном файле. Это не пароль от сервера, а ключ к расшифровке всех бэкапов. Потеряете его — восстановление станет невозможным. Утечёт — злоумышленник сможет читать архивы, если получит доступ к репозиторию.
sudo sh -c 'umask 077; printf "%s\n" "CHANGE_ME_LONG_RANDOM_PASSWORD" > /etc/restic/password'
В реальной среде сгенерируйте длинный случайный секрет и сохраните его в менеджере паролей или защищённом хранилище секретов. Не кладите файл /etc/restic/password в Git, не отправляйте его в чаты и не добавляйте в дампы, которые затем попадут в тот же backup-репозиторий.

Репозиторий: локальный пример и правильная production-логика
Для примера покажу локальный репозиторий в /srv/restic-repo, потому что его удобно повторить на тестовой машине. Но для реальной защиты от аварии диска или ошибки администратора репозиторий должен находиться отдельно от исходных данных: на другом сервере, отдельном диске, удалённом хранилище или backup-узле. Бэкап на тот же диск лучше, чем ничего, но от удаления сервера, поломки файловой системы или компрометации root он спасает плохо.
sudo install -d -m 0750 -o root -g root /srv/restic-repo
sudo RESTIC_PASSWORD_FILE=/etc/restic/password restic -r /srv/restic-repo init
Если репозиторий уже инициализирован, повторный init завершится ошибкой. Это нормально: инициализация делается один раз. Дальше все команды будут использовать одни и те же переменные окружения.
sudo tee /etc/restic/restic.env > /dev/null <<'EOF'
RESTIC_REPOSITORY=/srv/restic-repo
RESTIC_PASSWORD_FILE=/etc/restic/password
RESTIC_CACHE_DIR=/var/cache/restic
EOF
sudo chmod 0600 /etc/restic/restic.env
Для удалённого сценария вместо локального пути будет другой адрес репозитория. Сама логика не меняется: сначала инициализация, затем backup, затем forget, prune, check и автоматизация. Если хотите глубже разобрать объектное хранилище и альтернативы, посмотрите материал про бэкапы в S3-совместимое хранилище через restic и borg.
Что именно бэкапить на типичном сервере
Состав набора зависит от роли сервера. Для веб-сервера чаще всего нужны конфиги, сайты, дампы баз, конфиги приложений, systemd unit-файлы и иногда пользовательские загрузки. Системные кэши, временные каталоги, сокеты, PID-файлы и директории виртуальных окружений обычно не нужны: они увеличивают объём и шумят при восстановлении.
/etc— конфигурация системы и сервисов;/var/www— сайты, релизы, пользовательские файлы;/opt— вручную установленные приложения;/var/backups/restic-work— подготовленные дампы баз;/root— только если там действительно есть важные скрипты и ключи.
Исключения стоит описать отдельным файлом. Так проще поддерживать конфигурацию и не раздувать команду запуска.
sudo tee /etc/restic/excludes.txt > /dev/null <<'EOF'
/var/cache
/var/tmp
/tmp
/var/log
/var/lib/mysql
/var/lib/postgresql
/var/www/*/cache
/var/www/*/storage/framework/cache
/var/www/*/node_modules
/var/www/*/vendor
EOF
sudo chmod 0640 /etc/restic/excludes.txt
Обратите внимание на исключение каталогов баз данных. Это не значит, что базы не бэкапятся. Это значит, что мы не копируем их файловые директории как обычные файлы, а предварительно делаем дампы в рабочий каталог.
Дампы баз перед backup
Для MySQL/MariaDB простой вариант — использовать mariadb-dump или mysqldump. Для небольших баз этого достаточно. Для больших проектов лучше рассмотреть PITR, репликацию или физические бэкапы. По PostgreSQL у нас отдельно разобрана тема PITR-восстановления через WAL, а для MySQL/MariaDB — binlog и GTID для point-in-time recovery.
sudo tee /usr/local/sbin/restic-pre-backup > /dev/null <<'EOF'
#!/bin/sh
set -eu
umask 077
WORKDIR=/var/backups/restic-work
mkdir -p "$WORKDIR"
rm -f "$WORKDIR"/*.sql.gz
if command -v mariadb-dump > /dev/null 2>&1; then
mariadb-dump --all-databases --single-transaction --routines --events | gzip -9 > "$WORKDIR/mysql-all.sql.gz"
elif command -v mysqldump > /dev/null 2>&1; then
mysqldump --all-databases --single-transaction --routines --events | gzip -9 > "$WORKDIR/mysql-all.sql.gz"
fi
if command -v pg_dumpall > /dev/null 2>&1; then
sudo -u postgres pg_dumpall | gzip -9 > "$WORKDIR/postgresql-all.sql.gz"
fi
EOF
sudo chmod 0750 /usr/local/sbin/restic-pre-backup
Скрипт намеренно простой: он делает дамп, если соответствующая утилита есть в системе. В production адаптируйте его под вашу схему доступа: отдельный пользователь БД с минимальными правами, отдельные дампы по базам, контроль размера результата и уведомления при ошибке. Если дамп не создался, лучше пусть весь backup job упадёт, чем тихо сохранит неполный набор данных.
Первый backup: ручной запуск и проверка результата
Первый запуск всегда делайте вручную. Он покажет ошибки прав, неверные исключения, проблемы с репозиторием и неожиданный объём данных. Не автоматизируйте бэкап, пока ручной запуск не проходит стабильно.
sudo /usr/local/sbin/restic-pre-backup
sudo env $(cat /etc/restic/restic.env) restic backup /etc /var/www /opt /var/backups/restic-work --exclude-file /etc/restic/excludes.txt --one-file-system --tag vds --tag daily
Параметр --one-file-system защищает от случайного захода в примонтированные файловые системы. Если нужные данные лежат на отдельном разделе, укажите их явно отдельным путём или уберите этот параметр осознанно.
sudo env $(cat /etc/restic/restic.env) restic snapshots
sudo env $(cat /etc/restic/restic.env) restic check
sudo env $(cat /etc/restic/restic.env) restic check --read-data-subset=5%
restic check проверяет структуру репозитория. Проверка части данных через --read-data-subset=5% — компромисс между временем и пользой. Для небольших репозиториев иногда можно делать полную проверку --read-data, но на больших архивах это способно занять часы.
Восстановление: тестируем до аварии
До настройки расписания сделайте тестовый restore. Это дисциплинирует: вы сразу увидите, понятны ли имена снапшотов, хватает ли прав, не забыли ли вы важный каталог и не исключили ли лишнее.
sudo install -d -m 0750 -o root -g root /tmp/restic-restore-test
sudo env $(cat /etc/restic/restic.env) restic restore latest --target /tmp/restic-restore-test --include /etc/hostname
sudo find /tmp/restic-restore-test -maxdepth 4 -type f -print
sudo rm -rf /tmp/restic-restore-test
Для реальной аварии полезно заранее иметь короткий runbook: где лежит пароль, как получить доступ к репозиторию, какой командой посмотреть снапшоты, куда восстанавливать данные, как вернуть дамп БД. Когда сайт лежит, а клиенты пишут в поддержку, импровизировать тяжелее.
restic forget prune: как работает ретеншн
Команда restic forget отвечает за то, какие снапшоты считать больше не нужными. Сама по себе она может только «забыть» записи о снапшотах, но не обязательно сразу освободит место. Физическую очистку неиспользуемых блоков делает prune. Поэтому часто встречается связка forget --prune: сначала применяем политику хранения, затем вычищаем данные, на которые больше никто не ссылается.
Типичная политика для небольшого сервера: оставить 7 ежедневных копий, 5 недельных и 12 месячных, группируя снапшоты по хосту и путям.
sudo env $(cat /etc/restic/restic.env) restic forget --keep-daily 7 --keep-weekly 5 --keep-monthly 12 --group-by host,paths --prune
Параметр --prune запускает очистку сразу после forget. Это удобно, но нужно помнить: prune может быть тяжёлой операцией. На больших репозиториях она активно читает и пишет метаданные, а иногда заметно грузит диск. Не ставьте её на самое горячее время суток.
sudo env $(cat /etc/restic/restic.env) restic forget --keep-daily 7 --keep-weekly 5 --keep-monthly 12 --group-by host,paths
sudo env $(cat /etc/restic/restic.env) restic prune
Раздельный вариант удобен для диагностики: видно, какая команда упала и на каком этапе. Для регулярной автоматизации часто достаточно forget --prune, но на крупных репозиториях я предпочитаю выносить prune в отдельное окно обслуживания.
Скрипт backup job: один вход для systemd
Соберём основной скрипт. Он выполнит подготовку дампов, запустит restic backup, применит ретеншн, сделает быструю проверку и запишет лог. Добавим flock, чтобы два запуска не пересекались.
sudo tee /usr/local/sbin/restic-backup-job > /dev/null <<'EOF'
#!/bin/sh
set -eu
LOCK=/run/restic-backup.lock
LOG=/var/log/restic/backup.log
exec 9>"$LOCK"
if ! flock -n 9; then
echo "$(date -Is) another restic backup is running" >> "$LOG"
exit 0
fi
set -a
. /etc/restic/restic.env
set +a
echo "$(date -Is) pre-backup started" >> "$LOG"
/usr/local/sbin/restic-pre-backup >> "$LOG" 2>&1
echo "$(date -Is) restic backup started" >> "$LOG"
restic backup /etc /var/www /opt /var/backups/restic-work --exclude-file /etc/restic/excludes.txt --one-file-system --tag vds --tag daily >> "$LOG" 2>&1
echo "$(date -Is) restic forget and prune started" >> "$LOG"
restic forget --keep-daily 7 --keep-weekly 5 --keep-monthly 12 --group-by host,paths --prune >> "$LOG" 2>&1
echo "$(date -Is) restic check started" >> "$LOG"
restic check >> "$LOG" 2>&1
echo "$(date -Is) restic job finished" >> "$LOG"
EOF
sudo chmod 0750 /usr/local/sbin/restic-backup-job
sudo /usr/local/sbin/restic-backup-job
sudo tail -n 80 /var/log/restic/backup.log
Если команда завершилась ошибкой, не спешите создавать timer. Сначала исправьте причину. Частые проблемы: нет доступа к каталогу сайта, неверный пароль репозитория, не установлен клиент БД для дампа, удалённое хранилище недоступно, в исключениях указан не тот путь.
restic systemd timer: unit и расписание
Теперь автоматизируем запуск через systemd timer. По сравнению с cron у timer есть несколько плюсов: единые логи в journal, удобный просмотр статуса, настройка случайной задержки, запуск пропущенной задачи после простоя через Persistent.
sudo tee /etc/systemd/system/restic-backup.service > /dev/null <<'EOF'
[Unit]
Description=Restic backup job
Wants=network-online.target
After=network-online.target
[Service]
Type=oneshot
ExecStart=/usr/local/sbin/restic-backup-job
Nice=10
IOSchedulingClass=best-effort
IOSchedulingPriority=7
EOF
sudo tee /etc/systemd/system/restic-backup.timer > /dev/null <<'EOF'
[Unit]
Description=Run restic backup daily
[Timer] 03:20:00
RandomizedDelaySec=25m
Persistent=true
Unit=restic-backup.service
[Install]
WantedBy=timers.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable --now restic-backup.timer
OnCalendar задаёт запуск каждый день в 03:20, а RandomizedDelaySec добавляет случайную задержку до 25 минут. Это полезно, если у вас много однотипных серверов: они не начнут одновременно грузить backup-хранилище. Persistent=true означает, что пропущенный запуск будет выполнен после включения сервера.
systemctl list-timers restic-backup.timer
systemctl status restic-backup.timer
sudo systemctl start restic-backup.service
journalctl -u restic-backup.service -n 100 --no-pager

Когда лучше разделить backup и prune
В примере выше prune выполняется каждый день. Для маленьких репозиториев это нормально, но на больших объёмах лучше разделить частый backup и более редкое обслуживание. Например: backup каждый день, forget каждый день без очистки, а prune раз в неделю ночью. Так ежедневное окно будет короче и предсказуемее.
sudo tee /usr/local/sbin/restic-prune-job > /dev/null <<'EOF'
#!/bin/sh
set -eu
LOG=/var/log/restic/prune.log
set -a
. /etc/restic/restic.env
set +a
echo "$(date -Is) restic prune started" >> "$LOG"
restic prune >> "$LOG" 2>&1
echo "$(date -Is) restic check started" >> "$LOG"
restic check >> "$LOG" 2>&1
echo "$(date -Is) restic prune job finished" >> "$LOG"
EOF
sudo chmod 0750 /usr/local/sbin/restic-prune-job
sudo tee /etc/systemd/system/restic-prune.service > /dev/null <<'EOF'
[Unit]
Description=Restic prune job
Wants=network-online.target
After=network-online.target
[Service]
Type=oneshot
ExecStart=/usr/local/sbin/restic-prune-job
Nice=10
IOSchedulingClass=best-effort
IOSchedulingPriority=7
EOF
sudo tee /etc/systemd/system/restic-prune.timer > /dev/null <<'EOF'
[Unit]
Description=Run restic prune weekly
[Timer] 04:30:00
RandomizedDelaySec=40m
Persistent=true
Unit=restic-prune.service
[Install]
WantedBy=timers.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable --now restic-prune.timer
Такой подход особенно полезен, если репозиторий находится на удалённом хранилище, где операции чтения, записи или листинга стоят денег или занимают заметное время. prune освобождает место, но это не бесплатная операция с точки зрения IO.
Мониторинг и уведомления
Бэкап без мониторинга ломается тихо. Сегодня закончилось место, завтра поменяли пароль БД, послезавтра удалённое хранилище стало недоступно — и через месяц выясняется, что последняя рабочая копия слишком старая. Минимальный контроль должен отвечать на три вопроса: когда был последний успешный запуск, сколько длился бэкап и можно ли восстановить данные.
systemctl status restic-backup.service
systemctl list-timers restic-backup.timer restic-prune.timer
journalctl -u restic-backup.service --since today --no-pager
sudo env $(cat /etc/restic/restic.env) restic snapshots --json | jq '.[-1] | {time, hostname, paths, tags}'
Для полноценной эксплуатации добавьте уведомление о неуспешном unit. Это может быть локальный скрипт, отправка сообщения в вашу систему мониторинга или метрика для Prometheus/node_exporter textfile collector. Важно не конкретное средство, а факт: ошибка backup job должна попадать в тот же канал, где вы видите падение сайта, нехватку диска и проблемы с БД.
Безопасность репозитория и доступов
У restic есть сильное преимущество: данные шифруются до отправки в репозиторий. Но это не отменяет базовой модели угроз. Если атакующий получил root на сервере, он может прочитать пароль restic и удалить удалённый репозиторий, если у сервера есть права на удаление. Поэтому для ценных данных стоит думать не только о шифровании, но и о правах доступа к хранилищу.
- храните пароль restic вне репозитория и дублируйте его в безопасном месте;
- ограничивайте права ключей доступа к удалённому хранилищу;
- по возможности используйте версионирование или защиту от удаления на стороне хранилища;
- не держите единственную копию бэкапа на том же сервере, где лежат исходные данные;
- периодически проверяйте восстановление на отдельной машине.
Отдельно проверьте, что в бэкап не попадают лишние секреты: временные дампы, старые архивы, приватные ключи, которые уже не используются, файлы окружения тестовых приложений. Иногда бэкап становится самым полным архивом всех ошибок эксплуатации. Это нормально, если он хорошо защищён, но плохо, если доступ к нему шире, чем к production-серверу.
Типичные ошибки
Бэкап есть, дампа базы нет
Самая распространённая проблема — сохранить код сайта и забыть про базу. WordPress, Laravel, Django, CRM, интернет-магазины и панели управления почти всегда хранят критичные данные в БД. Проверьте, что дамп создаётся, имеет ненулевой размер и попадает в снапшот.
sudo ls -lh /var/backups/restic-work
sudo env $(cat /etc/restic/restic.env) restic ls latest /var/backups/restic-work
Prune запускается слишком часто
Если во время prune растёт iowait, увеличиваются задержки сайта или база начинает отвечать медленнее, вынесите очистку в отдельный weekly timer. Также можно перенести время запуска на период минимальной нагрузки и снизить IO-приоритет unit.
Restore ни разу не проверяли
Команда restic check полезна, но она не заменяет восстановление. Хотя бы раз в месяц восстанавливайте один сайт или один дамп БД в тестовый каталог. Для критичных проектов лучше автоматизировать restore-проверку на отдельном стенде.
Мини-чеклист готовой настройки
resticустановлен, репозиторий инициализирован, пароль сохранён безопасно.- Список путей и файл исключений соответствуют реальной структуре сервера.
- Базы данных сохраняются через дампы или другой согласованный механизм.
- Ручной
backup,snapshots,checkи тестовыйrestoreпрошли успешно. - Политика
restic forget pruneпонятна: сколько daily, weekly и monthly копий хранится. restic systemd timerвключён, виден вsystemctl list-timers, логи доступны черезjournalctl.- Есть уведомление об ошибках и регулярная проверка восстановления.
Итоги
Хороший debian ubuntu restic backup — это не одна команда, а небольшой процесс: подготовить данные, сохранить их в шифрованный репозиторий, применить понятный ретеншн, очистить старые блоки через prune, проверить целостность и регулярно тестировать восстановление. restic закрывает техническую часть очень элегантно, но дисциплина эксплуатации остаётся за нами.
Если внедряете схему на VDS, начните с простого варианта: ежедневный backup, хранение 7 daily, 5 weekly и 12 monthly, проверка restic check после запуска и тестовый restore раз в месяц. Когда объёмы вырастут, разделите backup и prune, добавьте мониторинг длительности, вынесите репозиторий на отдельное хранилище и документируйте процедуру восстановления. Именно так бэкап из «галочки в списке задач» превращается в рабочий инструмент аварийного восстановления.


