В панели мониторинга горит зелёный статус:
Backup: OK
Последняя копия: сегодня, 03:00
Размер: 2.4 GBКажется, всё хорошо.
База резервируется каждый день.
На сервере лежат архивы.
Есть отдельная папка backups.
Можно поставить галочку:
Резервное копирование настроено.
А потом происходит авария.
Основная база потеряна.
Администратор берёт последний backup, запускает восстановление — и получает ошибку.
Например:
archive corruptedили:
decryption failedили:
role does not existили:
extension is missingили восстановление формально проходит, но приложение после него не запускается.
В этот момент выясняется неприятная вещь:
наличие backup и возможность восстановиться — не одно и то же.
Поэтому в production нас интересует не вопрос:
Создаётся ли резервная копия?
А гораздо более строгий:
Сможем ли мы из этой конкретной копии восстановить работающую систему?
Разберём, как превратить резервное копирование из формального cron-задания в проверяемый recovery-процесс.
Backup — это только половина задачи
Упрощённая схема обычно выглядит так:
Production DB
↓
pg_dump
↓
backup.pgdumpПосле появления файла система считает задачу выполненной.
Но между:
backup.pgdump существуети:
из него можно восстановить productionнаходится ещё несколько важных проверок.
Например:
Файл полностью записан?
Он не обрезан?
Шифрование завершилось?
Ключ расшифровки существует?
Checksum совпадает?
Формат понимает pg_restore?
Внутри действительно нужная база?
Создаются ли таблицы?
Восстанавливаются ли данные?
Есть ли необходимые роли?
Есть ли extensions?
После restore запускается ли приложение?Пока эти вопросы не проверены, backup является кандидатом на восстановление, а не доказанной точкой восстановления.
Самая опасная строка — «backup completed successfully»
Представим скрипт:
pg_dump database > backup.sqlПосле выполнения shell возвращает:
exit code 0Это хороший сигнал.
Но даже он не отвечает на вопрос:
Умеем ли мы вернуть из этого файла рабочий сервис?
Например, сама база может восстановиться.
Но production ещё использует:
пользовательские файлы;
Object Storage;
ключи шифрования;
environment;
внешние credentials.Получается:
Database restore: PASS
Application recovery: FAILПоэтому recovery нужно рассматривать на нескольких уровнях.
Что вообще означает «восстановиться»
Есть как минимум три разных уровня.
Уровень 1. Файл backup читается
Например:
checksum совпадает;
шифрование корректно;
архив открывается.Это проверка целостности артефакта.
Уровень 2. База реально восстанавливается
Создаём отдельную пустую БД:
restore_testи выполняем настоящий restore.
После него существуют:
таблицы;
индексы;
constraints;
данные.Уровень 3. Восстановленный продукт работает
Приложение подключается к восстановленной базе.
Проходят критические проверки:
пользователь существует;
проект открывается;
данные связаны;
миграции согласованы;
основные запросы выполняются.Именно третий уровень больше всего похож на то, что понадобится при реальной аварии.
Почему checksum недостаточно
Контрольная сумма чрезвычайно полезна.
Например:
SHA-256:
a8b5...При создании backup мы сохраняем:
backup file
+
manifestПозже считаем SHA-256 повторно.
Если:
expected != actualкопию нельзя считать целой.
Но если checksum совпал, мы доказали только:
Файл не изменился после создания checksum.
Мы ещё не доказали:
Внутри хороший backup.
Если сам архив был создан неправильно и сразу после этого для него рассчитали checksum, хеш прекрасно совпадёт с повреждённым файлом.
Поэтому:
checksum verificationи:
restore verificationрешают разные задачи.
Шифрование добавляет ещё один уровень риска
Production backup часто содержит:
пользователей;
email;
проекты;
финансовые данные;
внутреннюю переписку.Хранить такой архив в открытом виде не всегда разумно.
Поэтому backup можно шифровать.
Например:
pg_dump
↓
AES-256-GCM
↓
backup.pgdump.aesgcmТеперь к recovery добавляется критическая зависимость:
ключ шифрования.
Зашифрованный backup без ключа равен отсутствующему backup
Представим идеальную ситуацию:
100 ежедневных backupвсе:
SHA-256 PASSи все находятся на независимом сервере.
Но encryption key существовал только:
в /root/.env
на потерянном production VPS.После аварии имеем:
100 отличных
нечитаемых файлов.Поэтому backup strategy должна отдельно отвечать:
Где находится ключ?
Кто имеет к нему доступ?
Как он восстанавливается?
Что происходит при rotation?
Есть ли независимая защищённая копия?
Мы разделяем проверку backup на несколько последовательных барьеров
Практическая схема может выглядеть так:
Production DB
↓
Disk preflight
↓
Temporary dump
↓
Encryption
↓
SHA-256 manifest
↓
Decrypt verification
↓
Restore verification
↓
Application checks
↓
Publish as known-good backupКаждый следующий этап повышает уверенность.
Первый барьер — достаточно ли вообще места для backup
Есть довольно неприятный сценарий.
На VPS осталось:
3 GBТекущая база занимает:
2.7 GBCron начинает backup.
Через некоторое время диск заканчивается.
Результат:
backup оборван;
PostgreSQL тоже потерял
свободное место;
логи больше не пишутся.То есть сама процедура защиты данных создаёт production-инцидент.
Поэтому backup начинается с disk preflight
В одном из production-контуров, который мы проверяли, перед pg_dump оценивается размер PostgreSQL и требуемое свободное пространство.
Логика концептуально выглядит так:
DB size
↓
Estimated backup size
↓
Current free disk
↓
Safety reserveBackup разрешается только если после операции останется эксплуатационный запас.
Например:
free
-
estimated backup
>=
5 GiB reserveСамо конкретное значение зависит от проекта.
Важно другое:
backup не должен иметь право съесть последние свободные гигабайты production-сервера.
До этой проверки полезно удалить старые временные файлы
Предыдущий backup мог аварийно завершиться и оставить:
backup.tmpразмером несколько гигабайт.
Если следующий запуск считает свободное место, не очистив stale temporary files, он может либо ошибочно отказаться от backup, либо постепенно накопить мусор.
Поэтому последовательность:
cleanup stale temp
↓
check disk
↓
start new backupнадёжнее.
Второй барьер — писать сначала во временный файл
Плохой вариант:
backup-latest.pgdumpперезаписывается непосредственно во время создания новой копии.
Представим предыдущий backup рабочий.
Новый процесс начал запись:
backup-latest.pgdumpи на 63% аварийно завершился.
Теперь:
старого хорошего backup нет;
новый неполный.Мы сами уничтожили последнюю доказанно рабочую копию.
Поэтому используется принцип known-good backup
Например:
ritm-latest.pgdump.aesgcmявляется текущей подтверждённой копией.
Новый backup сначала создаётся отдельно:
ritm-next.pgdump.tmpДальше проходит проверки.
И только когда новая копия признана валидной:
new backup
↓
atomic replace
↓
latestЕсли любой этап завершился ошибкой:
старый known-good backup
остаётся нетронутым.Это маленькое архитектурное решение сильно меняет надёжность.
Третий барьер — проверить шифрование сразу после создания
Если backup зашифрован:
dump
↓
encrypt
↓
encrypted backupнельзя считать успешным только тот факт, что появилась .aesgcm.
Мы можем выполнить обратную операцию:
encrypted backup
↓
decrypt
↓
temporary decrypted dumpЕсли расшифровка не проходит, backup нельзя публиковать как рабочий.
Таким образом мы сразу проверяем:
ключ подходит;
формат корректен;
authentication tag проходит;
данные реально расшифровываются.Почему проверять нужно новым чтением файла
Если программа только что зашифровала byte stream и сама себе сказала:
Всё хорошо.
это слабее, чем:
закрыть output
↓
снова открыть готовый файл
↓
расшифровать
↓
проверитьВторой вариант ближе к реальной процедуре восстановления.
Четвёртый барьер — настоящий pg_restore
Для custom/archive-форматов pg_dump PostgreSQL предоставляет pg_restore.
Но наличие команды:
pg_restore backup.pgdumpв документации проекта ещё не означает, что конкретная копия успешно восстановится.
Нужно это выполнить.
Restore нельзя тестировать поверх production
Очевидно, но чрезвычайно важно.
Не:
production database
← restore testа:
temporary isolated databaseНапример:
app_restore_test_20260928Схема:
Backup
↓
Decrypt
↓
Create empty DB
↓
pg_restore
↓
Validation
↓
Drop test DBProduction при этом не изменяется.
Сам факт завершения pg_restore — ещё не конец
После restore полезно проверить данные.
Например:
SELECT COUNT(*) FROM users;
SELECT COUNT(*) FROM projects;
SELECT COUNT(*) FROM migrations;Но одних COUNT(*) тоже мало.
Лучше проверять бизнес-инварианты
Например:
Есть хотя бы один пользователь?
Нет проектов без владельца?
Последняя migration присутствует?
Количество критичных таблиц ожидаемое?
Существуют необходимые индексы?
Нет broken foreign keys?Или конкретный заранее известный контрольный объект.
Например:
system settings row existsЕщё сильнее — подключить приложение к восстановленной базе
Это уже очень полезный restore drill.
Вместо проверки исключительно SQL:
Restored PostgreSQL
↓
Test application instance
↓
/api/ready
↓
Critical queriesТеперь проверяется не только PostgreSQL.
Мы проверяем совместимость:
backup
+
schema
+
current applicationПочему это важно после развития проекта
Представим backup физически прекрасен.
Но новая версия приложения ожидает:
schema version 74В копии:
schema version 69В нормальном recovery процессе это может быть совершенно допустимо — нужно просто прогнать migrations.
Но если процедура этого не предусматривает, аварийное восстановление внезапно потребует импровизации.
Лучше знать это заранее.
Restore drill должен проверять и migrations
Например:
Backup from version 3.2
↓
Restore
↓
Run migrations
↓
Current version 3.4
↓
Application checksТеперь мы отвечаем на значительно более реальный вопрос:
Можем ли мы взять сохранённую копию и действительно поднять текущий продукт?
Резервная копия БД — это ещё не резервная копия продукта
Это особенно важно для современных веб-приложений.
Например PostgreSQL хранит:
users;
projects;
file metadata;
permissions.А S3/Object Storage:
documents;
images;
audio.Если восстановить только БД:
File #1842
storage_key = abc...а object abc... потерян, пользователь увидит запись о документе, но скачать его не сможет.
Нужно восстановить весь persistent state
В зависимости от продукта:
PostgreSQL
+
Object Storage
+
critical secrets
+
configurationИногда:
Redisвообще можно не резервировать, если он используется только как cache.
Но если там хранится уникальное состояние, ситуация другая.
Каждый persistent component нужно классифицировать заранее:
Можно восстановить из других источников?
или:
Его потеря означает потерю данных?
Очень полезно составить recovery inventory
Например:
| Компонент | Нужно backup | Почему |
|---|---|---|
| PostgreSQL | Да | Основные пользовательские данные |
| Object Storage | Да | Пользовательские файлы |
| Redis cache | Нет | Перестраивается |
| Application image | Не обязательно | Воспроизводится из release |
.env secrets | Да, безопасно | Без них часть данных/интеграций недоступна |
| TLS certificate | Обычно нет | Можно перевыпустить |
| Logs | Зависит | Нужны для аудита/диагностики |
Теперь disaster recovery становится конкретным.
Главное — не сохранять то, что можно воспроизвести
Если Docker image можно снова собрать из:
Git
+
release tag
+
Dockerfileон не так ценен, как:
пользовательская база.Backup должен в первую очередь защищать невоспроизводимые данные.
Но секреты являются особым случаем
Например данные в Object Storage зашифрованы application-level ключом.
Исходный код воспроизводится.
Файлы хранятся.
Но ключ исчез.
Получается:
source ✓
database ✓
objects ✓
key ✗Результат:
restore impossible.Поэтому recovery inventory должен учитывать зависимости между компонентами.
Можно сделать красивую карту восстановления
Например:
Encrypted DB Backup
│
├── requires → Backup Key
│
└── restores → PostgreSQL
│
├── requires → migrations
│
└── used by → Application
Object Storage Backup
│
└── requires → Storage credentialsТакая схема иногда полезнее десятка страниц документации.
Offsite backup должен быть действительно offsite
Допустим:
production:
VPS Abackup:
/opt/app/backups
на VPS AЭто полезно при:
случайном DROP;
ошибочной migration;
удалении записи.Но если:
VPS A потерян полностьюbackup исчезает вместе с ним.
Поэтому нужна независимая копия
В production-контуре, который мы проверяли, удалённая backup-цель отделяется от основного storage.
Идея:
Primary VPS
↓
encrypted backup
↓
independent offsite storageЭто защищает уже от другого класса аварий.
Но offsite не означает «копируем бесконечно»
Если каждый день хранить полный backup навсегда:
Day 1
Day 2
...
Day 1000storage будет расти бесконечно.
Нужна retention policy.
Например концептуально:
daily backups
→ ограниченное окноТочный срок определяется требованиями бизнеса.
Retention — это не только экономия места
Представим данные были повреждены:
10 дней назадНо никто этого не заметил.
Если хранится только:
последний backupон уже содержит повреждённое состояние.
Несколько исторических точек дают выбор:
T-1
T-2
T-7
...Для критичных систем могут использоваться и более развитые схемы — например continuous archiving и point-in-time recovery.
Но больше backup не всегда значит лучше
Есть две независимые характеристики:
Retentionи:
Verified recoverabilityМожно иметь:
100 непроверенных копийи ни одной рабочей.
И:
7 проверяемых копийс понятной процедурой восстановления.
В аварии второй вариант часто ценнее.
Нужно контролировать возраст последнего backup
Представим backup job перестал работать неделю назад.
Файлы в каталоге существуют.
Поэтому поверхностная проверка:
backups directory not emptyговорит:
OKХотя последняя копия:
7 days old.Это production-дефект.
Мониторинг должен проверять freshness
Например в одном из проверенных production-контуров монитор следит за возрастом последней успешной зашифрованной копии и считает её проблемной при превышении заданного окна — порядка суток с техническим запасом.
То есть проверяется не:
backup exists?а:
now - latest_verified_backup
<= allowed age?Это превращает молчаливый отказ cron в alert
Без monitoring:
Day 1:
backup ✓
Day 2:
job broken
Day 30:
аварияИ только тогда выясняется:
Последняя копия месячной давности.
С monitoring:
backup stale
↓
alertпроблему можно исправить до настоящей аварии.
Backup job тоже должен иметь наблюдаемое состояние
Например:
Started:
03:00:01
Dump:
PASS
Encrypt:
PASS
Checksum:
PASS
Decrypt verify:
PASS
Offsite upload:
PASS
Completed:
03:03:42При ошибке:
Stage:
OFFSITE_UPLOAD
Error:
timeoutЭто намного полезнее:
backup failed.Очень важно различать local backup и disaster recovery
Local backup отвечает:
Как быстро откатить данные после обычной ошибки?
Offsite:
Как восстановиться после потери основной инфраструктуры?
PITR:
Как вернуться к конкретному моменту?
Restore drill:
Работает ли вся эта система вообще?
Это разные задачи.
Хорошая backup-стратегия начинается с RPO
Можно не использовать термин, но вопрос простой:
Сколько данных бизнес готов потерять?
Если backup выполняется:
раз в 24 часав худшем случае можно потерять почти:
24 часа изменений.Для блога это может быть приемлемо.
Для активно используемой CRM — уже болезненно.
Для финансовой системы — возможно неприемлемо.
Второй вопрос — RTO
Сколько времени допустимо восстанавливать сервис?
Например:
15 минут?
2 часа?
8 часов?
сутки?Если база занимает:
500 GBа restore никогда не измерялся, обещание:
Восстановимся быстро.
не имеет технической основы.
Restore drill позволяет получить реальную цифру
Мы можем измерить:
decrypt: 3 min
create DB: 10 sec
pg_restore: 18 min
migrations: 2 min
application checks: 1 minИ получить:
technical restore path
≈ 24 minЭто уже факт.
Но RTO включает больше, чем pg_restore
При полной потере VPS понадобится:
создать новый сервер;установить runtime;получить secrets;восстановить DB;вернуть files;настроить DNS;проверить HTTPS;запустить приложение.То есть настоящий disaster recovery test может быть значительно шире обычного database restore test.
Поэтому полезны два разных теста
Быстрый автоматический restore-check
Регулярно:
latest backup
↓
decrypt
↓
restore to isolated DB
↓
SQL checks
↓
PASS / FAILОн обнаруживает большую часть проблем.
Полный disaster recovery drill
Периодически:
чистая инфраструктура
↓
documentation only
↓
backup
↓
secrets
↓
restore
↓
application launch
↓
critical E2EЭто уже проверка всей способности бизнеса восстановить продукт.
Полный DR-test можно проводить без реальной аварии
Например создаём:
temporary VPS / isolated environmentИ ставим задачу:
Считайте production потерянным.
Использовать можно только:
документацию;
backup;
release;
хранилище секретов.После этого пытаемся поднять продукт.
Такой тест быстро обнаруживает скрытые зависимости
Например:
Для запуска нужен файл, который лежит только в /root старого VPS.Или:
Никто не знает пароль от remote backup.
Или:
Restore-инструкция ссылается на старый Docker image.
Или:
После восстановления не запускается worker.
Или:
Object Storage существует, но ключ восстановления потерян.
Ни одна из этих проблем не видна по зелёному:
backup success.Backup должен быть автоматизирован, restore — документирован и проверяем
Создание копий происходит постоянно.
Поэтому его логично автоматизировать.
Restore выполняется реже.
Но именно поэтому процедура должна быть особенно хорошо задокументирована.
Например:
1. Получить backup
2. Получить encryption key
3. Verify SHA-256
4. Decrypt
5. Create PostgreSQL database
6. Restore
7. Run migrations
8. Restore object data
9. Start application
10. Run readiness
11. Run critical smoke testsВ экстренной ситуации никто не должен восстанавливать этот процесс по памяти.
Особенно полезно, если restore выполняет не автор backup-скрипта
Это отличный тест документации.
Разработчик, написавший backup, знает:
После команды 7 надо ещё вручную сделать X.
Но в инструкции этого нет.
Сам он автоматически выполнит X и даже не заметит пробел.
Другой инженер — нет.
Если другой человек способен восстановить систему только по документации, процедура действительно переносима.
Самая плохая backup-система зависит от одного человека
Например:
Где ключ?
— У разработчика.
Как restore?
— Он знает.
Где offsite?
— Нужно спросить его.Технически backup есть.
Организационно recovery отсутствует.
Полезно отделить backup ownership от production access
Например технический процесс может иметь отдельные credentials только для:
write backupа restore credentials храниться более строго.
Это зависит от модели безопасности проекта.
Главный принцип:
компрометация production-сервера не должна автоматически позволять незаметно уничтожить все независимые резервные копии.
Immutable или защищённая история повышает устойчивость
Представим злоумышленник получил полный доступ к production.
Он удаляет:
databaseа затем:
backups.Если offsite storage доступен теми же credentials с полным delete:
disaster recovery тоже уничтожен.Для критичных проектов имеет смысл продумывать отдельные права, retention или другие механизмы защиты backup от одновременного удаления вместе с production.
Restore нужно проверять после изменения backup-процесса
Например обновили:
PostgreSQL;формат dump;encryption;storage provider.Самое неправильное:
Backup job зелёный — значит всё нормально.
После существенного изменения нужно снова пройти restore.
То же относится к rotation ключей
Было:
Key AСтало:
Key BНовые backups шифруются B.
Но старые:
A.Если rotation уничтожил A, старая история перестала быть восстанавливаемой.
Нужно заранее решить:
храним старые keys?или:
reencrypt backups?Restore-test должен использовать копию, а не оригинал
Проверяя архив, мы не должны случайно повредить единственный known-good backup.
Работаем:
source backup
↓
read-only
↓
temporary restore workspaceПосле проверки temporary artifacts удаляются.
Исходная копия остаётся неизменной.
И сам restore-check не должен заполнить production disk
Здесь появляется забавная рекурсия.
Чтобы проверить backup размером:
20 GBможет потребоваться:
20 GB decrypted dump
+
30 GB restored PostgreSQLЕсли всё выполнять на основном VPS без проверки свободного места, тест восстановления может сам положить production.
Поэтому полноценный restore лучше выполнять:
в изолированной средеили предварительно рассчитывать необходимый headroom.
Это одна из причин разделять быстрый verify и полный restore
На каждом backup:
checksum
+
decrypt verificationможет быть относительно дешёвым.
Полный:
pg_restore
+
application testможет потреблять больше CPU, disk и времени.
Поэтому уровни проверки могут иметь разную периодичность.
Важно не конкретное расписание.
Важно, чтобы полный restore действительно когда-нибудь выполнялся автоматически или в рамках регулярного drill, а не существовал только как теоретическая инструкция.
Как тестировать восстановленную базу
Можно начать с технических проверок.
Например:
database opens;
expected tables exist;
migrations table present;
foreign keys valid;
critical queries execute.Но затем полезны бизнес-проверки.
Например:
последний пользователь найден;у него есть workspace;проект связан с владельцем;история задач читается;файл имеет metadata.Для каждого проекта набор будет своим.
Можно хранить контрольные показатели
Например production перед backup имеет:
users = 14 821
projects = 6 402После restore:
users = 14 821
projects = 6 402Это полезно.
Но не стоит сравнивать произвольное текущее production-состояние с backup, если между ними уже прошло несколько часов.
Контрольные значения лучше сохранять вместе с backup manifest или вычислять в момент создания копии.
Manifest может содержать больше, чем checksum
Например:
{
"createdAt": "...",
"database": "app",
"format": "pg_dump-custom",
"sha256": "...",
"encrypted": true,
"appVersion": "3.4.0",
"schemaVersion": 74
}Можно добавить:
record counts;
backup size;
PostgreSQL version.Так backup становится самодокументируемым артефактом.
Но manifest тоже нужно защищать
Если злоумышленник может изменить:
backupи затем пересчитать:
manifest SHA-256обычный checksum не обнаружит намеренную подмену.
Если threat model включает такую атаку, понадобятся более сильные механизмы проверки происхождения и неизменности.
Для многих небольших продуктов главная задача checksum проще:
Обнаружить повреждение файла при хранении или передаче.
Проверка recovery должна попадать в release process
Например перед серьёзным production update:
current backup freshness
→ PASSЕсли последняя подтверждённая копия:
старше допустимого окнаrelease можно остановить.
Это очень полезный принцип:
не выполнять рискованное изменение, пока нет свежей точки восстановления.
Особенно перед database migration
Например предстоит:
перестроить таблицу;перенести миллионы строк;удалить старое поле.До migration:
verified backup required.Если backup job сломан, сначала исправляется backup.
Потом migration.
Не наоборот.
Но backup не заменяет rollback
Это важное различие.
Rollback release:
v3.4
↓
v3.3может занять минуты.
Restore database:
удалить/переименовать текущую БД
↓
создать новую
↓
восстановить гигабайтынамного дороже.
Поэтому backup — последний защитный уровень.
Не основной способ откатывать каждую неудачную версию.
Хорошая последовательность защитных механизмов
Например:
Tests
↓
Staging
↓
Safe migration
↓
Application rollback
↓
Database backup
↓
Disaster restoreКаждый следующий уровень используется, если предыдущий не помог.
Что происходит при реальном инциденте
Представим:
12:00
ошибочная migration удаляет данные.Первое действие — остановить дальнейшее повреждение.
Затем определить:
можно исправить forward?или:
нужен restore?Если нужен restore, у нас уже существует заранее проверенный путь:
выбрать recovery point
↓
проверить manifest
↓
decrypt
↓
restore
↓
migrations if needed
↓
application readiness
↓
business smoke test
↓
switch trafficА не:
Найдите где-нибудь backup и попробуем понять, как его открыть.
Отдельно нужно продумать point-in-time recovery
Ежедневный dump отвечает:
Как вернуться к состоянию последней копии?
Но иногда ошибка произошла:
17:43а backup создан:
03:00.Возврат на 03:00 означает потерю почти 15 часов нормальных изменений.
Для проектов с более строгими требованиями PostgreSQL поддерживает continuous archiving/WAL и Point-in-Time Recovery, позволяя строить восстановление к более конкретной точке времени. Это уже следующий уровень сложности.
PITR тоже нужно проверять
Очень легко настроить:
WAL archiveи считать:
Теперь у нас есть PITR.
Но пока никто не выполнял:
base backup
+
WAL replay
+
recovery targetэто снова только предположение.
Принцип остаётся прежним:
любая recovery-функция должна хотя бы периодически проходить полный путь восстановления.
Мониторинг backup должен отвечать минимум на несколько вопросов
Последняя копия свежая?Последний backup завершился?Размер выглядит разумно?Checksum PASS?Последний restore-check PASS?Offsite copy существует?Так выглядит уже настоящий backup health.
Полезно различать пять статусов
Например:
CREATEDФайл создан.
VERIFIEDChecksum/шифрование проверены.
RESTOREDПрошёл реальный database restore.
APPLICATION_VERIFIEDПриложение прошло контрольные проверки.
OFFSITE_CONFIRMEDНезависимая копия доступна.
Теперь зелёное:
Backup: OKимеет значительно более конкретный смысл.
Что считать failure
Очевидно:
pg_dump errorНо также:
decrypt failed;checksum mismatch;restore failed;critical table missing;application readiness failed;offsite upload failed.В зависимости от backup policy не все failures имеют одинаковую критичность.
Но скрывать их нельзя.
Размер backup тоже стоит мониторить
Представим обычно backup:
2.4 GBСегодня:
18 MB.Cron завершился с exit code 0.
Но резкое изменение размера выглядит подозрительно.
Возможно:
подключились не к той БД;часть таблиц отсутствует;изменилась конфигурация.Аномалия размера — хороший дополнительный сигнал.
То же относится к неожиданно большому росту
Вчера:
2.4 GBСегодня:
17 GB.Может быть:
нормальный импорт данных.А может:
логическая ошибка;дублирование;бесконтрольный рост служебной таблицы.Backup monitoring иногда первым замечает проблему основного storage.
Backup и retention нужно включать в расчёт диска
Очень легко написать:
храним 14 backupНе посчитав:
14 × 20 GB
=
280 GB.На VPS:
60 GB.Очевидно, политика невозможна.
Поэтому retention должна соответствовать реальной инфраструктуре.
Либо история хранится offsite.
Rolling backup полезен там, где локальный диск ограничен
В одном из production-контуров мы использовали идею:
один локальный
known-good backupвместо бесконечного каталога timestamped dumps.
Новый проверенный backup атомарно заменяет предыдущий.
Более длинная история хранится уже независимо и с ограниченной retention.
Получается:
VPS
→ быстрый локальный recovery point
Offsite
→ история disaster recoveryЭто помогает не превращать системный диск в архив.
Но один local backup нельзя считать всей backup strategy
Если ошибка была обнаружена через два дня, текущая локальная копия уже может содержать проблему.
Именно поэтому offsite history остаётся важной.
Rolling local backup решает:
disk management.Retention history:
historical recovery.Это разные задачи.
Что мы считаем хорошим результатом restore-check
Не:
command exited 0а, например:
Decrypt PASS
Checksum PASS
pg_restore PASS
Schema PASS
Critical queries PASS
Application ready PASS
Cleanup PASSИ отдельно:
Duration:
18m 42sТеперь у нас есть не вера.
Есть проверяемый результат.
Restore-check должен оставлять журнал
Если через месяц новый тест провалился, хочется знать:
что изменилось?Например:
2026-08-20 PASS
2026-08-27 PASS
2026-09-03 PASS
2026-09-10 FAIL:
missing extensionТак можно связать regression с конкретным инфраструктурным изменением.
Тест recovery полезно сделать частью автоматических проверок
Например отдельная команда:
npm run backup:restore-checkили:
./restore-check.shкоторая воспроизводимо выполняет процедуру.
Это гораздо надёжнее инструкции:
Иногда вручную попробуйте восстановить.
Но автоматический тест не отменяет DR-документацию
Автоматизация сама может потеряться вместе с сервером.
Поэтому нужно иметь:
код restore-check;человеческую инструкцию;backup location;key recovery procedure.Лучше сразу предполагать:
Основной VPS больше недоступен вообще.
Очень хороший вопрос: что останется после полной потери сервера?
Мысленно удаляем:
VPSцеликом.
Что у нас осталось?
Хороший ответ:
source code/releaseoffsite encrypted backupobject storage backupsecrets/key recoverydeployment documentationDNS accessЕсли хотя бы один незаменимый компонент находился только на уничтоженном VPS, disaster recovery неполный.
Backup — часть архитектуры, а не системного администрирования «на потом»
Когда проектируют новую CRM или SaaS, обычно обсуждают:
авторизацию;роли;API;файлы;платежи.Backup часто появляется:
Потом перед запуском настроим cron.
Но способ восстановления зависит от архитектуры данных.
Если:
PostgreSQL
+
S3
+
application encryptionнужно заранее знать, как три компонента будут восстановлены согласованно.
Чем сложнее продукт, тем важнее recovery design
У лендинга:
Git
+
несколько формпотеря сервера может быть относительно простой.
У SaaS:
10 000 пользователей;
платежи;
файлы;
история;
проекты.самой ценной частью системы постепенно становится уже не исходный код.
А накопленные данные.
Код можно написать заново.
Пять лет истории клиентов — нет.
Поэтому production readiness без restore-check неполна
Перед запуском проекта можно проверить:
DNS ✓
HTTPS ✓
Login ✓
Payments ✓
Mobile ✓Но если на вопрос:
А если завтра потеряем PostgreSQL?
ответ:
У нас вроде есть backup.
финальная production-проверка ещё не закончена.
Практический checklist резервного копирования
Перед тем как считать backup-контур готовым, мы бы проверили:
- определено, какие данные являются невоспроизводимыми и действительно требуют backup;
- известны RPO и приемлемая потеря данных;
- известен желаемый RTO;
- перед backup контролируется свободное место и эксплуатационный резерв;
- новый dump создаётся во временный файл и не уничтожает предыдущую known-good копию до завершения проверки;
- backup шифруется, если этого требует модель данных;
- ключ восстановления хранится независимо и его реально можно получить;
- для backup рассчитывается и проверяется checksum;
- зашифрованная копия реально расшифровывается;
- PostgreSQL dump периодически восстанавливается в отдельную БД;
- после restore проверяются схема и критические данные;
- при необходимости текущая версия приложения подключается к восстановленной БД и проходит readiness/smoke checks;
- пользовательские файлы и Object Storage входят в recovery plan отдельно от PostgreSQL;
- существует независимая offsite-копия;
- настроена ограниченная retention старых копий;
- мониторинг контролирует возраст последнего успешного backup;
- мониторинг знает о failure backup/restore-check;
- restore procedure документирована;
- проверено восстановление без доступа к основной production-машине;
- известен порядок восстановления secrets и encryption keys;
- после значительных изменений инфраструктуры restore-check запускается снова;
- перед рискованной database migration существует свежая проверенная recovery point.
Такой список намного строже обычного:
cron настроен ✓Но именно поэтому он полезнее.
Когда backup можно назвать проверенным
Мы бы не использовали слово:
verifiedтолько потому, что файл существует.
Более честная последовательность:
Created
↓
Integrity verified
↓
Decryptable
↓
Restorable
↓
Application usable
↓
Offsite protectedЧем дальше прошла копия, тем выше реальная уверенность.
Самая дорогая проверка backup — первая авария
Есть два способа узнать, работает ли restore.
Первый:
провести контролируемый тест.Второй:
дождаться потери production.Цена второго может быть на несколько порядков выше.
Поэтому restore drill — не избыточная DevOps-процедура.
Это способ заранее купить знание:
Да, вот эта процедура действительно возвращает наши данные.
Вместо вывода
Резервное копирование часто воспринимают как процесс создания файлов:
03:00
backup createdНо бизнесу нужен не файл.
Бизнесу нужна возможность продолжить работу после потери данных или инфраструктуры.
Поэтому зрелая backup-система строится в обратную сторону.
Сначала задаётся вопрос:
Как мы будем восстанавливаться?И уже из ответа определяется:
что сохранять;
как часто;
куда;
как шифровать;
сколько версий;
как проверять.Для production важна вся цепочка:
Production
↓
Backup
↓
Integrity
↓
Encryption verification
↓
Offsite
↓
Restore
↓
Application check
↓
RecoveryЕсли цепочка заканчивается на:
backup file existsу нас пока есть резервная копия.
Но ещё нет доказанного восстановления.
Именно поэтому один из самых полезных production-тестов звучит не:
Создался ли сегодняшний backup?
А:
если прямо сейчас удалить основной сервер, сможем ли мы из этой копии вернуть рабочий продукт — и знаем ли мы, сколько это займёт?
Когда на этот вопрос можно ответить не предположением, а результатом реального restore-теста, резервное копирование наконец начинает выполнять свою настоящую задачу.