# Backup и восстановление — Phase 5 `./mcpack backup` работает с полным релизом и runtime, который уже связан с ним через `scripts/server.sh`. Нужны Node/npm dependencies, закреплённый Compose, Docker daemon, GNU tar и zstd. Оператор должен читать весь runtime и сохранять владельцев при распаковке: запускать от владельца данных или с необходимыми правами. Проверка UID/GID не пропускается. ## Создание snapshot ```bash export MC_ROOT=/absolute/server-root export MC_RELEASE_DIR=/absolute/full-release # MC_ENV_FILE — необязательный абсолютный путь, по умолчанию MC_ROOT/.env. ./mcpack backup --wait-timeout 120 ``` Команда удерживает общую `.mcpack-runtime.lock`, проверяет релиз/settings, останавливает сервер через Compose и подтверждает остановку по Docker state. После этого архивирует **весь `MC_ROOT/runtime` без исключений**: world, данные модов вне world, конфиги, server.properties, whitelist/operators, логи, cache, downloads и установленные server/mod JAR. Для NeoForge безопасные исключения пока не подтверждены. Releases, snapshots и host `.env` находятся вне runtime и не входят в архив; внутри snapshot хранится отдельная полная копия исходного релиза, включая его `.env.example`. Результат находится в `MC_ROOT/backups/-/`: ```text runtime.tar.zst metadata.json SHA256SUMS release/ # исходный полный релиз с собственными checksums ``` Metadata фиксирует исходный релиз, SHA256 manifest/checksum list, закреплённый image, целевой релиз (если передан `--target-release /absolute/release`), прежнее состояние сервера, подтверждение остановки, время, исключения, архив SHA256/размер и inventory путей/хешей/размеров/mode/UID/GID runtime. `--target-release` только проверяет и записывает назначение будущего deployment; команда backup сама не применяет новый релиз. Перед публикацией выполняются tar/zstd listing, проверка типов/путей и пробная распаковка с точным сравнением inventory. Checksums покрывают все файлы snapshot и сохранённого релиза. Каталог публикуется атомарным rename внутри backups; незавершённый snapshot не появляется под окончательным именем. При успехе ранее запущенный сервер возвращается на тот же релиз через `up --wait`. Остановленный сервер или отсутствующий контейнер не запускаются. Ошибка backup возвращает non-zero. При ошибке после успешной остановки сервер остаётся остановленным; оператор получает ошибку и status в `MC_ROOT/.mcpack-backup-operation.json`. Ошибка resume сохраняет опубликованный проверенный snapshot и пытается снова остановить сервер. Новый deployment должен использовать `backupBeforeApply` из `platform/backups.mjs`: callback применения допускается только после публикации snapshot и под той же блокировкой. Этот gate проверен тестами и используется SSH deployment Phase 7 под общей блокировкой всей операции: [deploy.md](deploy.md). Snapshot не удаляется автоматически. Нужны свободное место под полный runtime, пробную распаковку и копию релиза; операция завершается ошибкой при нехватке места. Символические/жёсткие ссылки и специальные файлы в runtime сейчас отклоняются. Команды вне wrapper не участвуют в блокировке; во время backup не изменять runtime вручную и не запускать контейнер прямым Compose. ## Восстановление в отдельный каталог ```bash ./mcpack restore \ --backup /absolute/server-root/backups/TIMESTAMP-UUID \ --destination /absolute/restored-server ``` Destination должен **не существовать**. Команда сначала проверяет snapshot, распаковывает во временный соседний каталог, сверяет bytes/mode/UID/GID, проверяет сохранённый релиз и публикует новый root атомарным rename. Исходный runtime не изменяется. Результат: ```text restored-server/runtime/ restored-server/release/ restored-server/.mcpack-runtime.json restored-server/restore-report.json ``` Host settings и EULA acceptance из snapshot не восстанавливаются; сервер автоматически не запускается. Для ручного recovery подготовить `.env` из `release/server/.env.example`, задать прежние UID/GID, подходящие memory/port и принятие EULA. Используется прежний стабильный Compose project из binding. Прежде чем запускать восстановленную копию под тем же project, остановить и удалить **старый контейнер** через wrapper (данные старого root сохраняются): ```bash MC_ROOT=/absolute/original-server \ MC_RELEASE_DIR=/absolute/original-release scripts/server.sh down # На данном host settings уже настроены и EULA принята: export MC_ROOT=/absolute/restored-server export MC_RELEASE_DIR="$MC_ROOT/release" export MC_ENV_FILE=/absolute/prepared-host-settings.env scripts/server.sh up -d --wait --wait-timeout 120 scripts/server.sh exec -T minecraft mc-health scripts/server.sh logs --tail 100 ``` Не заменять binding или release на более новую версию: восстановленный мир нужно запускать с сохранённым исходным релизом. Если прежний контейнер ещё использует другой root, wrapper отклонит запуск. Это ручное восстановление в отдельный root; переключение active release и rollback — последующие фазы. ## Область проверки Проверен local Linux/WSL Docker и NeoForge 21.1.252 / Minecraft 1.21.1. Primary Phase 0 runtime пока управляется старым spike wrapper без full-release binding, поэтому его миграция не выполняется этой командой. Off-host копии, retention, автоматический rollback и защита от внезапного отключения питания во время записи (fsync) остаются последующими улучшениями. Реальный опыт восстановления и свидетельства: [phase5-results.md](phase5-results.md).