# Rollback Plan **Mechanism** (D-26, `infrastructure-design.md` § 3–4): two releases are always retained — `current` plus exactly one previous. Pruning only ever runs after a **passing** health check, so a failed deploy never leaves fewer than two releases on disk. ## When to Roll Back - The deploy workflow's health check failed (job already shows red; `current` was left pointing at the new, unhealthy release — no automatic rollback, per `infrastructure-design.md` § 4) - The deploy succeeded and passed its health check, but a defect surfaces afterward that only shows up under real traffic ## Fast Rollback (Previous Release Still on Disk) — the Common Case No rebuild needed. Over SSH, on the Pi: ```bash cd ~/apps/slpmodularcms-/releases ls -1t # confirm which directory is the previous, working release ln -sfn ~/apps/slpmodularcms-/releases/ ~/apps/slpmodularcms-/current systemctl --user restart slpmodularcms-.service curl -f https:///health # confirm the rollback itself is healthy ``` This is the same atomic-switch primitive the deploy workflow itself uses — pointing it backward instead of forward. `wwwroot/web/` is untouched either way, since it was never part of the switched directory to begin with (FR-08). ## Rebuild-and-Redeploy Rollback (Older Than One Release Back) If the defect predates the retained previous release, redeploy an **earlier commit** through the normal pipeline: 1. `git revert` or check out the last-known-good commit on a branch 2. Push to `master` (test) or run `workflow_dispatch` with `deploy_production: true` (production) — the same gates and deploy sequence run as any other deploy 3. This only works because migrations are required to be forward-compatible and non-destructive (D-26) — redeploying an older commit's code against a database that has since had newer migrations applied must not break. If a migration since the target commit **was** destructive, this path is not safe and the database backup (§ below) is the actual recovery route instead ## Database Rollback A backup is taken before every **production** deploy (`operations/deployment/deployment-instructions.md` § 4, invoked by `deploy-scp.yaml` when `run_db_backup: true`). To restore: ```bash sqlcmd -S -U -P -C -Q \ "RESTORE DATABASE [SlpModularCmsProduction] FROM DISK = N'.bak' WITH REPLACE" ``` Restoring a database backup and rolling back the application release are **independent actions** — decide based on the actual failure whether one, the other, or both are needed. Rolling back the app without restoring the database is usually sufficient (migrations are non-destructive by design); restoring the database without rolling back the app should be rare and deliberate. ## What Is Never Part of a Rollback - `wwwroot/web/` (the customer's website) — structurally outside every release directory; no rollback action should ever touch it - The Data Protection key ring — lives in the database, not the release directory; rolling back the app release does not affect it - Gitea Actions variables/secrets — these describe the *target*, not a specific release; nothing about them changes during a rollback