Стратегия резервного копирования для root-серверов, которая не подведёт

Опубликовано 14 мин. чтения

Правило 3-2-1 на одном root-сервере, restic и Borg с примерами, сроки хранения и шифрование. И тот шаг, который пропускают почти все: по-настоящему проверить восстановление.

Чек-лист на первые 30 минут заканчивается архивом tar с каталогом /etc и выводом, что это ещё не резервная копия, а лишь копия на том же накопителе. Отсюда начинается эта статья: как превратить её в стратегию, которая переживёт полную потерю сервера.

Всё крутится вокруг одной мысли: резервная копия, из которой ещё ни разу ничего не восстанавливали, — это не резервная копия, а надежда. Всё остальное служит тому, чтобы превратить надежду в проверенный факт.

Все команды выполняются от root. Если вы работаете обычным пользователем, ставьте перед каждой командой sudo. Опорные системы: Debian 13 (trixie), Debian 12 (bookworm), Ubuntu 24.04 LTS и Ubuntu 22.04 LTS. Там, где эти четыре системы расходятся, об этом сказано отдельно.

Прежде чем что-то менять: путь назад

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

1. Разберитесь с доступом к консоли до того, как он понадобится

Для KVM-root-серверов и выделенных серверов KernelHost консоль VNC доступна в личном кабинете. Она не зависит от сетевого стека гостевой системы и отвечает даже тогда, когда SSH молчит. Это и есть путь спасения, если восстановление записало старый sshd_config или authorized_keys поверх актуального. Войдите через неё один раз заранее и убедитесь, что знаете пароль root.

2. Никогда не восстанавливайте первым делом прямо в /

Первое восстановление всегда идёт в пустой каталог, например в /var/tmp/restore-test. Оттуда вы сравниваете файлы и копируете назад только нужное. Восстановление напрямую в / перезапишет и те файлы, которые изменились после резервного копирования вполне обоснованно.

3. Держите открытым второй сеанс

Пока вы правите SSH-ключи или файл authorized_keys на приёмной стороне, действует то же правило, что и при настройке файрвола: оставьте открытым второй терминал с уже установленным соединением и закройте его только тогда, когда заработает новое подключение.

4. Проверьте место до того, как репозиторий начнёт расти

df -h /
df -i /

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

Правило 3-2-1 в применении к одному серверу

  • Три копии. Рабочие данные и есть первая копия. Значит, нужны две резервные копии, а не одна.
  • Два разных хранилища. Два каталога на одном накопителе — это одно хранилище, два накопителя в одном RAID тоже: случайный rm заденет оба. RAID защищает от отказа накопителя и больше ни от чего.
  • Одна копия вне площадки. Не тот же сервер, не та же учётная запись управления, в идеале не та же локация.

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

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

Что нужно сохранять, а что нет

Самая частая ошибка не в том, что сохраняют слишком мало, а в том, что сохраняют всё подряд. Кто выгружает / без исключений, тащит с собой кэши пакетов, временные файлы и swap.

ЧтоТипичное местоСпособПочему
Конфигурация/etcКопирование файловПересборка вручную отнимает дни
Список пакетовТекстовый файл, см. нижеКопирование файловДелает пересборку воспроизводимой
Пользовательские данные/var/www, /srv, /homeКопирование файловВзять их больше неоткуда
Базы данных/var/lib/mysqlВыгрузка вместо копирования файловКопии файлов на ходу несогласованны
Сертификаты/etc/letsencryptКопирование файловКлюч учётной записи и лимиты удостоверяющего центра
КонтейнерыФайлы Compose и томаКопирование файловОбразы скачиваются заново, тома нет
Не сохранять/proc, /sys, /dev, /run, /tmpисключитьИнтерфейсы ядра без содержимого файлов
Не сохранять/var/cache, swapисключитьСоздаётся заново в любой момент
apt-mark showmanual > /root/package-list.txt
dpkg --get-selections > /root/package-selections.txt
wc -l /root/package-list.txt /root/package-selections.txt

apt-mark showmanual перечисляет только те пакеты, которые вы установили осознанно, без подтянутых вместе с ними зависимостей: это и есть короткий список, который понадобится при пересборке.

Файлы, база данных, образ: три способа, которые друг друга не заменяют

СпособХорошо защищает отНе защищает отТипичная ловушка
Копирование файловУдаления, повреждения отдельных файлов, потери сервераНесогласованности открытых баз данныхФайлы базы данных скопированы на ходу
Резервная копия базы данных (выгрузка)Несогласованности, возврата к чистому состояниюВсего, что лежит вне базы данныхОборванная выгрузка, файл которой выглядит пригодным
Образ системыПолного отказа, даёт короткое время восстановленияПоздно замеченного удаления, потери учётной записиМало состояний, и все в одной учётной записи

Отсюда следует последовательность, а не выбор. Сначала сервер баз данных пишет выгрузку, затем идёт копирование файлов, а каталог данных остаётся в исключениях. Копия /var/lib/mysql на работающей системе захватывает разные таблицы в разные моменты времени. Получится ли её восстановить, вы узнаете в самый неподходящий момент.

Файл с учётными данными, права и подводные камни выгрузки разобраны в статье Автоматическое резервное копирование баз MySQL и MariaDB. Важна точка стыковки: выгрузки складываются в /var/backups/db, лежат там день или два, а за срок хранения отвечает репозиторий. PostgreSQL сохраняется командой pg_dumpall от пользователя postgres.

restic или Borg

Оба разбивают файлы на блоки, сохраняют одинаковые блоки только один раз, шифруют и складывают состояния с версиями. Оба есть в пакетах всех четырёх дистрибутивов. Различие, на котором держится выбор, стоит в последней строке таблицы.

ПризнакresticBorg
Пакетresticborgbackup, команда borg
Шифрованиевсегда включенона выбор, разумны repokey или keyfile
Освобождение местаforget с ключом --pruneprune, затем compact
Защита от удаления самим серверомREST-сервер или объектное хранилище с версионированиемborg serve --append-only
Требование к месту назначениядостаточно доступа по SFTPBorg должен стоять и на приёмной стороне

На хранилище, куда вы ничего не можете установить, Borg отпадает. Если же у вас есть второй Linux-сервер под собственным управлением, режим append-only говорит в пользу Borg.

Настройка с restic

apt update
apt install -y restic
restic version

Проверьте вывод, прежде чем создавать репозиторий: четыре дистрибутива поставляют очень разные версии, а репозиторий, записанный более новой версией, старая не обязательно откроет.

Доступ к месту назначения

Местом назначения здесь служит второй сервер, доступный по SSH. Ключ принадлежит root, потому что читать все сохраняемые файлы имеет право только root:

ssh-keygen -t ed25519 -N '' -f /root/.ssh/id_ed25519_backup -C 'backup srv01'
ssh-copy-id -i /root/.ssh/id_ed25519_backup.pub backup@203.0.113.50
cat > /root/.ssh/config <<'EOF'
Host backuptarget
    HostName 203.0.113.50
    User backup
    IdentityFile /root/.ssh/id_ed25519_backup
    BatchMode yes
EOF
chmod 600 /root/.ssh/config

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

Пароль и репозиторий

install -d -m 700 /etc/restic
head -c 32 /dev/urandom | base64 | tr -d '\n' > /etc/restic/repo.pass
chmod 600 /etc/restic/repo.pass
cat > /etc/restic/env <<'EOF'
RESTIC_REPOSITORY=sftp:backuptarget:/srv/backup/srv01
RESTIC_PASSWORD_FILE=/etc/restic/repo.pass
EOF
chmod 600 /etc/restic/env

Этот файл одинаково годится и для оболочки, и для systemd. В оболочке:

set -a; . /etc/restic/env; set +a
restic init

Проверка результата: restic cat config выводит короткую структуру JSON с идентификатором репозитория. Если приходит встречный вопрос Is there a repository at the following location?, значит, путь неверен либо restic init ни разу не запускался.

Первый запуск

cat > /etc/restic/excludes.txt <<'EOF'
/proc
/sys
/dev
/run
/tmp
/var/tmp
/var/cache
/var/lib/apt/lists
/var/lib/mysql
/swapfile
EOF
restic backup / --one-file-system --exclude-file=/etc/restic/excludes.txt --exclude-caches --tag system

--one-file-system удерживает копирование в пределах корневой файловой системы и не берёт с собой смонтированные сетевые ресурсы. --exclude-caches пропускает каталоги, которые сами пометили себя как кэш. Исключение /var/lib/mysql — это предыдущий раздел, переведённый в практику.

Проверка результата: запуск заканчивается строкой вида snapshot 0a1b2c3d saved. После этого:

restic snapshots
restic stats latest

В списке указаны время, имя машины и пути. Если restic stats latest показывает неожиданно малый объём, значит, какое-то исключение захватило лишнее.

То же самое с Borg

apt install -y borgbackup
borg --version
export BORG_REPO='ssh://backup@203.0.113.50/./srv01'
borg init --encryption=repokey-blake2
borg create --stats --compression zstd ::'system-{now}' /etc /var/www /home /var/backups
borg list
borg info

Все четыре дистрибутива поставляют версию из ветки 1.x. Borg должен присутствовать с обеих сторон, а версия на приёмной стороне не должна быть старее той, что стоит на сервере. Держите для каждого сервера отдельный репозиторий: это избавит вас от необходимости ограничивать очистку архивами именно этого сервера и позволит развести ключи доступа.

Проверка результата: borg list показывает архив с отметкой времени, borg info — объём до и после дедупликации.

Срок хранения: дольше, чем закладывает большинство

Срок хранения определяется не тем, как долго вам хочется держать данные, а тем, сколько времени проходит до момента, когда повреждение вообще замечают. Удалённый каталог обнаруживают в тот же день, а сломанную таблицу нередко лишь через недели. Кто хранит семь дней, тот все семь дней исправно сохранял и само повреждение.

Тип данныхПредложениеПричина
Выгрузки баз данных7 ежедневных, 4 еженедельных, 6 ежемесячныхТихие повреждения замечают поздно
Конфигурация30 ежедневных, 12 ежемесячныхВидно, когда и что менялось
Пользовательские данные30 ежедневных, 6 ежемесячныхПропажу удалённых файлов редко замечают сразу
Журналынастолько коротко, насколько допустимоОбъёмные, для восстановления нужны редко
restic forget --dry-run --keep-daily 7 --keep-weekly 4 --keep-monthly 6
restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --prune

Пробный запуск показывает, какие состояния исчезли бы. Без --prune уходят только ссылки, а место остаётся занятым. У Borg начиная с версии 1.2 это делается в два шага, и забытая вторая команда объясняет большинство репозиториев, которые никак не хотят уменьшаться:

borg prune --list --dry-run --keep-daily=7 --keep-weekly=4 --keep-monthly=6
borg prune --list --keep-daily=7 --keep-weekly=4 --keep-monthly=6
borg compact

Проверка результата: restic snapshots или соответственно borg list показывает ожидаемую разбивку по срокам, а занятый объём на приёмной стороне уменьшается.

Шифрование и ключ, которого потом ни у кого нет

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

Отсюда два правила. Первое: пароль должен лежать во втором месте, обычно в менеджере паролей. Пароль, который есть только в /etc/restic/repo.pass, исчезнет вместе с потерянным сервером, а вместе с ним и резервная копия. Второе: при использовании repokey в Borg экспортируйте ключ и положите его наружу, потому что там он хранится внутри самого репозитория:

borg key export ::

Проверка результата: выполните restic snapshots на другой машине, взяв пароль из менеджера паролей. Пароль, которым вы ещё ни разу не пользовались с другой машины, остаётся неподтверждённым.

Защитить место назначения от удаления

У злоумышленника с правами root есть доступ и к сохранённому паролю, и к ключу для приёмной стороны. Значит, он может удалить резервные копии до того, как зашифрует рабочие данные. Поэтому нужно хранилище, которое принимает новые состояния, но не разрешает удаление. В Borg это настраивается в файле authorized_keys пользователя резервного копирования на приёмной стороне:

command="borg serve --append-only --restrict-to-path /srv/backup/srv01",restrict ssh-ed25519 AAAA... backup srv01

После этого ключ принимает только резервные копии и только в этот путь. Учтите, что prune здесь хотя и отработает, но места не освободит, потому что удаление на приёмной стороне не выполняется. Очистку вы делаете там, куда у сервера доступа нет. У restic эту роль берут на себя REST-сервер в режиме append-only или объектное хранилище с версионированием. Обычный доступ по SFTP этого не даёт.

Автоматизация с помощью systemd-таймера

Таймер предпочтительнее cron-задания: он пишет вывод в журнал, догоняет пропущенные запуски и умеет разносить время старта. О строении юнитов: Создание собственной systemd-службы.

cat > /etc/systemd/system/backup.service <<'EOF'
[Unit]
Description=Ежедневная резервная копия с restic
Wants=network-online.target
After=network-online.target

[Service]
Type=oneshot
EnvironmentFile=/etc/restic/env
Nice=10
IOSchedulingClass=idle
ExecStart=/usr/bin/restic backup / --one-file-system --exclude-file=/etc/restic/excludes.txt --exclude-caches --tag system
ExecStart=/usr/bin/restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --prune
EOF
cat > /etc/systemd/system/backup.timer <<'EOF'
[Unit]
Description=Запускает ежедневное резервное копирование

[Timer]
OnCalendar=*-*-* 02:30:00
RandomizedDelaySec=1800
Persistent=true

[Install]
WantedBy=timers.target
EOF
systemctl daemon-reload
systemctl enable --now backup.timer
systemd-analyze calendar '*-*-* 02:30:00'

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

ExecStartPre=/usr/bin/mountpoint -q /mnt/backup

Проверка результата:

systemctl start backup.service
systemctl list-timers backup.timer
journalctl -u backup.service -n 50 --no-pager

systemctl list-timers обязан показывать время следующего запуска. Если там пусто, таймер не включён. Самое важное остаётся напоследок: запуск, который тихо перестал выполняться, и есть обычный сценарий провала. Проверяйте systemctl is-failed backup.service либо добавьте последней строкой ExecStart вызов внешней службы мониторинга, которая поднимет тревогу, когда ежедневный сигнал жизни не придёт.

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

Малая проверка: раз в месяц, пять минут

mkdir -p /var/tmp/restore-test
restic restore latest --target /var/tmp/restore-test --include /etc/ssh/sshd_config
diff /etc/ssh/sshd_config /var/tmp/restore-test/etc/ssh/sshd_config && echo "идентично"

С Borg соответственно, он хранит пути без ведущей косой черты:

cd /var/tmp/restore-test
borg extract --list ::system-2026-09-03T02:30:00 etc/ssh/sshd_config

Дополнительно проверьте целостность репозитория. Вторая строка в каждом случае вычитывает данные обратно и пересчитывает контрольные суммы, у restic только для части, чтобы запуск не длился часами:

restic check
restic check --read-data-subset=1/7
borg check
borg check --verify-data

Проверка результата: restic check заканчивается строкой no errors were found, borg check отрабатывает без сообщений об ошибках. Репозиторий, который проверяют раз в год, мог быть повреждён все предыдущие одиннадцать месяцев.

Большая проверка: раз в год

Малая проверка доказывает, что файлы читаются. Она не доказывает, что вы снова запустите сервис. Для этого нужен полный прогон на втором, пустом сервере: установить базовую систему, применить список пакетов, подключить репозиторий, вернуть данные, залить выгрузку, запустить службы. Засеките время. Эта цифра и есть ваш реальный срок восстановления, и по опыту он в несколько раз больше ожидаемого.

Запишите, чего не хватило. Это почти всегда одно и то же: файл за пределами сохраняемых путей, сервис с конфигурацией в /opt, сертификат без ключа учётной записи, база данных без пользователей и прав. Этот список и есть результат проверки.

Частые ошибки и их решения

Host key verification failed. Служба работает от root, а root ни разу не подтверждал ключ хоста приёмной стороны. Выполните один раз вручную ssh backuptarget true либо ssh-keyscan -H 203.0.113.50 >> /root/.ssh/known_hosts. В консоли всё получается, а в таймере нет: почти всегда именно эта ошибка.

Permission denied (publickey). Не тот пользователь, не тот ключ или неверные права на приёмной стороне: каталогу .ssh нужны права 700, файлу authorized_keys права 600, и оба должны принадлежать целевому пользователю.

Is there a repository at the following location? restic не находит структуру репозитория: неверный путь, restic init ни разу не выполнялся или место назначения сейчас недоступно.

Fatal: wrong password or no key found Файл с паролем не подходит к репозиторию, чаще всего потому, что его создали заново уже после. Проверьте командой cat -A /etc/restic/repo.pass, не закрался ли туда лишний пробел.

repository is already locked exclusively by Оборванный запуск оставил после себя блокировку. Сначала убедитесь, что ничего больше не работает, затем выполните restic unlock. У Borg сообщение звучит как Failed to create/acquire the lock с добавкой (timeout), а команда называется borg break-lock. Обе рискованны, пока какой-то запуск всё же активен.

Warning: The repository at location ... was previously located at ... Адрес репозитория изменился, и Borg переспрашивает в интерактивном режиме. Внутри юнита команда из-за этого ждёт ответа, который никогда не придёт. Убедившись, что репозиторий тот же самый, впишите BORG_RELOCATED_REPO_ACCESS_IS_OK=yes в файл окружения.

No space left on device на приёмной стороне. Либо очистка не запускается вовсе, либо она идёт без --prune или без borg compact. Если же, наоборот, корневую файловую систему забила выгрузка, поможет статья Как освободить место на диске в Linux.

Запуск сообщает об успехе, но почти ничего не сохраняет. Либо исключение захватывает лишнее, либо в пути опечатка. Сравните restic stats latest со значением за предыдущий день. Резервная копия, внезапно уменьшившаяся на порядки, — это тревога, а не успех.

Различия между дистрибутивами

  • Названия пакетов во всех четырёх системах одинаковы: restic и borgbackup. А вот поставляемые версии нет.
  • Ведение журнала: в Debian 13 и во многих установках Debian 12 нет rsyslog. Вывод запуска вы найдёте там исключительно в журнале, то есть через journalctl -u backup.service. В Ubuntu 22.04 и 24.04 дополнительно есть запись в /var/log.
  • Монтирование состояний резервных копий: для restic mount и borg mount нужен пакет fuse3. Если его нет, команда прерывается с указанием на отсутствующий fusermount3. Путь без FUSE — это restic restore или соответственно borg extract.
  • Утилита для баз данных: Debian поставляет исключительно MariaDB, там она называется mariadb-dump, а mysqldump остаётся ссылкой на неё. В Ubuntu может работать и MySQL 8, там существует только mysqldump.

Итоговая проверка

Стратегия готова тогда, когда каждый из этих семи пунктов вы можете подтвердить командой, а не предположением:

  1. restic snapshots или borg list показывает состояние за прошедшую ночь.
  2. systemctl list-timers backup.timer показывает время следующего запуска.
  3. restic check или borg check не сообщает об ошибках.
  4. В этом месяце удалось вернуть отдельный файл, и после восстановления он оказался идентичным оригиналу.
  5. Разбивка по срокам соответствует запланированному хранению, репозиторий не растёт бесконечно.
  6. Пароль лежит во втором месте, и вы уже хотя бы раз пользовались им оттуда.
  7. Как минимум одно хранилище принимает резервные копии так, что сервер не может их удалить.

Нет четвёртого пункта, значит, у вас предположение. Нет шестого, значит, зашифрованный мусор. Нет седьмого, значит, резервная копия, которая не переживёт ровно ту атаку, ради которой она нужнее всего.

Частые вопросы

Что означает правило 3-2-1 на одном root-сервере?
Три копии данных в двух разных хранилищах, одна из них вне площадки. Рабочие данные на сервере уже являются первой копией, поэтому нужны две резервные копии, а не одна. Два каталога на одном накопителе считаются одним хранилищем, два накопителя в одном RAID тоже. Вне площадки означает: не на том же сервере, не в той же учётной записи управления, в идеале не в той же локации.
Является ли RAID или образ системы уже резервной копией?
Нет. RAID защищает от отказа одного накопителя и больше ни от чего, ведь случайный rm заденет все накопители одновременно. Образ системы — самый быстрый путь назад после полного отказа, но он лежит в той же учётной записи управления, что и сам сервер, и потому за копию вне площадки не считается. И то и другое дополняет резервное копирование, но не заменяет его.
restic или Borg: что и когда подходит?
Практическая разница лежит на стороне назначения. restic обходится обычным доступом по SFTP, а Borg требует, чтобы Borg был установлен и на приёмной системе. Зато Borg даёт через borg serve --append-only режим работы, в котором сервер может писать новые состояния, но уже ничего не может удалить. Шифрование и дедупликацию умеют оба.
Почему репозиторий не уменьшается, хотя я удаляю старые состояния?
Потому что удаление ссылок и освобождение места — это два разных шага. У restic команде restic forget дополнительно нужен ключ --prune. У Borg начиная с версии 1.2 после borg prune следует ещё borg compact. Если репозиторий работает в режиме append-only, места не освободит и это: там очистку выполняют на приёмной системе.
Можно ли просто скопировать работающую базу данных как обычные файлы?
Надёжно нет. Копия /var/lib/mysql на работающей системе захватывает разные таблицы в разные моменты времени и восстанавливается то удачно, то нет. Сначала сделайте выгрузку, включите её в копирование файлов, а каталог данных базы исключите.
Что будет, если я потеряю пароль от репозитория?
Тогда данные потеряны окончательно. restic и Borg шифруют данные до передачи, и обходного пути не существует. Поэтому пароль должен лежать во втором месте за пределами сервера, обычно в менеджере паролей. При использовании repokey в Borg дополнительно экспортируйте ключ, иначе он хранится только внутри самого репозитория.
Как часто нужно проверять восстановление?
Раз в месяц возвращайте один файл в пустой каталог и сравнивайте его с оригиналом командой diff, к этому добавьте restic check или соответственно borg check. Раз в год следует полный прогон на втором, пустом сервере с замером времени. Это время и есть ваш реальный срок восстановления, и по опыту он в несколько раз больше ожидаемого.
Почему резервное копирование работает вручную, но не работает в systemd-таймере?
Самая частая причина — сообщение Host key verification failed: таймер работает от root, а root ни разу не подтверждал ключ хоста приёмного сервера. Выполните один раз вручную ssh backuptarget true. У Borg вдобавок может зависнуть интерактивный вопрос, если адрес репозитория изменился. Против этого помогает BORG_RELOCATED_REPO_ACCESS_IS_OK=yes в файле окружения.

Резервное копирование restic BorgBackup Debian Ubuntu root-сервер systemd Безопасность сервера