Стратегия резервного копирования для root-серверов, которая не подведёт
Правило 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
Оба разбивают файлы на блоки, сохраняют одинаковые блоки только один раз, шифруют и складывают состояния с версиями. Оба есть в пакетах всех четырёх дистрибутивов. Различие, на котором держится выбор, стоит в последней строке таблицы.
| Признак | restic | Borg |
|---|---|---|
| Пакет | restic | borgbackup, команда borg |
| Шифрование | всегда включено | на выбор, разумны repokey или keyfile |
| Освобождение места | forget с ключом --prune | prune, затем compact |
| Защита от удаления самим сервером | REST-сервер или объектное хранилище с версионированием | borg serve --append-only |
| Требование к месту назначения | достаточно доступа по SFTP | Borg должен стоять и на приёмной стороне |
На хранилище, куда вы ничего не можете установить, 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.
Итоговая проверка
Стратегия готова тогда, когда каждый из этих семи пунктов вы можете подтвердить командой, а не предположением:
restic snapshotsилиborg listпоказывает состояние за прошедшую ночь.systemctl list-timers backup.timerпоказывает время следующего запуска.restic checkилиborg checkне сообщает об ошибках.- В этом месяце удалось вернуть отдельный файл, и после восстановления он оказался идентичным оригиналу.
- Разбивка по срокам соответствует запланированному хранению, репозиторий не растёт бесконечно.
- Пароль лежит во втором месте, и вы уже хотя бы раз пользовались им оттуда.
- Как минимум одно хранилище принимает резервные копии так, что сервер не может их удалить.
Нет четвёртого пункта, значит, у вас предположение. Нет шестого, значит, зашифрованный мусор. Нет седьмого, значит, резервная копия, которая не переживёт ровно ту атаку, ради которой она нужнее всего.
Частые вопросы
Что означает правило 3-2-1 на одном root-сервере?
Является ли RAID или образ системы уже резервной копией?
restic или Borg: что и когда подходит?
Почему репозиторий не уменьшается, хотя я удаляю старые состояния?
Можно ли просто скопировать работающую базу данных как обычные файлы?
Что будет, если я потеряю пароль от репозитория?
Как часто нужно проверять восстановление?
Почему резервное копирование работает вручную, но не работает в systemd-таймере?
2026 KernelHost GmbH. Все права защищены. Эта инструкция охраняется авторским правом. Публикация на других сайтах, в том числе частично или в изменённом виде, без нашего письменного согласия не разрешается. Цитирование с указанием источника и активной ссылкой мы приветствуем.

