В нашей документации это сказано одной строкой: резервное копирование — ваша ответственность. Это честная позиция для любого хостинга, который даёт вам полный root, и именно на ней большинство людей перестаёт читать. А самое интересное остаётся за скобками этой фразы, потому что бэкап сервера — это на самом деле вопрос вовсе не о том, какую команду запустить. Это вопрос о том, где живёт вторая копия, кто может её уничтожить и можно ли то, что вы старательно производите каждую ночь, действительно превратить обратно в работающую машину.
Большинство схем резервного копирования проваливают одну из этих трёх проверок, и почти всегда одинаково: копия лежит у того же провайдера и на том же аккаунте, что и оригинал; учётные данные, которыми она записывается, позволяют её и удалить; и никто ни разу из неё не восстанавливался. Это руководство исправляет все три проблемы. Оно написано для одного Linux VPS с реальной нагрузкой — сайтом, базой данных, Nextcloud, нодой — где всю работу делает restic или BorgBackup, шифрование происходит на вашей машине до того, как что-либо её покинет, а на другом конце стоит второй недорогой сервер в другой стране, который тоже принадлежит вам.
Почему бэкап обычно — наименее защищённая копия
Люди делают сложную часть и оставляют несделанной простую. Сервер проходит базовую защиту: ключи вместо паролей, файрвол, который по умолчанию всё запрещает, а может, и полнодисковое шифрование, чтобы диск ничего не значил, когда машина выключена. А затем каждую ночь полная незашифрованная копия всего, что есть на этом диске, уходит в хранилище, аккаунт в котором был заведён много лет назад на банковскую карту, в стране, о которой никто не задумывался, и защищён паролем, который заодно сохранён в браузере.
Копия заслуживает того же внимания, что и оригинал, потому что она и есть оригинал, только без аптайма. Стоит чётко понимать, от чего именно вы защищаетесь, потому что ответы разные и только один из них — тот, который обычно себе представляют:
- Машина умирает. Железо, гипервизор, файловая система, которая после сбоя поднимается только для чтения. Редко, буднично, и с этим случаем справляется любой бэкап.
- Вы ломаете всё сами. Миграция, прошедшая наполовину,
rmс чересчур смелой маской, изменение конфигурации, которое было в порядке до перезагрузки. С огромным отрывом самая частая причина, по которой кто-либо вообще что-либо восстанавливает. - Кто-то другой получает root. Случай, который определяет всю вашу архитектуру. Атакующий, которому нужен рычаг давления, сначала удаляет бэкапы и только потом шифрует сервер; если на вашем сервере лежат учётные данные, способные стереть репозиторий, вы построили систему, которая ему помогает.
- Аккаунт исчезает. Не прошедший платёж, спор, блокировка. Если оригинал и копия находятся за одним и тем же логином, у них одна и та же единая точка отказа, и она не техническая.
- Приходит кто-то с полномочиями. Железо могут изъять, а аккаунт заморозить в установленном порядке, без всякой атаки. Помогает только копия в другой юрисдикции, и то лишь если вы по-прежнему можете до неё добраться.
Обратите внимание, от скольких из этих случаев никак не спасает снапшот, сделанный вашим же провайдером, на вашем же аккаунте, в том же дата-центре.
3-2-1 и две цифры, добавленные позже
Это правило пришло из фотографии, а не из системного администрирования — Питер Крог сформулировал его для людей с незаменимыми негативами — и выжило, потому что оно короткое: три копии данных, на двух разных системах, одна из них — на удалённой площадке. Каждая цифра защищает от своего пункта из списка выше, и именно поэтому правило — нечто большее, чем лозунг.
Три копии отвечают на обычную потерю: рабочие данные плюс два бэкапа, чтобы обнаружение испорченного бэкапа было досадной неприятностью, а не концом. Две разные системы отвечают на коррелированные отказы — две копии на одном хосте, одном массиве или одном аккаунте — это одна копия, которая притворяется двумя. А копия на удалённой площадке отвечает на всё, что случается с местом, а не с диском: пожар, изъятие, блокировку, провайдера, который перестал отвечать на письма.
Современное расширение правила — 3-2-1-1-0, и в 2026 году важны именно две добавленные цифры. Дополнительная 1 — это неизменяемая или офлайн-копия — то, что атакующий с вашими учётными данными не сможет удалить, а это ровно то, что даёт режим append-only. 0 — это ноль ошибок при проверке: бэкап, который вы проверили, а не просто сделали.
Для одного небольшого VPS на практике всё выглядит скромнее, чем звучит. Рабочий сервер — копия номер один. Репозиторий на второй машине в другой стране — копия номер два: на удалённой площадке, на другой системе и в режиме append-only, так что она же и неизменяемая. Ежемесячное полное восстановление на временный сервер — это ноль. Вот и всё правило, выполненное за цену второго небольшого сервера, а остальная часть руководства — его механика.
restic или Borg: различия, которые действительно решают
Оба инструмента делают одни и те же четыре вещи, и оба делают их хорошо: режут данные на блоки по содержимому, так что перемещённый или переименованный файл не загружается заново; дедуплицируют данные между всеми снапшотами, так что год ежедневных бэкапов обходится гораздо дешевле, чем год отдельных копий; сжимают; и — самое важное здесь — шифруют и аутентифицируют данные на клиенте, прежде чем хоть один байт уйдёт в сеть. В обоих случаях хранилище держит шифротекст, который не может ни прочитать, ни незаметно изменить. Именно поэтому руководство и не высказывает мнения о том, кто управляет вашим хранилищем.
Настоящие различия касаются другого конца.
- restic — это один статически собранный бинарник на Go, и от места назначения он ничего не требует. Он пишет по SFTP, в S3-совместимое объектное хранилище, на собственный REST-сервер или куда угодно, куда дотягивается
rclone. Шифрование — AES-256 с аутентификатором Poly1305; во втором формате репозитория появилось сжатие. Если ваша цель — бакет или машина, на которую нельзя ставить софт, ответ — restic, и спорить тут особо не о чем. - Borg требует, чтобы
borgбыл установлен на обоих концах, и общается сам с собой по SSH. Взамен он даёт то, для чего restic нужен помощник: настоящий, принудительно применяемый на сервере режим append-only, закреплённый опциейcommand=вauthorized_keysудалённой учётной записи. Клиент — в том числе скомпрометированный — может добавлять архивы, но не может их удалять. Для модели угроз из предыдущего раздела это самая ценная функция обоих проектов.
Так что решение короткое. Если вы контролируете другой конец, используйте Borg в режиме append-only. Если нет — restic, а то же свойство верните себе, либо запустив на целевой машине rest-server --append-only, либо используя бакет с object lock. Ниже показаны оба пути.
Одна оговорка, прежде чем вы определитесь с репозиторием: набор команд Borg не стабилен между основными ветками — сокращение repo::archive, которое используется ниже, относится к серии 1.x и в 2.x меняется — так что сверяйте borg --version с той документацией, которую читаете, а не наоборот. restic здесь стабильнее, хотя и ему пришлось пройти через собственную миграцию формата репозитория.
Чего делать не стоит — так это запускать оба сразу в расчёте на то, что два бэкапа надёжнее одного. Две системы, которые поддерживаются наполовину, дают два репозитория, которые вы не проверяли, и одну процедуру восстановления, которую вы не сможете вспомнить в стрессовой ситуации. Выберите тот инструмент, который подходит к вашему другому концу, а сэкономленные силы потратьте на его проверку.
Где живёт репозиторий и кому разрешено его удалять
Всё, что выше, — стандартные советы. А вот эту часть обычно опускают.
Обычный способ бэкапить сервер — выдать ему учётные данные и позволить ему самому отправлять данные. Эти учётные данные почти всегда позволяют и удалять — потому что для очистки старых снапшотов нужно удаление, а выдать один ключ, который может всё, было проще. В результате атакующий, завладевший вашим сервером, завладевает и вашими бэкапами, и в том самом сценарии, ради которого бэкап в первую очередь и заводили, он не стоит ровным счётом ничего. Выходов ровно три:
- Append-only на стороне хранилища. Другой конец принимает новые данные и отказывается что-либо удалять, что бы ни просил клиент. Именно так работают
borg serve --append-onlyиrest-server --append-only, с принудительным применением на сервере, а удаление становится операцией, которую вы осознанно выполняете с бэкап-сервера. - Object lock. S3-совместимое хранилище может держать объекты неизменяемыми в течение срока хранения. Эффективно, но доверие при этом переносится на провайдера и на ваши платёжные отношения с ним.
- Pull вместо push. Бэкап-сервер сам подключается к рабочему серверу и читает данные. Ничто на рабочем сервере не может дотянуться до репозитория — но теперь у бэкап-сервера есть ключ к продакшену, так что вы перевернули доверие, а не избавились от него. Разумно, когда бэкап-сервер действительно защищён лучше; в остальных случаях преимущество не очевидно.
Вариант с append-only — самый дешёвый и с наименьшим числом оговорок, поэтому ниже строится именно он.
Затем вопрос — где. Бэкап — это полная копия вашего сервера, он подчиняется законам того места, куда вы его положили, и лежит на аккаунте, который кому-то принадлежит. Тщательно выбрать юрисдикцию для сервера, а потом каждую ночь отправлять копии в бакет совсем в другом месте — распространённая и незаметная ошибка — юрисдикция копии — это и есть юрисдикция данных. Шифрование на стороне клиента заметно смягчает проблему: требование, предъявленное вашему провайдеру хранилища, вернёт только шифротекст. Но оно не защищает доступность и не скрывает того, что репозиторий существует, какого он размера и когда менялся в последний раз.
Поэтому схема, которая решает больше всего проблем разом, — это второй небольшой сервер, который тоже принадлежит вам, расположенный в другой стране и арендованный так же, как первый: без привязки к личности, без KYC и с оплатой в Monero. Оба конца ваши, оба дешёвые, передача никогда не проходит через третью сторону, а копия недосягаема для всего, что может случиться с юрисдикцией или аккаунтом оригинала.
Подобрать ему размер проще, чем настоящему серверу, потому что сервер для бэкапов — это диск с приделанным к нему процессором. Что ему действительно нужно — это достаточно памяти, чтобы выполнить очистку (prune) и проверку целостности без свопинга, ведь это единственная по-настоящему прожорливая операция — так что одного гигабайта впритык, а с двумя комфортно. Из линейки тарифов:
- Cub — 1 vCPU / 2 GB / 40 GB NVMe, $5.00/мес. Вариант по умолчанию. Обычный веб-сервер или сервер приложений с 10–15 GB рабочих данных и годом ежедневной истории помещается сюда с запасом, потому что основную работу делают дедупликация и сжатие.
- Scout — 2 vCPU / 4 GB / 70 GB, $9.00/мес. Несколько серверов в одном репозитории или Nextcloud с фотографиями.
- Runner и Hunter — 100 GB за $14.00 и 140 GB за $19.00. Сюда в итоге попадают почтовое хранилище, медиатека или история за несколько лет.
- Pup — 1 vCPU / 1 GB / 25 GB, $3.50/мес. Годится только для конфигурации и дампов баз данных. Не направляйте на него большое дерево файлов в расчёте на то, что обслуживание останется приятным.
Рабочее правило для диска: закладывайте в два-три раза больше размера одного полного бэкапа, и у вас будет год истории с запасом на ошибку. Разместите сервер там, где нет оригинала — Амстердам, Париж, Бухарест и София идут по базовой цене, Рейкьявик — с коэффициентом 1.20, а Цюрих — 1.35, так что Cub в Швейцарии обойдётся в $6.75 в месяц. Первая загрузка — единственная крупная передача, которую вы когда-либо сделаете, а трафик безлимитный на скорости 1 Gbps, так что начальное заполнение репозитория не обернётся счётом в конце месяца.
Пошаговая инструкция
- Решите, что незаменимо, а остальное запишите
Прежде чем браться за инструменты, потратьте десять минут и разложите содержимое сервера на три кучи. Одно это упражнение убирает большую часть затрат и почти всю путаницу.
Незаменимое — то, чего больше нигде нет: базы данных, загруженные файлы, состояние приложений, TLS-материалы, которые нельзя перевыпустить, файлы кошельков, SSH host keys и
/etcцеликом, потому что/etc— это письменная летопись каждого решения, которое вы приняли и забыли.Воспроизводимое с усилием — установленные пакеты, образы контейнеров, всё, что можно пересобрать скриптом, если сам скрипт лежит в первой куче.
Бесплатно получаемое заново — кеш пакетов, данные блокчейна, дистрибутивы, артефакты сборки. Никогда их не бэкапьте. На ноде это разница между репозиторием на 3 GB и репозиторием на 300 GB.
Превратите третью кучу в файл исключений прямо сейчас, чтобы он действовал с самого первого запуска:
cat > /etc/backup-excludes <<'EOF' /dev /proc /sys /run /tmp /var/tmp /var/cache /var/lib/apt/lists /var/lib/docker/overlay2 /swapfile /var/lib/bitcoind/blocks /var/lib/bitcoind/chainstate **/node_modules **/.cache EOFОтдельно про Docker. Бэкапить
/var/lib/docker— неверный инстинкт: слои пересобираются и занимают огромное место. Бэкапьте compose-файлы и именованные тома —/var/lib/docker/volumes— а образы пусть вернутся из реестра. - Поднимите сервер для бэкапов и дайте ему одну узкую задачу
Разверните второй сервер — по умолчанию Cub, Debian 13, не в той стране, где стоит сервер, который вы бэкапите. Проведите на нём ту же десятиминутную защиту, что и на любом другом: SSH только по ключу, вход под root отключён, файрвол по умолчанию всё запрещает. Это машина, на которой хранится полная копия другой машины, так что расслабляться здесь не место.
Затем создайте пользователя, всё существование которого сводится к приёму бэкапов, без интерактивного доступа и без возможности делать что-либо ещё:
adduser --disabled-password --gecos '' --home /srv/backup --shell /bin/bash backup install -d -m 700 -o backup -g backup /srv/backup/.ssh apt install -y borgbackup # or: apt install -y resticОболочка здесь намеренно настоящая. Принудительная SSH-команда выполняется через login shell учётной записи, так что если поставить здесь
nologin— а рука сама к этому тянется — вы не усилите ограничение, которое добавите на седьмом шаге, а сломаете его. Изоляцию обеспечивает строкаcommand=, а не поле с оболочкой.Сгенерируйте на рабочем сервере ключ только для этого и ни для чего больше, чтобы отзыв доступа к бэкапам никогда не означал вмешательства в ваш собственный логин:
ssh-keygen -t ed25519 -f /root/.ssh/id_backup -C 'backup@prod' -N ''Скопируйте публичную часть на целевой сервер, в
/srv/backup/.ssh/authorized_keys, с владельцемbackupи правами600. Пока оставьте её обычной записью — ограничения она получит на седьмом шаге, когда вы убедитесь, что вся цепочка работает. - Сделайте базы данных безопасными для копирования
Этот шаг отличает бэкап от файла, который только выглядит как бэкап. Движок базы данных держит состояние в памяти и пишет на диск в собственном порядке; копирование его каталога снаружи захватывает момент, который движок никому не собирался показывать, и результат восстанавливается во что-то, что не запускается — или, хуже того, запускается, но работает неуловимо неправильно.
Сначала дамп, по расписанию, которое срабатывает раньше бэкапа. Для MariaDB или MySQL
--single-transactionдаёт согласованный снимок таблиц InnoDB, не блокируя приложение:install -d -m 700 /var/backups/db mariadb-dump --single-transaction --quick --all-databases \ | zstd -T0 > /var/backups/db/all-$(date +%F).sql.zstДля PostgreSQL запускайте дамп от пользователя
postgres;pg_dumpallзаодно сохраняет роли, а в день восстановления люди обычно обнаруживают, что они были нужны:su - postgres -c 'pg_dumpall' | zstd -T0 > /var/backups/db/pg-$(date +%F).sql.zstУ SQLite своя ловушка: простая копия рядом с активным журналом упреждающей записи (WAL) может захватить разорванное состояние. Вместо этого попросите саму библиотеку:
sqlite3 /var/lib/app/app.db ".backup '/var/backups/db/app.db'"Держите на диске дампы только за пару дней — историю хранит репозиторий — и помните, что
/var/backups/dbтеперь содержит всё, что содержит ваша база данных, так что права700— не для красоты. Если вы вообще не хотите держать на диске незашифрованные дампы, оба инструмента умеют читать из пайпа:borg create ::db-{now} -с--stdin-nameилиrestic backup --stdin --stdin-filename. - Инициализируйте репозиторий и храните парольную фразу там, где сервер не сможет её потерять
Сгенерируйте парольную фразу с настоящей энтропией, потому что только она стоит между репозиторием и любым, кто им завладеет:
openssl rand -base64 32Запишите её в менеджер паролей и на бумагу, прежде чем использовать. Любую другую ошибку в этом руководстве можно исправить; эту — нельзя. Репозиторий, чья парольная фраза существовала только на сервере, который он защищал, — самый полный и самый бесполезный бэкап, какой только можно сделать.
Затем инициализируйте репозиторий. С Borg — по SSH к пользователю из второго шага, в аутентифицированном режиме с ключом внутри репозитория — и сразу же экспортируйте этот ключ куда-нибудь ещё:
export BORG_REPO='ssh://[email protected]/srv/backup/prod' borg init --encryption=repokey-blake2 borg key export :: /root/borg-key.txt # then move it OFF this machineС restic — то же самое по SFTP:
export RESTIC_REPOSITORY='sftp:[email protected]:/srv/backup/prod' export RESTIC_PASSWORD_FILE=/root/.restic-pass install -m 600 /dev/null /root/.restic-pass && nano /root/.restic-pass restic initДа, файл с парольной фразой лежит на рабочем сервере — иначе нельзя, если задача должна запускаться без участия человека. Именно поэтому значение имеют копия в менеджере паролей и копия на бумаге, и именно поэтому следующие шаги лишают этот сервер возможности что-либо уничтожить.
- Запустите первый бэкап и прочитайте, что он вам сообщает
Первый запуск — медленный: всё новое, так что ничего не дедуплицируется и ничего не закешировано. Запустите его вручную, в терминале, за которым можно следить, и будьте готовы к тому, что на большом дереве файлов это займёт время.
borg create --stats --progress --compression zstd,3 \ --exclude-from /etc/backup-excludes ::'prod-{now}' \ /etc /home /root /srv /var/www /var/lib/docker/volumes /var/backups/dbrestic backup --verbose --exclude-file /etc/backup-excludes \ /etc /home /root /srv /var/www /var/lib/docker/volumes /var/backups/dbЗатем сделайте то, чего почти никто не делает (две минуты, а ловит большинство ошибок первой попытки) — посмотрите, что вы на самом деле сохранили:
borg list ::prod-... | less restic ls latest | lessВы ищете две ошибки противоположного рода. Что-то важное отсутствует, потому что путь был неверным или шаблон исключения оказался жаднее, чем задумывалось. Или что-то огромное оказалось там, где ему не место — каталог кеша, блокчейн, архив логов — ведь именно в этом разница между репозиторием, который годами помещается на свой диск, и тем, что заполнит его к весне. Исправьте списки сейчас, пока репозиторий маленький и повторный запуск ничего не стоит.
- Автоматизируйте через таймер systemd, а не строкой в cron
Сведите всю последовательность — дамп, бэкап, очистка, отчёт — в один скрипт, чтобы, когда что-то сломается, читать нужно было ровно одно место:
install -m 700 /dev/null /usr/local/sbin/backup-run nano /usr/local/sbin/backup-run # set -eu; dumps; borg create; ping the monitorЗатем сервис и таймер. Здесь systemd стоит двух лишних файлов: код выхода записывается, вывод попадает в журнал, а не в почту, которую никто не читает,
Persistent=trueдогоняет запуск, который машина проспала, а сбой может напрямую запустить юнит оповещения.# /etc/systemd/system/backup.service [Unit] Description=Encrypted off-site backup OnFailure=backup-alert.service [Service] Type=oneshot ExecStart=/usr/local/sbin/backup-run Nice=10 IOSchedulingClass=idle# /etc/systemd/system/backup.timer [Unit] Description=Nightly encrypted backup [Timer] OnCalendar=daily RandomizedDelaySec=45m Persistent=true [Install] WantedBy=timers.targetsystemctl daemon-reload && systemctl enable --now backup.timer systemctl list-timers backup.timer journalctl -u backup.service -n 50NiceиIOSchedulingClass=idleне дают ночному запуску замедлять сайт, пока он работает.RandomizedDelaySecважнее, чем кажется: без него все ваши машины стартуют в одну и ту же секунду, а это жестоко и по отношению к целевому серверу, и по отношению к вам самим. - Отнимите у рабочего сервера право на удаление
Теперь закройте брешь, которую большинство схем оставляют открытой. На сервере для бэкапов отредактируйте
/srv/backup/.ssh/authorized_keysи привяжите ключ рабочего сервера к одной команде, которая ничего не может удалить:command="borg serve --append-only --restrict-to-path /srv/backup/prod",restrict ssh-ed25519 AAAA...Здесь вместе работают два механизма.
--append-onlyзаставляет сервер отклонять любое удаление, что бы ни просил клиент, а--restrict-to-pathограничивает его собственным репозиторием, чтобы один скомпрометированный хост не мог добраться до чужого. Опцияrestrictотключает проброс портов, проброс агента и всё остальное, так что ключ не открывает ничего, кроме этого.Эквивалент для restic — запустить его REST-сервер на целевой машине и поручить ему соблюдение того же правила:
rest-server --path /srv/backup --append-only --private-reposПроверьте как следует — с рабочего сервера, попытавшись сделать запрещённое:
borg delete ::prod-... # must fail borg create ::test-{now} /etc # must succeedЕсли удаление прошло, строка
command=не применяется — обычно потому, что в файле со второго шага всё ещё лежит второй, неограниченный ключ. Очистка теперь происходит на целевом сервере, где репозиторий локальный: еженедельный таймер запускает тамborg pruneиborg compact, а рабочий сервер в этом никак не участвует. - Восстановите — и сразу поставьте следующие учения в календарь
Всё до этого момента было подготовкой. Именно этот шаг превращает её в бэкап.
Начните с целостности, то есть с проверки внутренней согласованности репозитория. Читайте выборку реальных данных, а не только метаданные, потому что проверки одних метаданных проходят и на репозиториях, из которых нельзя восстановиться:
borg check --verify-data :: restic check --read-data-subset=10%Затем выполните настоящее восстановление — во временный каталог или, что лучше, на свежеразвёрнутый сервер, который вы потом удалите. Монтирование — самый быстрый способ просмотреть содержимое; извлечение — то, что вы на самом деле будете делать в три часа ночи:
mkdir /mnt/r && borg mount ::prod-... /mnt/r # browse borg extract ::prod-... etc/nginx # restore one path restic mount /mnt/r restic restore latest --target /tmp/restore --include /etc/nginxТеперь сделайте ту часть, которая не про файлы. Восстановите дамп базы данных в работающий движок и откройте приложение, подключив его к этой базе. Дамп, который распаковывается, — это ещё не дамп, который импортируется, и разрыв между этими двумя фактами погубил не одну компанию:
zstd -dc /tmp/restore/var/backups/db/all-2026-09-03.sql.zst | mariadbЗапишите, сколько всё это заняло и что вам пришлось искать. Это число — ваше реальное время восстановления, а не то, которое вы себе предполагаете, а заметки сделают вторые учения быстрыми. Затем запланируйте следующие — раз в квартал — разумный интервал, а запись в календаре — механизм куда надёжнее благих намерений.
Политика хранения: история с запасом на проблему, которую вы ещё не заметили
Именно на политике хранения люди тихо теряют данные, которые у них ещё были. Хранить семь ежедневных снапшотов кажется ответственным решением, но защищает только от проблем, замеченных в течение недели. Тихое повреждение данных, база, обрезанная из-за бага, файл, удалённый в апреле и понадобившийся в июне, взломщик, который долго сидел тихо, прежде чем сделать что-то заметное — всё это обнаруживается спустя долгое время после того, как семь дней истекли, и к этому моменту каждый снапшот добросовестно содержит повреждение.
Стандартный ответ — многоуровневая политика, которая почти ничего не стоит, потому что благодаря дедупликации ежемесячный снапшот медленно меняющихся данных — это погрешность округления:
restic forget --keep-daily 7 --keep-weekly 5 --keep-monthly 12 --keep-yearly 2 --pruneborg prune --keep-daily=7 --keep-weekly=5 --keep-monthly=12 --keep-yearly=2 ::
borg compactДве механические детали стоит знать, потому что обе уже не раз заставали людей врасплох. В restic forget удаляет снапшоты из индекса, но не освобождает ни байта; данные на самом деле удаляет prune, поэтому этот флаг и стоит в команде. В Borg prune удаляет архивы, но репозиторий не уменьшается, пока не отработает borg compact — это отдельная команда как раз для того, чтобы её можно было запускать по отдельному расписанию.
И здесь есть настоящее противоречие с предыдущим разделом: очистке нужно удаление, а вы только что отняли удаление у клиента. Это правильный компромисс, а не изъян конструкции. Разрешается он так: очистку выполняют с бэкап-сервера, где репозиторий локальный, а у клиента нет права голоса — еженедельным таймером на целевой машине, вообще без участия рабочего сервера. Если это больше механики, чем вам хочется, альтернатива — второй SSH-ключ без --append-only, который хранится не на рабочем сервере и используется вручную раз в несколько месяцев. Чего делать не стоит — так это возвращать неограниченный ключ на сервер ради того, чтобы упростить cron-задачу.
Мониторинг — потому что сбой выглядит как тишина
Задача резервного копирования, которая перестала запускаться, выдаёт ровно то же, что и задача, которой нечего делать: ничего. Это определяющее эксплуатационное свойство бэкапов и причина, по которой большинство сломанных бэкапов остаются сломанными месяцами. Стоит настроить три вещи, в порядке возрастания ценности:
- Оповещение о сбое. Юнит systemd может при сбое запустить другой юнит с помощью
OnFailure=, а тот может отправить вам сообщение. Необходимо, но это самое слабое из трёх, потому что срабатывает, только когда что-то действительно запустилось и сообщило об ошибке. - Оповещение о возрасте. Спрашивайте у репозитория, когда он в последний раз что-либо получал, и поднимайте тревогу, если ответ старше суток-двух. Так ловятся замаскированный таймер, заполнившийся диск, переставшее резолвиться имя хоста — все сбои, которые не породили ни одной ошибки, потому что ничего так и не запускалось.
- Dead man’s switch («кнопка мертвеца»). Переверните логику: задача при успехе пингует URL, а внешний сервис поднимает тревогу, когда пинги прекращаются. Только этот вариант переживает исчезновение всего сервера целиком, а это тот сбой, о котором меньше всего хочется узнать от клиента.
Проверка, которая окупается сама, — проверка возраста, и это три строки:
#!/bin/sh
# alert if the newest snapshot is older than 36 hours
last=$(restic snapshots --json --latest 1 | grep -o '"time":"[^"]*"' | head -1 | cut -d'"' -f4)
[ $(( $(date +%s) - $(date -d "$last" +%s) )) -lt 129600 ] || echo "BACKUP STALE: $last"Следите и за диском на целевом сервере. Заполненный том для бэкапов роняет каждую последующую задачу, и падает она в самом конце — после передачи данных, а это самый дорогой способ об этом узнать. Оповещение на восьмидесяти процентах даёт вам недели форы; альтернатива не даёт ни одной.
Сбои, из-за которых люди на самом деле теряют данные
Примерно в порядке того, как часто они превращают бэкап в сувенир:
- База данных копировалась на ходу. Каталог данных InnoDB, захваченный посреди записи, восстанавливается в базу, которая не запускается, и происходит это тихо — бэкап завершается, все файлы на месте, размер выглядит правильным. Сначала дамп. Недаром это третий шаг.
- Пароль был только на сервере. Безупречный зашифрованный репозиторий, чья парольная фраза жила в
/rootна сгоревшей машине, — это высококачественный шум. Как и парольная фраза в менеджере паролей, единственная копия которого синхронизируется через тот же самый сервер. - Никто ни разу не восстанавливался. Список исключений оказался слишком агрессивным, дамп базы три месяца был пустым, путь был неверным. Всё это невидимо до восстановления и очевидно уже через десять минут после его начала.
- Бэкапы были на том же аккаунте. Тот же провайдер, тот же логин, те же платёжные отношения. Спасает при отказе диска и больше ни в чём.
- Клиент мог удалять. Шифровальщик удаляет бэкапы до того, как тронет данные, потому что в этом вся его бизнес-модель. Append-only — или смиритесь с тем, что этот сценарий не покрыт.
- Целевой сервер тихо заполнился. Политика хранения так ничего и не чистила, или в список включений добавили новый большой каталог. Каждая следующая задача падает, и если вы не следили за возрастом бэкапов, никто вам об этом не скажет.
- В бэкап попал блокчейн. Бэкапить каталог данных Bitcoin или Monero — значит платить за хранение публичного реестра, который можно бесплатно скачать заново. Бэкапьте кошелёк и конфигурацию, а цепочку исключайте.
- Список исключений что-то съел. Шаблон, рассчитанный на
/var/cache, заодно совпал с каталогом кеша приложения, где лежало состояние, которое приложение так и не восстановило. Один раз внимательно прочитайте список файлов вашего первого бэкапа. - Для восстановления нужен был ключ, лежавший в репозитории. Режим
repokeyв Borg хранит ключ внутри репозитория, что удобно ровно до тех пор, пока вы не потеряете сам репозиторий. Один раз, в самом начале, сделайтеborg key exportкуда-нибудь в другое место.
Чего бэкапы не делают
Четыре честных ограничения, потому что руководство, которое перехваливает бэкапы, поступает так же, как непроверенный бэкап.
Бэкап — это не высокая доступность. Он сокращает простой, но не предотвращает его. Восстановить сервер — значит развернуть машину, скачать данные, распаковать их и заново всё настроить — легко час для чего-то небольшого и куда больше, если до этого вы не пробовали ни разу. Если час простоя неприемлем, ответ — машина в горячем резерве, а бэкап всё равно нужен для всего, что она добросовестно реплицирует, включая ваши ошибки.
Шифрование не делает копию невидимой. Оно делает её нечитаемой, а это не одно и то же. Тот, кто хостит репозиторий, по-прежнему видит, что он существует, какого он размера и когда менялся в последний раз — а аккаунт, с которого он оплачивается, — такая же идентифицирующая деталь, как и любая другая. Если причина, по которой вы арендовали основной сервер без документов, всё ещё в силе, она относится и ко второй машине; тот же способ оплаты подходит для обеих.
Шифрование при хранении — это не сквозное шифрование во всех смыслах. restic и Borg защищают репозиторий. Они не защищают работающий сервер, где данные по определению лежат в открытом виде, и не помогут, если парольная фраза скомпрометирована вместе с машиной, на которой она хранилась. Полнодисковое шифрование на исходном сервере и парольная фраза, хранящаяся вне обеих машин, — взаимодополняющие меры, а не дублирующие.
И копия в другой стране — это трение, а не иммунитет. Юрисдикция меняет лишь то, кто кого должен просить и сколько времени это займёт — подробно об этом говорится в статье об оффшорном хостинге. Обе машины здесь работают по одной и той же чётко изложенной политике допустимого использования, с одними и теми же жёсткими запретами в её основе. Что такая схема действительно даёт — это то, что никакой одиночный сбой — умерший диск, неудачная команда, взломщик, замороженный аккаунт — не заберёт оригинал и копию одновременно. Именно для этого и нужен бэкап, и этого достаточно.

