Перейти к содержанию

Чистый Compose env и Caddy recreate при rollback

  • Дата: 2026-08-26.
  • Статус: VALIDATED.
  • Тип задачи: DEV/STAGE deploy и rollback.
  • Компоненты: Docker images, Docker Compose, Caddy edge, runtime env, migration job.
  • Теги: deploy, rollback, compose, environment, entrypoint, migration, caddy, admin-off.
  • Связанные решения: 0025, 0026.
  • Связанный Skill: отсутствует.

Контекст и границы

Во время отката DEV deploy shell ранее загрузил Resend variables через set -a. Восстановленный .env содержал Gmail, но exported shell variables имели приоритет над docker compose --env-file, поэтому старый image получил provider resend и остался unhealthy. Одновременно edge Caddy имел admin off, поэтому команда caddy reload не могла обратиться к admin API.

При STAGE rollback 2026-08-26 прежние image IDs сначала были запущены с новым Compose. Старый server image содержал прежний API entrypoint и не содержал нового migration entrypoint, поэтому пара old image + new Compose получила MODULE_NOT_FOUND, хотя база и сами images были исправны.

Запись описывает только наблюдённые runtime-механизмы. Она не является активным deploy Skill, не разрешает server mutation и не заменяет task-specific rollback.

Что сработало

  • Выполнять rollback Compose в новом чистом SSH/process environment либо явно unset candidate variables до чтения восстановленного .env.
  • При admin off применять проверенный Caddyfile через controlled container recreate, а не через admin API reload.
  • В rollback trap отключать fail-fast на время recovery и независимо пытаться восстановить edge и DEV, сохраняя exit status каждого шага.
  • После recovery проверять runtime provider/image, readiness, оба внешних домена и сохранность logical dump; volumes и backups не удалять.
  • Сохранять и восстанавливать как один recovery set точные image refs/IDs, .env и compose.yml; до mutation проверять, что команды/entrypoints сохранённого Compose существуют в восстанавливаемых images.
  • Если новый Compose добавил одноразовый migrator, использовать сохранённый старый Compose, не выполнять schema downgrade и оставлять постоянный volume нетронутым.

Что не сработало и почему

  • docker compose --env-file old.env в shell с exported candidate variables не гарантирует old runtime config: process environment имеет более высокий приоритет.
  • caddy reload несовместим с фактическим admin off.
  • Первый rollback trap работал под set -e и остановился на unhealthy DEV раньше, чем восстановил edge-файл.
  • Возврат только old images и env под текущим Compose: API ушёл в restart loop из-за нового entrypoint, а migrator завершился 1, потому что файла запуска не было в old image.

Проверки и свидетельства

  • Первый recovery из чистого SSH process вернул прежние DEV images/Gmail и readiness 200.
  • Общий edge был восстановлен через recreate; DEV и legacy STAGE снова возвращали прежний strict-origin-when-cross-origin.
  • STAGE images, start times, volumes и data runtime не изменились; актуальный pre-deploy DEV dump повторно прошёл pg_restore --list.
  • Полный evidence: task card и session log (session log excluded from published snapshot).
  • STAGE recovery подтвердил точную связку old images/env/Compose: API/web вернулись в healthy, restart counters — 0, DEV и edge/docs не изменились; затем тот же exact candidate был безопасно redeployed с новым dump и rollback package. Evidence: STAGE task card.

Когда применять

  • при rollback Compose после загрузки candidate secrets в shell;
  • при изменении Compose commands, entrypoint paths или добавлении migration service;
  • при Caddy runtime с отключённым admin API;
  • при общем ingress, где recovery должен проверить несколько доменов.

Когда не применять

  • как универсальную команду deploy без сверки фактического Compose/Caddy runtime;
  • для удаления volumes, backups, releases или images;
  • как доказательство успешного нового deploy.

Следующее улучшение

Recovery-set contract независимо проверен после фактического DEV/STAGE опыта. Критический deploy Skill пока не создаётся: общий end-to-end контракт ещё не стабилизирован и не должен активироваться из одной memory record.