Снапшот — это не бэкап
Снапшот в вашей панели управления — вещь по-настоящему полезная, и его стоит делать перед каждым рискованным изменением. Но это не бэкап, и разница здесь не в занудстве — в ней весь список ситуаций, которые вы пытаетесь пережить. Снапшот живёт на том же хосте, внутри того же аккаунта, за теми же учётными данными, что и машина, которую он защищает. Он хорошо отвечает ровно на один вопрос: как отменить то, что случилось двадцать минут назад? На все остальные вопросы он не отвечает вовсе. Если аккаунт пропал, снапшот пропал вместе с ним. Если регион недоступен, недоступен и откат. Если злоумышленник добрался до панели, он добрался и до снапшотов. А если данные были повреждены уже на момент снятия снапшота, вы с похвальной точностью сохранили именно это повреждение.
То же самое касается двух вещей, которые чаще всего принимают за бэкап. RAID — это не бэкап: он защищает от смерти диска и с той же скоростью реплицирует rm -rf на зеркало. Репликация — не бэкап по той же причине: она устроена так, чтобы как можно быстрее сделать вторую копию идентичной первой, в том числе в тот момент, когда первая копия только что уничтожена. На no-KYC-хостинге пункт про аккаунт в этом списке звучит острее, чем где бы то ни было: регистрация — это логин, пароль и восемь кодов восстановления, без единого email или документа во всей цепочке, а значит, и подниматься по лестнице поддержки некуда, если вы их потеряете. Это продукт, работающий именно так, как задуман. А ещё это значит, что по-настоящему важна та копия, которая недоступна по тем же учётным данным, которые вы можете потерять.
3-2-1, переписанное для сервера, который никто не может опознать
Старое правило по-прежнему в силе: три копии всего, что вам дорого, на двух разных системах, причём одна — вне хоста. Современное расширение, которое записывают как 3-2-1-1-0, добавляет два пункта, важнее которых в 2026 году нет: одна копия должна быть неизменяемой или офлайновой, и ноль ошибок при проверке. Применительно к одному серверу это звучит так: копия первая — это ваши боевые данные; копия вторая — снапшот на стороне провайдера или надстройка ежедневных зашифрованных бэкапов, которая без каких-либо усилий с вашей стороны закрывает обычную ошибку в два часа ночи; копия третья — зашифрованный репозиторий на второй машине, в другом регионе, у которой первая машина не имеет никаких прав на удаление. Только третья копия переживает потерю аккаунта первой машины, и только третья копия по-настоящему принадлежит вам — в том смысле, что никого больше нельзя принудить её предоставить.
Отнеситесь осознанно к тому, что на самом деле означает «вне хоста». Другая стойка в том же здании не является «вне хоста» ни в каком значимом смысле, а другой регион под тем же правом — это лишь половина дела: один и тот же юридический инструмент, способный дотянуться до одной машины, не должен автоматически дотягиваться до обеих. Когда на выбор 6 регионов, это решение вы принимаете один раз, при развёртывании, и больше к нему не возвращаетесь. Стоит сказать прямо: два сервера здесь — это всё равно два сервера у одного провайдера, а значит, коррелированный риск, какой бы хорошей ни была изоляция между ними. Если ваша модель угроз всерьёз включает потерю нас, третья копия должна находиться совсем в другом месте — на домашней машине, на стойке у друга, у другого хостера на другом континенте. Всё в этом руководстве работает одинаково независимо от того, где физически находится принимающая машина; меняется только адрес в одной переменной окружения.
SP·03Шифруйте прежде, чем данные покинут машину
Пункт назначения должен быть местом, которое хранит байты, но не может их прочитать. Это не политика, которую вы принимаете от провайдера, — это свойство, которое вы выстраиваете сами: данные разбиваются на чанки, сжимаются, шифруются и аутентифицируются прямо на исходной машине, и то, что уходит по проводу, уже непрозрачно. Режимы Borg repokey и keyfile шифруют каждый чанк алгоритмом AES-256 в режиме счётчика и аутентифицируют его, а варианты -blake2 заменяют HMAC-SHA256 на BLAKE2b, который заметно быстрее на современных 64-битных процессорах. Содержимое файлов, их имена и манифест архива, перечисляющий всё это, — всё зашифровано; на принимающей стороне остаются лишь пронумерованные файлы сегментов и индекс, который она не в состоянии интерпретировать. restic даёт ту же картину, но на другой внутренней механике. В любом случае правильная ментальная модель такая: вы арендуете дисковое пространство, а не доверие.
Две честные оговорки. Первая: что всё же утекает. Тот, кто владеет диском на принимающей стороне, видит, сколько данных приходит и когда именно. Размер репозитория и время записи видны, даже когда содержимое — нет, и для большинства людей это неважно, а для некоторых — важно. Вторая: шифрование данных на принимающей машине в состоянии покоя — LUKS на бэкап-сервере — защищает от ситуации, когда диск физически покидает здание, а не от работающего хоста, поэтому оно дополняет шифрование на источнике, а не заменяет его. И вот часть, которая губит людей: в режиме repokey материал ключа хранится внутри самого репозитория, обёрнутый вашей парольной фразой, так что потеря репозитория означает и потерю ключа; в режиме keyfile ключ живёт только на исходной машине, так что потеря исходной машины означает потерю всех архивов, которые вы когда-либо делали. Ни один из вариантов не безопасен, пока вы не экспортировали ключ и не положили экспорт туда, где нет ни одной из этих двух машин. Сделайте это на третьем шаге, а не «как-нибудь потом».
Borg, restic, rclone: выберите один и понимайте почему
Здесь правильных ответов два, и выбор на самом деле определяется приёмной стороной. Borg делает дедупликацию на уровне чанков, сжатие и аутентифицированное шифрование, и — именно поэтому он выбран ниже в качестве рабочего примера — поставляется с настоящим серверным append-only-режимом, который достаётся вам бесплатно поверх обычного SSH, без демона и без лишнего открытого порта. Ему нужен borg, установленный на обоих концах, и общается он по собственному протоколу. restic даёт те же гарантии, но не привязан к конкретному бэкенду: SFTP, S3-совместимое объектное хранилище, Backblaze B2 или собственный rest-server. На простой SFTP-цели ничего устанавливать не нужно, что удобно, но append-only в этом случае держится на rest-server --append-only или политике бакета, а не на ограничении на уровне SSH. Практическое правило простое: для сервера под вашим контролем — Borg, для объектного хранилища или когда нужен один инструмент сразу для нескольких разных бэкендов — restic.
Что не стоит использовать — потому что именно так чаще всего всё и ломается. rclone — это инструмент синхронизации. Его remote crypt действительно даёт шифрование на стороне клиента, но синхронизация распространяет и удаления: файл, который вы потеряли в 03:00, будет добросовестно удалён и на принимающей стороне в 03:15 — а это ровно тот сбой, который вы пытались пережить. Он отлично подходит, чтобы отправить уже готовый репозиторий Borg или restic в третье место, но сам по себе бэкапом не является. То же возражение касается голого rsync --delete в cron — это зеркало, переодетое в бэкап. tar | gpg в удалённый путь — это уже настоящий бэкап, но без дедупликации, без логики хранения и с восстановлением, которое означает прохождение всей цепочки инкрементов ради одного-единственного файла. Используйте инструменты, созданные именно для этой задачи, — они упакованы в каждом дистрибутиве, упомянутом в этом руководстве.
Append-only, или как злоумышленник удаляет заодно и ваши бэкапы
Вот сценарий, который отличает систему бэкапов от скрипта для бэкапов. Кто-то получает root на вашей боевой машине — через приложение, через зависимость, через утёкший ключ, неважно как именно. Первое, что делает грамотный ransomware, и первое, что делает грамотный человек, — это ищет бэкапы, потому что жертва с рабочими бэкапами не платит и не паникует. Ваша ночная задача запускается без присмотра, а значит, её учётные данные обязательно лежат на этой же машине. Если задача может удалять архивы, значит, их может удалить и злоумышленник, — и вы узнаете об этом ровно в тот момент, когда они вам понадобятся. Это не гипотетический сценарий отказа, а самый обычный.
Исправление небольшое, и это самая важная строка во всём этом руководстве. На принимающей машине привяжите автоматический ключ к единственной команде в authorized_keys: command="borg serve --append-only --restrict-to-path /srv/borg",restrict. Теперь этот ключ умеет ровно одно — добавлять данные в репозиторий по одному конкретному пути. Он не может получить шелл, не может пробросить порт, не может посмотреть вашу файловую систему и не может удалить ни единого архива. Ключевое слово restrict (OpenSSH 7.2 и новее) одним словом отключает выделение pty и все виды проброса, так что ограничение не рассыпается по мере появления новых опций. А теперь честная оговорка, которую пропускает большинство туториалов: пока действует режим append-only, borg prune и borg compact выглядят так, будто отработали успешно, но на деле ничего не освобождают — удаление фиксируется в транзакции, которую откатывает следующая же append-only-сессия. Поэтому для политики хранения нужен второй, ничем не ограниченный ключ, которым вы пользуетесь вручную с ноутбука и который никогда не хранится на исходной машине. Два ключа, две задачи: ночной умеет только писать; обслуживающий умеет удалять и живёт там, куда скомпрометированная машина дотянуться не может. Держите этот второй ключ под рукой ещё для одной цели — задача, прибитая на середине выполнения, оставляет за собой зависшую блокировку, а borg break-lock — это тоже запись.
Где должна жить вторая копия
Бэкапы — это едва ли не единственная нагрузка, где задержка не имеет значения, так что забудьте привычный инстинкт ставить машину поближе к пользователям и выбирайте, исходя из права и независимости. Другая страна относительно продакшена — это минимум; другая правовая семья — уже лучше. Каждый наш регион отвечает на свой вопрос: Румыния — флагман, где уведомления DMCA попросту не обрабатываются; Швейцария стоит вне ЕС, за необычно строгими законами о защите данных; в Исландии действует фреймворк IMMI; в Панаме нет обязательного закона о хранении данных и нет зелёного коридора для иностранных запросов; Малайзия выносит копию данных полностью за пределы досягаемости альянса Five Eyes; а Нидерланды — это узел пиринга. Материал «Какую офшорную локацию выбрать?» разбирает эти компромиссы как следует; а для цели бэкапов ответ обычно один — «там, где нет продакшена». Порты не тарифицируются ни на одном тарифе, так что первую полную заливку ограничивает лишь скорость, с которой исходная машина способна читать собственный диск, а не какая-то квота трафика, которую приходится экономить.
С размером всё куда менее драматично, чем принято думать. Дедупликация вместе с zstd означает, что репозиторий обычно составляет лишь долю от объёма исходных данных, а после первого запуска по проводу идут только изменившиеся чанки — нагруженный сервер на 40 ГБ с обычной интенсивностью изменений укладывается в несколько сотен мегабайт за ночь, так что год ежедневных архивов обходится по месту на диске куда дешевле, чем год ежедневных tar-архивов. Младший тариф в линейке — вполне достойная цель; переходите на более крупный, только если храните глубокую историю чего-то по-настоящему большого, и сверяйтесь с точными характеристиками на странице тарифов, а не с цифрой, вписанной прямо в руководство, где она неизбежно устареет. Одно правило насчёт самой машины: не давайте ей больше никаких задач. Ни веб-сервера, ни базы данных, ни единого публичного сервиса, кроме SSH по ключу. Бэкап-сервер, на котором заодно крутится сторонний проект, — это бэкап-сервер с поверхностью атаки этого стороннего проекта, и он должен получить полный набор мер первого часа ещё до того, как примет первый архив.
SP·07Бэкап, который вы не восстанавливали, — это просто слух
Системы бэкапов редко ломаются громко. Они ломаются потому, что exclude-паттерн незаметно проглотил каталог с данными, или потому, что база данных была скопирована файл за файлом прямо во время записи, и дамп восстанавливается в искорёженную таблицу, или потому, что таймер молча падает уже шесть недель из-за обновления дистрибутива, а почтовый спул никто не читает. Единственная проверка, которая ловит всё это, — это восстановление. Делайте его по расписанию: возьмите случайный архив, распакуйте его во временный каталог, сравните горстку файлов с продакшеном, поднимите базу данных из дампа и выполните запрос, а затем запишите, сколько времени всё это заняло. Это число и есть ваше реальное время восстановления — а не то, которое вы себе предполагали, — и это единственная цифра, которую стоит называть самому себе в три часа ночи. Хотя бы раз проведите эту тренировку с чистой машины, потому что это и есть настоящий сценарий: свежий VPS, парольная фраза из менеджера паролей, экспортированный ключ оттуда, куда вы его положили, — и больше ничего.
Мониторинг заслуживает того же скепсиса, который вы применили ко всему остальному стеку. Стандартный совет — сторонний dead man's switch, которому ваша задача шлёт пинг при успешном завершении, — а это значит, что внешнему сервису вы тихо сообщаете имена своих хостов, своё расписание и моменты, когда с вашей инфраструктурой что-то не так, — довольно странная вещь для машины, которую вы намеренно купили, нигде не оставив свою личность. Вам это не нужно. Свежесть бэкапа можно проверить со стороны принимающей машины вообще без ключа: у самого нового файла сегмента в репозитории есть метка времени, так что пятистрочный cron на бэкап-сервере, который поднимает шум, если за двадцать пять часов ничего не пришло, не стоит вам ничего и ничего не раскрывает. У целостности тоже есть проверка, не требующая ключа, — borg check --repository-only выполняется локально на принимающей машине и проверяет структуру сегментов, ни разу не заглядывая в ваши данные. Глубокий проход с --verify-data запускайте время от времени с исходной машины обслуживающим ключом — там, где ему и место.
Сколько это стоит и какое место занимает в общей картине
Экономика здесь даже не близка к спорной. Второй VPS от $8.00/мес, оплачиваемый с того же предоплаченного баланса, который пополняется от $30.00, выходит в онлайн примерно за 15 min — и всё это против стоимости потери всего, что есть на первой машине. Используйте его вместе с бэкапами на стороне провайдера, а не вместо них: надстройка ежедневных зашифрованных бэкапов закрывает случай «удалил не ту папку» вообще без усилий с вашей стороны и без раздумий в три часа ночи, а то, что вы построили здесь, — это копия, которая остаётся вашей, когда исчезает аккаунт, регион или сам провайдер. Они закрывают разные сбои, и ни один не заменяет другой, — в этом весь смысл считать до трёх.
Стоит закончить тем, какое место бэкапы занимают относительно всего остального, — ведь это четыре меры контроля, и каждая отказывает независимо от прочих. Первое — это то, что о вас знает хостер, и здесь это почти ничего: логин и крипто-баланс, чему посвящена статья анонимная оплата хостинга. Второе — это то, чьё право применяется, что определяется тем, где физически стоит железо, а не тем, где находитесь вы. Третье — это то, что позволяет сама машина: это первый час после развёртывания, и настраивать его — только вам самим. А четвёртое — то, что переживает саму машину: единственная из четырёх мер, которая является обещанием, данным самому себе в будущем, и единственная, о которой никто не напомнит вам, пока не станет слишком поздно её выполнять. Час сегодня вечером и тренировка восстановления в календаре. Вот и всё.
SP·09Шаг за шагом
-
01
Разверните бэкап-сервер и не давайте ему больше никаких задач
Разверните второй VPS в регионе, который не совпадает с регионом продакшена, и относитесь к нему как к устройству с одной-единственной задачей. Прогоните на нём стандартный чек-лист первого часа — SSH только по ключу, firewall с default-deny на обоих семействах IP, автоматические обновления безопасности, — а затем не открывайте больше ничего. Единственный входящий порт на этой машине — SSH.
ssh root@198.51.100.7 apt update && apt full-upgrade -y hostnamectl set-hostname vault-01 apt install -y borgbackup ufw unattended-upgrades ufw default deny incoming && ufw default allow outgoing ufw limit 22/tcp && ufw enable
-
02
Создайте ограниченную учётную запись borg с двумя ключами
Сгенерируйте на исходной машине две пары ключей: одну для ночной задачи (без парольной фразы — ей нужно запускаться без присмотра) и одну для обслуживания, которую вы храните только на своём ноутбуке. На целевой машине создайте непривилегированного пользователя
borgи привяжите каждый ключ к принудительной команде. Ключ задачи получает--append-only; обслуживающий ключ — нет. Именно этот единственный файл не позволяет злоумышленнику на исходной машине стереть вашу историю.# on the source ssh-keygen -t ed25519 -N "" -f /root/.ssh/borg_job -C "job@edge-01" # on your laptop ssh-keygen -t ed25519 -f ~/.ssh/borg_maint -C "maint@laptop" # on the target adduser --disabled-password --gecos "" borg install -d -m 700 -o borg -g borg /home/borg/.ssh /srv/borg cat > /home/borg/.ssh/authorized_keys <<'EOF' command="borg serve --append-only --restrict-to-path /srv/borg",restrict ssh-ed25519 AAAA...job@edge-01 command="borg serve --restrict-to-path /srv/borg",restrict ssh-ed25519 AAAA...maint@laptop EOF chown borg:borg /home/borg/.ssh/authorized_keys chmod 600 /home/borg/.ssh/authorized_keys
-
03
Инициализируйте репозиторий, затем унесите ключ с обеих машин
Инициализируйте репозиторий обслуживающим ключом, с ноутбука, — создание репозитория не является добавлением данных. Один репозиторий на одну исходную машину делает политику хранения тривиальной, а радиус поражения — небольшим. Затем экспортируйте ключ дважды, в двух форматах, и унесите оба экспорта туда, где нет ни исходной, ни целевой машины: в менеджер паролей, на зашифрованную флешку, на лист бумаги в ящике стола. Репозиторий, ключ от которого существует только внутри него самого, — это просто орёл или решка.
export BORG_REPO='ssh://borg@198.51.100.7/srv/borg/edge-01' export BORG_RSH='ssh -i ~/.ssh/borg_maint' borg init --encryption=repokey-blake2 borg key export :: /tmp/edge-01.borgkey borg key export --paper :: /tmp/edge-01.paper # move both off this machine, then shred the copies shred -u /tmp/edge-01.borgkey /tmp/edge-01.paper
-
04
Заморозьте состояние приложения, прежде чем читать диск
Копирование каталога живой базы данных файл за файлом даёт на выходе файл, похожий на базу данных, а восстановление из него больше похоже на работу с места преступления. Сначала сделайте дамп, включите дамп в бэкап, а сырой каталог с данными исключите. Та же логика применима к любому формату на диске, который вы не контролируете: сделайте дамп или остановите сервис на те несколько секунд, которые займёт снятие снапшота.
install -d -m 700 /var/backups/dumps mariadb-dump --single-transaction --quick --all-databases \ > /var/backups/dumps/mariadb.sql # PostgreSQL: # sudo -u postgres pg_dumpall > /var/backups/dumps/pg.sql chmod 600 /var/backups/dumps/*.sql
-
05
Напишите задачу бэкапа и повесьте её на таймер
Пусть скрипт будет скучным и падает громко. Парольная фраза берётся из файла с правами 600 через
BORG_PASSCOMMAND, так что она никогда не попадает в список процессов. Называйте архивы именем хоста и ISO-меткой времени, чтобы они сортировались по порядку. Обратите внимание, чего нет в этом скрипте: ниprune, ниdelete— ключ задачи всё равно не смог бы их выполнить. Таймер сPersistent=trueдогоняет пропущенные запуски после перезагрузки, а случайная задержка не даёт всем вашим машинам заливать данные в одну и ту же секунду.cat > /usr/local/sbin/borg-backup.sh <<'EOF' #!/bin/bash set -euo pipefail export BORG_REPO='ssh://borg@198.51.100.7/srv/borg/edge-01' export BORG_RSH='ssh -i /root/.ssh/borg_job -o BatchMode=yes' export BORG_PASSCOMMAND='cat /root/.config/borg/passphrase' borg create --stats --compression zstd,6 --one-file-system \ --exclude-caches --exclude '/var/cache/*' \ --exclude '/var/lib/mysql' --exclude '/home/*/.cache' \ ::'{hostname}-{now:%Y-%m-%dT%H:%M}' \ /etc /root /home /srv /var/www /var/backups/dumps EOF chmod 700 /usr/local/sbin/borg-backup.sh cat > /etc/systemd/system/borg-backup.service <<'EOF' [Unit] Description=Off-site Borg backup [Service] Type=oneshot Nice=10 IOSchedulingClass=idle ExecStart=/usr/local/sbin/borg-backup.sh EOF cat > /etc/systemd/system/borg-backup.timer <<'EOF' [Unit] Description=Nightly off-site Borg backup [Timer] OnCalendar=*-*-* 03:17:00 RandomizedDelaySec=900 Persistent=true [Install] WantedBy=timers.target EOF systemctl daemon-reload systemctl enable --now borg-backup.timer -
06
Чистите обслуживающим ключом, уплотняйте на целевой машине
Политика хранения не может выполняться с исходной машины, потому что её ключ работает в режиме append-only, и любые удаления с него молча откатятся назад. Вместо этого запускайте её с ноутбука, с любой удобной вам периодичностью — раз в месяц более чем достаточно.
pruneрешает, какие архивы оставить;compact— это то, что реально высвобождает место на диске. Сначала выполните с--dry-runи прочитайте список, прежде чем разрешать что-либо удалять.export BORG_REPO='ssh://borg@198.51.100.7/srv/borg/edge-01' export BORG_RSH='ssh -i ~/.ssh/borg_maint' borg prune --dry-run --list \ --keep-daily 7 --keep-weekly 4 --keep-monthly 6 borg prune --list --stats \ --keep-daily 7 --keep-weekly 4 --keep-monthly 6 borg compact --progress
-
07
Проведите тренировку восстановления, затем напишите runbook
Восстанавливайте до того, как это понадобится по-настоящему. Распакуйте реальный архив во временный каталог, сравните несколько файлов с оригиналами и загрузите дамп базы данных в одноразовую схему. Затем добавьте две проверки, которые работают без вашего участия: не требующую ключа сигнализацию о свежести на целевой машине и периодическую проверку целостности. Наконец, запишите три факта, которые понадобятся вам в ваш худший день: где хранится экспортированный ключ, где хранится парольная фраза и точные команды, приведённые ниже.
borg list :: mkdir -p /var/tmp/drill && cd /var/tmp/drill borg extract --list ::edge-01-2026-08-31T03:17 etc/nginx diff -r etc/nginx /etc/nginx && echo 'restore OK' # on the target, no key needed: borg check --repository-only /srv/borg/edge-01 find /srv/borg/edge-01/data -type f -mmin -1500 | head -1 # empty = stale


