У вас есть бэкапы? Хорошо, лёгкий вопрос. А когда вы в последний раз восстанавливали из них хотя бы один файл? Вот она неловкая пауза, понимаю… Между «бэкап есть» и «бэкап восстанавливается» находится пропасть, и в неё регулярно падают даже большие компании. Под катом две страшилки из реальной жизни и разбор того, как проверить свои бэкапы до того, как восстанавливать их придётся на боевом сервере.

Две страшилки из реальной жизни

«История игрушек 2» могла бы быть другой

Первая история из 1998 года. На финальном этапе производства «Истории игрушек 2» кто-то в Pixar выполнил /bin/rm -r -f * не в том каталоге, и примерно 90% фильма удалилось на глазах у команды. Бэкапы, конечно, были, но есть два «но». Система резервного копирования не работала (переполненный диск маскировал ошибки), а восстановленная копия оказалась частично битой, и это вскрылось не сразу. Фильм спасла копия на домашнем компьютере технического директора Галин Сусман, которая работала из дома после рождения ребёнка. Машину аккуратно упаковали и привезли в студию на автомобиле, как самый ценный груз в истории компании. 

Однако домашняя копия оказалась лишь самой свежей из нескольких неполных версий. Команда сравнила её с двухмесячным бэкапом и деревом, собранным из уцелевших файлов на компьютерах аниматоров, результатов тестовых рендеров и других кусков. Из условных 100 тыс. файлов принять сразу удалось около 70 тыс., а оставшиеся 30 тыс. сотрудники Pixar проверяли вручную. Даже после этого часть материалов восстановить всё равно не удалось. 

Ирония в том, что спустя несколько месяцев сценарий «Истории игрушек 2» почти полностью переписали, после чего значительную часть фильма сняли заново…

Маленькая трагедия большого GitLab

Вторая история посвежее — 31 января 2017 года инженер GitLab, чиня реплику PostgreSQL, выполнил rm -rf каталога данных не на вторичном сервере, а на основном. Команду остановили через пару секунд, но из 300 ГБ осталось 4,5 ГБ. Дальше началось самое интересное, и я рекомендую прочитать их постмортем целиком.  

Дампы pg_dump создавались бинарниками версии 9.2 против PostgreSQL 9.6, из-за несовпадения major-версий завершались ошибкой, свежих дампов в S3 не было, а письма об ошибках отклонялись из-за DMARC. GitLab спас ручной LVM-снапшот, сделанный за шесть часов до инцидента для совершенно другой задачи. Однако были потеряны данные, созданные между 17:20 и 00:00 UTC, а это как минимум 5 тыс. проектов, 5 тыс. комментариев и около 700 пользователей. 

Обратите внимание, в обеих историях бэкапы формально существовали. Проблема была в том, что никто не проверял восстановление. Так что делать? 

Правило 3-2-1 и три уровня проверки 

Начнём с базы — с правила 3-2-1, которое придумал не сисадмин, а фотограф Питер Крог, автор книги The DAM Book про управление фотоархивами. Он обучал всех держать три копии данных (1 основная + 2 резервные копии), на двух разных типах носителей, а одну копию удалённо. 

Сейчас, так как шифровальщики первым делом уничтожают именно бэкапы, появилась версия 3-2-1-1-0. Добавляется «1» копия, которую нельзя перезаписать или удалить (в объектных хранилищах это Object Lock, а на железе это просто отключённый от сети диск), и «0» ошибок при автоматической проверке восстановления. Про эту проверку, а точнее, про её уровни, и поговорим дальше.

Уровень 1. Целостность архива

Самое дешёвое, что можно сделать, это убедиться, что архив вообще читается (смотрите на код возврата): 

tar -tzf backup.tar.gz >/dev/null

tar -tzf backup.tar.gz | head -50

Если список файлов вывелся без ошибок, архив жив. Для серьёзных инструментов есть встроенная верификация. У restic:

restic -r /backup/restic-repo check

restic -r /backup/restic-repo check --read-data-subset=10%

Первая команда проверяет структуру репозитория, вторая реально скачивает и сверяет контрольные суммы 10% данных, что полезно для больших репо, подробности в мануале

У borg аналог называется borg check с флагом --verify-data, который криптографически проверяет содержимое, правда, читает при этом весь репозиторий, так что запускайте ночью: 

borg check --verify-data /backup/borg-repo

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

Если бэкапы у вас самописные через tar и cron, то добавьте в скрипт манифест контрольных сумм:

cd /backup

sha256sum backup.tar.gz > backup.tar.gz.sha256

sha256sum -c backup.tar.gz.sha256

Команда ловит битую запись на диске, оборванную закачку в облако или порчу при переносе. Проверять хеш лучше уже после копирования в хранилище. Если файл уехал в S3, MinIO или на другой сервер, скачайте его обратно в тестовый контур и проверьте там. 

Уровень 2. Учебная тревога

Лучший тест бэкапа — это его восстановление. Рекомендую завести ритуал, по которому раз в месяц вы будете поднимать бэкап на отдельной машине. Под это дело отлично подходит недорогая VDS, которую можно создать на час репетиции и после удалить.

Для проверки файловых бэкапов просто восстановите архив в отдельный каталог и сравните с оригиналом:

tmpdir="$(mktemp -d)"

tar -xzf backup.tar.gz -C "$tmpdir"

find "$tmpdir" -maxdepth 2 -type f | head -50

Если бэкап делаете rsync-ом, обратную проверку можно гнать тем же rsync, он покажет различия без записи:

rsync -aHn --delete --itemize-changes /etc/ "$tmpdir/etc/"

Дальше попробуйте поднять сервис. Разверните конфиги nginx, запустите приложение и откройте сайт. Именно на этом шаге можно увидеть, что в бэкапе нет .env с секретами, что права на каталоги потерялись или что вы забыли бэкапить crontab. Можно проверить так: 

curl -fsS http://127.0.0.1/health 

И засеките время. GitLab в истории выше восстанавливал сервис около 18 часов, в том числе потому, что копирование со снапшота на медленных дисках заняло много времени. Если ваша база весит сто гигабайт, а вы никогда не замеряли восстановление, значит, вы не знаете своего времени простоя. Команда: 

/usr/bin/time -v tar -xzf backup.tar.gz -C "$tmpdir"

Когда ритуал обкатан, его можно автоматизировать через ночной systemd timer или cron-задачу, которая восстанавливает свежую копию на тестовой машине, прогоняет пару проверок и шлёт результат в мессенджер. Это и есть тот самый «ноль» из правила.

Уровень 3. Мониторинг самого бэкапа

В идеальном и прекрасном мире бэкап должен сам сообщать, что он жив. Для этого лучше использовать приём «Переключатель мертвеца», по которому задача после успешного завершения дёргает внешний URL, а если сигнала нет сутки, то сервис шлёт вам тревогу. Готовый вариант — это Healthchecks, а связку можно собрать и самому:

#!/bin/bash

set -euo pipefail




BACKUP_DIR="/backup"

HC_URL="https://hc-ping.com/your-uuid"




# Ищем свежий непустой дамп за последние 24 часа

if ! find "$BACKUP_DIR" -type f -name '*.dump*' -mtime -1 -size +1k -print -quit | grep -q .; then

    echo "backup missing or empty" >&2

    curl -fsS -m 10 --retry 3 -o /dev/null "$HC_URL/fail" || true

    exit 1

fi




# Всё хорошо, сообщаем внешнему наблюдателю

curl -fsS -m 10 --retry 3 -o /dev/null "$HC_URL"

Проверка -size +1k отсекает дампы «в несколько байт», которые погубили GitLab. Заодно добавьте в скрипт бэкапа set -euo pipefail и проверку кода выхода pg_dump, чтобы система сигналила о падении. 

Базы данных — отдельная песня

Дамп базы нужно проверять восстановлением во временную базу, в идеале это отдельная ВМ!!! Используйте pg_dump той же major-версии, что и сервер, или более новой, если это поддерживается вашей версией PostgreSQL. Для проверки версий: 

pg_dump --version

psql -d postgres -Atc 'SHOW server_version;'

Для custom-формата, который обычно получают через pg_dump -Fc, сначала можно посмотреть оглавление архива:

pg_restore --list dump.dump > /tmp/dump.list 

head -50 /tmp/dump.list 

Команда показывает, что архив читается, но не заменяет восстановление. Для нормальной проверки: 

dropdb --if-exists restore_check

createdb --template=template0 restore_check




pg_restore \

  --exit-on-error \

  --no-owner \

  --no-privileges \

  -d restore_check \

  dump.dump

Флаги --no-owner и --no-privileges удобны для проверки данных в тестовой базе, потому что восстановление не будет падать на отсутствующих ролях и грантах. Для полноценной репетиции аварии роли и права тоже нужно проверять отдельно:

pg_dumpall --globals-only > pg_globals.sql 

Если дамп обычный SQL-файл, его нужно загружать через psql, а не через pg_restore: 

dropdb --if-exists restore_check

createdb --template=template0 restore_check




psql -X -v ON_ERROR_STOP=1 -d restore_check -f dump.sql

Не забудьте про ON_ERROR_STOP=1, потому что без него можно получить ошибки в середине вывода. 

После загрузки проверьте не только список таблиц, а прикладные признаки: 

psql -X -d restore_check -c "SELECT count(*) FROM users;"
psql -X -d restore_check -c "SELECT count(*) FROM orders;"
psql -X -d restore_check -c "SELECT max(created_at) FROM orders;"

Если в базе есть расширения, проверьте и их:

psql -X -d restore_check -c "SELECT extname, extversion FROM pg_extension ORDER BY 1;" 

Для физических PostgreSQL-бэкапов есть утилита pg_verifybackup. Она проверяет base backup, созданный pg_basebackup, и если есть манифест, то backup_manifest: 

pg_verifybackup /backup/postgres/base/2026-08-31

Если WAL лежит отдельно, укажите каталог с WAL:

pg_verifybackup --wal-path=/backup/postgres/wal /backup/postgres/base/2026-08-31

И помните, что логический дамп — это не единственный ваш вариант, для больших баз смотрите в сторону физических копий и PITR через архив WAL.

Замерить время восстановления можно через:

/usr/bin/time -v pg_restore -d restore_check dump.dump

Для MySQL логика та же — создаёте пустую базу и наваливаете дамп — команды тут. Отдельно запишите процедуру восстановления текстом и положите рядом с бэкапами и во внутреннюю вики. Поверьте, пригодится, когда горит ночью. 

Секреты и конфиги тоже часть восстановления 

Отдельно проверьте секреты и конфиги — зачастую приложение после восстановления не стартует из-за того, что .env, ключи шифрования, kube secrets, crontab или конфиги nginx жили вне бэкапа. Для файлового бэкапа это можно проверить руками: 

test -f /tmp/restore-test/etc/nginx/nginx.conf

test -f /tmp/restore-test/opt/app/.env

test -d /tmp/restore-test/var/www/uploads

Для приложения лучше держать короткий список обязательных файлов. 

Вместо заключения

Ну и наконец, как вы любите, чеклист: 

  1. Раз в месяц восстанавливайте бэкап на отдельной машине и запускайте сервис, а не просто разворачивайте архив. 

  2. Прогоняйте встроенную верификацию: restic check --read-data-subset, borg check --verify-data или хотя бы tar -tzf.

  3. Дампы баз проверяйте восстановлением во временную базу и сверяйте версии pg_dump и сервера.

  4. Следите за свежестью и размером копий — файл нулевого размера это не бэкап.

  5. Настройте мониторинг, чтобы о сломавшемся бэкапе узнавали первым вы, а не ваши пользователи.

  6. Держите одну копию офлайн, на случай шифровальщика.

Кстати, такие проверки не нужно делать каждый день — это дорого и как бы не имеет смысла. Но хотя бы раз в месяц делайте пробное восстановление. 

Когда вы в последний раз проверяли свои бэкапы? Делитесь в комментариях, заодно расскажите, какие сюрпризы нашли с помощью проверки.

© 2026 ООО «МТ ФИНАНС»