MHOSTMHOST

Инкрементальные бэкапы с restic

restic — консольная программа для резервного копирования, которая шифрует данные ещё на стороне клиента, хранит их дедуплицированно и после первого полного снимка загружает только изменения. В этой статье вы установите restic на VPS mHost, создадите репозиторий (на локальном диске, на другом сервере по SFTP или в S3), сделаете и восстановите бэкап, а также настроите политику хранения, чтобы старые снимки не копились бесконечно.

Что понадобится

  • VPS mHost на Ubuntu 22.04/24.04, Debian 11/12 или AlmaLinux/Rocky Linux 8/9.
  • Доступ по SSH под root или пользователем с sudo.
  • Куда складывать бэкапы — один из вариантов шага 3: смонтированный отдельный диск на этом же сервере, второй сервер (например, ещё один VPS mHost) с доступом по SSH, либо bucket в S3 или S3-совместимом хранилище с ключами доступа.
  • Данные, которые нужно защитить, — в примерах ниже это /etc и /var/www; замените на свои пути.
Совет: команды ниже даны от имени root — бэкап /etc в любом случае требует root (там есть файлы, недоступные на чтение обычным пользователям), а задание cron из последнего шага тоже будет выполняться от root. Если работаете под пользователем с sudo — проще один раз выполнить sudo -i и дальше идти по статье как есть: экспортированные переменные окружения (шаги 2–3) не передаются в команду через отдельный sudo без флага -E.

Как это устроено: шифрование, дедупликация, инкрементальность

  • Шифрование. restic шифрует данные ещё на клиенте, до отправки в репозиторий, — AES-256 в режиме счётчика (CTR) плюс Poly1305-AES в качестве MAC для проверки целостности. Даже если репозиторий лежит на чужом сервере или в публичном облаке, без пароля прочитать его содержимое не получится.
  • Дедупликация. Файлы режутся на фрагменты переменного размера (content-defined chunking, по отпечаткам Rabin) — обычно от 512 КиБ до 8 МиБ, в среднем около 1 МиБ; файлы меньше 512 КиБ не режутся вовсе. Каждый фрагмент адресуется по хешу SHA-256, и одинаковые фрагменты — даже из разных файлов и разных снимков — физически хранятся в репозитории только один раз.
  • Инкрементальность. При каждом новом restic backup restic читает файлы заново, но благодаря дедупликации загружает в репозиторий только те фрагменты, которых там ещё нет, — фактически только изменения. При этом каждый снимок (snapshot) в списке выглядит как полноценный самостоятельный бэкап: чтобы восстановиться, не нужно искать «полный» бэкап и вручную накатывать на него цепочку инкрементов, как в классических схемах full+incremental, — берёте любой снимок и восстанавливаетесь прямо из него.

Шаг 1. Установите restic

Готового рецепта VMmanager 6 для restic нет (в отличие, например, от Zabbix или LEMP — полный список того, что ставится в один клик, в статье «Готовые рецепты VMmanager 6») — ставится restic одной командой из штатных репозиториев.

Ubuntu / Debian

bash
apt update
apt install -y restic

AlmaLinux / Rocky Linux

Пакет restic не входит в базовые репозитории — подключите EPEL:

bash
dnf install -y epel-release
dnf install -y restic

Проверьте версию:

bash
restic version
Совет: пакет из репозитория дистрибутива может отставать от самой свежей версии restic. Если нужна именно последняя версия — скачайте бинарник со страницы релизов github.com/restic/restic/releases, а дальше обновляйте его командой restic self-update (она сама проверяет GPG-подпись и подставляет свежий бинарник).

Шаг 2. Задайте пароль репозитория

Пароль репозитория — это то, из чего restic выводит ключ шифрования. Сам restic пароль нигде в открытом виде не хранит и восстановить его не может — только вы.

Сгенерируйте случайный пароль и сохраните его в файл, доступный на чтение только root:

bash
mkdir -p /etc/restic
openssl rand -base64 48 > /etc/restic/password.txt
chmod 600 /etc/restic/password.txt

Пароль можно передавать restic тремя способами:

  • RESTIC_PASSWORD — сам пароль прямо в переменной окружения (виден в выводе ps и истории команд — для сервера не лучший вариант);
  • RESTIC_PASSWORD_FILE (или флаг --password-file) — путь к файлу с паролем, как созданный выше;
  • RESTIC_PASSWORD_COMMAND (или флаг --password-command) — команда, которая выводит пароль в stdout, например обращение к менеджеру паролей.

Экспортируйте переменную на файл — дальше по статье restic будет брать пароль из него и не станет спрашивать интерактивно:

bash
export RESTIC_PASSWORD_FILE=/etc/restic/password.txt
Важно: потерянный пароль репозитория означает потерянные бэкапы — обхода или сброса пароля у restic не предусмотрено. Держите его копию отдельно от этого VPS (например, в менеджере паролей) — если сервер выйдет из строя, копия внутри него самого (/etc/restic/password.txt) пропадёт вместе с сервером.

Шаг 3. Создайте репозиторий (restic init)

Репозиторий — это то место, куда restic пишет зашифрованные и дедуплицированные данные. restic поддерживает и другие бэкенды (Backblaze B2, Azure Blob Storage, Google Cloud Storage, rclone, REST-сервер и другие) — здесь разберём три варианта, которые чаще всего нужны на VPS. Выберите один.

Локальный диск

Подходит для отдельного смонтированного диска на этом же сервере — например, для быстрого локального восстановления. Как единственная защита не годится: при потере или аварии самого сервера пропадёт и репозиторий вместе с ним — для надёжной защиты нужна ещё и копия за пределами этого сервера (SFTP или S3, см. варианты ниже).

bash
mkdir -p /mnt/backup/restic-repo
export RESTIC_REPOSITORY=/mnt/backup/restic-repo
restic init

По SFTP на другой сервер

Понадобится второй сервер (например, ещё один VPS mHost в другой локации) с SSH и беспарольным входом по ключу — без этого автоматический бэкап по расписанию будет останавливаться на запросе пароля. Пользователь resticbackup ниже — обычный пользователь на сервере-приёмнике (подойдёт и root, но отдельный пользователь безопаснее); создайте его заранее, если такого ещё нет.

Сгенерируйте отдельный ключ и скопируйте его на сервер-приёмник:

bash
ssh-keygen -t ed25519 -f ~/.ssh/restic_backup -N ""
ssh-copy-id -i ~/.ssh/restic_backup.pub resticbackup@backup.example.com

Добавьте настройки для этого хоста в ~/.ssh/config — restic использует обычный SSH, поэтому подхватит их сам:

bash
cat >> ~/.ssh/config <<'EOF'
Host backup.example.com
    IdentityFile ~/.ssh/restic_backup
    ServerAliveInterval 60
    ServerAliveCountMax 240
EOF
Совет: ServerAliveInterval/ServerAliveCountMax не дают SSH разорвать соединение по тайм-ауту, пока restic долго обрабатывает большой объём данных, ничего не отправляя по сети.

Создайте репозиторий:

bash
export RESTIC_REPOSITORY="sftp:resticbackup@backup.example.com:/srv/restic-repo"
restic init
Важно: на сервере-приёмнике должен быть открыт порт SSH (обычно 22) в файрволе — иначе restic не подключится. Как открыть порт — в статье «Настройка файрвола UFW».
Совет: SFTP-серверы не разворачивают ~ как домашнюю директорию пользователя — если путь в RESTIC_REPOSITORY не начинается с /, restic будет считать его относительным к домашней папке пользователя resticbackup на сервере-приёмнике.

В S3 или S3-совместимом хранилище

Подойдёт Amazon S3 или любое S3-совместимое object storage — синтаксис отличается только URL.

Задайте ключи доступа:

bash
export AWS_ACCESS_KEY_ID=<MY_ACCESS_KEY>
export AWS_SECRET_ACCESS_KEY=<MY_SECRET_ACCESS_KEY>

Amazon S3:

bash
export RESTIC_REPOSITORY="s3:s3.us-east-1.amazonaws.com/mhost-backups"
restic init

Другое S3-совместимое хранилище — вместо адреса AWS укажите endpoint провайдера:

bash
export RESTIC_REPOSITORY="s3:https://s3.example-provider.com/mhost-backups"
restic init
Совет: для Amazon S3 restic создаст указанный bucket сам, если его ещё нет. Не все S3-совместимые провайдеры это умеют (или требуют для этого отдельных прав) — надёжнее создать bucket заранее в панели провайдера. Если у провайдера свой регион по умолчанию — укажите его через AWS_DEFAULT_REGION или флагом -o s3.region="ваш-регион".

После того как репозиторий создан, сохраните обе переменные в файл — он понадобится в новых SSH-сессиях и в шаге 8 для cron:

bash
cat > /etc/restic/env <<'EOF'
export RESTIC_REPOSITORY="sftp:resticbackup@backup.example.com:/srv/restic-repo"
export RESTIC_PASSWORD_FILE=/etc/restic/password.txt
EOF
chmod 600 /etc/restic/env
Совет: подставьте в RESTIC_REPOSITORY тот адрес, который выбрали выше (локальный путь, sftp:... или s3:...). В новой SSH-сессии достаточно source /etc/restic/env — и команды restic ниже снова работают без флага -r и без запроса пароля.
Готово: restic init без ошибок сообщает created restic repository ... at ... — репозиторий создан и готов принимать бэкапы.

Шаг 4. Сделайте первый бэкап (backup)

Список исключений — файлы и папки, которые бэкапить не нужно (логи, временные файлы и подобное). Создайте /etc/restic/excludes.txt:

bash
cat > /etc/restic/excludes.txt <<'EOF'
*.log
*/node_modules
*/.git
EOF

Запустите первый бэкап — например, конфигурации сервера и файлов сайта:

bash
restic backup --tag daily --exclude-file=/etc/restic/excludes.txt /etc /var/www

restic обойдёт указанные пути, зашифрует данные и загрузит их в репозиторий, а в конце покажет сводку: сколько файлов новых/изменённых/без изменений, сколько данных добавлено в репозиторий (Added to the repository: ...) и ID нового снимка.

Совет: отдельный путь или маску можно исключить прямо флагом, без файла, — --exclude="*.tmp" (флаг можно повторять). --tag тоже можно указывать несколько раз, если нужно больше одной метки.

Запустите ту же команду ещё раз, не меняя файлы:

bash
restic backup --tag daily --exclude-file=/etc/restic/excludes.txt /etc /var/www

Во второй раз restic всё равно читает все файлы заново — иначе не понять, что изменилось, — но благодаря дедупликации почти нечего загружать: в сводке будет Added to the repository: 0 B (0 B stored). Это и есть инкрементальность на практике.

Готово: снимок создан. Дальше — как посмотреть список снимков и восстановиться из них.

Шаг 5. Посмотрите список снимков (snapshots)

bash
restic snapshots

Команда выведет таблицу по всем снимкам в репозитории: короткий ID снимка, дата и время создания, хост, теги (если задавали --tag) и список путей, которые вошли в снимок. ID снимка (первых нескольких символов достаточно, если они уникальны в репозитории) понадобится в следующем шаге для восстановления конкретной версии.

Шаг 6. Восстановите данные (restore)

Восстановить можно последний снимок:

bash
restic restore latest --target /tmp/restore

Или конкретный — по ID из restic snapshots:

bash
restic snapshots
restic restore 79766175 --target /tmp/restore

restic не перезаписывает файлы на месте — он воссоздаёт полный исходный путь внутри --target. Например, если бэкапили /etc, после восстановления с --target /tmp/restore конфиги окажутся в /tmp/restore/etc/.... Сравните восстановленные файлы с текущими и скопируйте нужное в боевое расположение вручную — так вы не затрёте случайно рабочую конфигурацию неудачным восстановлением.

Восстановить только часть снимка — например, один каталог — можно флагом --include:

bash
restic restore latest --target /tmp/restore --include /etc/nginx
Готово: файлы появились в --target — восстановление отработало. Если восстанавливаете на другой сервер (например, при переезде), скопируйте их оттуда на постоянное место обычным cp/rsync.

Шаг 7. Ретеншн-политика: forget --prune

Без политики хранения каждый бэкап добавляет новый снимок, и список снимков растёт бесконечно. Команда forget убирает старые снимки по заданным правилам, но физически освобождает место, занятое больше не нужными данными, только prune — отдельной командой либо сразу, если добавить к forget флаг --prune.

Сначала проверьте, что политика удалит именно то, что вы ожидаете, — с --dry-run ничего не удаляется по-настоящему:

bash
restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --dry-run

Если результат ожидаемый, выполните по-настоящему — сразу с --prune, чтобы освободить место, занятое удалёнными снимками:

bash
restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --prune
  • --keep-daily 7 — хранить по одному снимку за последние 7 дней, в которые был бэкап;
  • --keep-weekly 4 — плюс по одному снимку за 4 предыдущие недели;
  • --keep-monthly 6 — плюс по одному снимку за 6 предыдущих месяцев.

Снимок сохраняется, если подходит хотя бы под одно из условий (условия работают через «ИЛИ», а не все одновременно). Есть и более простой вариант — --keep-last N, оставить только N последних снимков независимо от календаря. По умолчанию restic группирует снимки по хосту и набору путей и применяет политику к каждой группе отдельно — это важно, если один и тот же репозиторий используют несколько серверов или в нём бэкапятся разные наборы путей.

Важно: forget без --dry-run удаляет записи о снимках сразу и без отмены (кроме восстановления из отдельной копии репозитория). А сам prune может надолго занять диск, CPU и сеть — он перекладывает данные внутри репозитория, и это особенно заметно на SFTP/S3 с большим объёмом. Планируйте prune на спокойное для сервера время суток.

Шаг 8. Автоматизируйте через cron

Заверните бэкап и очистку старых снимков в один скрипт:

bash
cat > /usr/local/bin/restic-backup.sh <<'EOF'
#!/bin/bash
set -euo pipefail
source /etc/restic/env

restic backup --tag daily --exclude-file=/etc/restic/excludes.txt /etc /var/www
restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --prune
EOF
chmod +x /usr/local/bin/restic-backup.sh

Проверьте его вручную перед тем, как доверить cron:

bash
/usr/local/bin/restic-backup.sh

Добавьте задание в crontab — например, каждую ночь в 03:00:

bash
crontab -e
cron
0 3 * * * /usr/local/bin/restic-backup.sh >> /var/log/restic-backup.log 2>&1
Совет: разбор синтаксиса cron, ротации логов и уведомлений о сбое бэкапа — отдельная тема, подробно разобранная в статье «Бэкапы по расписанию: cron».
Готово: бэкапы теперь снимаются и чистятся сами, без ручного запуска.

Что дальше

Полный разбор автоматизации через cron — в статье «Бэкапы по расписанию: cron». Если для ваших задач достаточно простого зеркалирования файлов на другой сервер без шифрования и дедупликации — см. «Бэкапы через rsync».