docs(ops): prove the recovery backup decrypts before dropping production (issue #74)

The rollback restore dropped jyotisha before decrypting the backup; a bad
key or damaged file would leave the site on an empty database. Load the
host env file, check sha256, decrypt into pg_restore --list first.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LutgzyJqNhPpZy1nFsJtN3
This commit is contained in:
Jesse_Chen
2026-10-14 21:28:47 +08:00
co-authored by Claude Opus 5.5
parent 40bc565a49
commit 9274491681
@@ -106,7 +106,21 @@ Gitea → Deploy production。Use workflow from:`main`。
1. 整库还原。没有单独按钮。只允许 `RECOVERY_FORMAT=2` 的恢复点。先看 `/opt/jyotisha-production/.state/recovery-baseline.env`:没有 `RECOVERY_FORMAT=2` 就停,回到第 4 步再点一次创建恢复点。8 月的旧备份不能用。第 4 步的 `recovery_reference` 斜杠后面就是备份文件名,例如 `production-pre-migration-20261014T120000Z.dump.enc`。文件在 `/opt/jyotisha-production/backups/` 下面。先 `sha256sum` 这个文件,和同一份 `recovery-baseline.env` 里的 `RECOVERY_SHA256` 对上,再继续。
先停 `api` 和 `web`,不要停 `postgres`。口令和密钥只用主机上已有的环境文件,不要贴出来。
**先证明备份能解开、能读,再动生产库。** 顺序不能反:先删库再发现备份解不开,网站就停在一个空库上。口令和密钥只用主机上已有的环境文件,不要贴出来,也不要 `echo`:
```text
cd /opt/jyotisha-production
set -a; . ./.env.production.database; set +a
backup=/opt/jyotisha-production/backups/<备份文件名>
sha256sum "$backup" # 必须等于 recovery-baseline.env 里的 RECOVERY_SHA256
postgres_id=$(sudo -n docker ps -q --filter label=com.docker.compose.project=jyotisha-production --filter label=com.docker.compose.service=postgres)
openssl enc -d -aes-256-cbc -pbkdf2 -pass env:PRODUCTION_BACKUP_ENCRYPTION_KEY -in "$backup" \
| sudo -n docker exec -i "$postgres_id" pg_restore --list >/dev/null && echo backup_readable=1
```
上面只读备份、不碰任何库。必须看到 `backup_readable=1`;没看到(解密失败、文件损坏)就停,不要往下做,问产品负责人。
看到之后,再停 `api` 和 `web`,不要停 `postgres`:
```text
api_id=$(sudo -n docker ps -q --filter label=com.docker.compose.project=jyotisha-production --filter label=com.docker.compose.service=api)
@@ -119,7 +133,7 @@ sudo -n docker exec -e PGPASSWORD="$POSTGRES_PASSWORD" "$postgres_id" \
sudo -n docker exec -e PGPASSWORD="$POSTGRES_PASSWORD" "$postgres_id" \
psql -U "$POSTGRES_USER" -d postgres -v ON_ERROR_STOP=1 \
-c "CREATE DATABASE jyotisha;"
openssl enc -d -aes-256-cbc -pbkdf2 -pass env:PRODUCTION_BACKUP_ENCRYPTION_KEY -in <备份文件> \
openssl enc -d -aes-256-cbc -pbkdf2 -pass env:PRODUCTION_BACKUP_ENCRYPTION_KEY -in "$backup" \
| sudo -n docker exec -i -e PGPASSWORD="$POSTGRES_PASSWORD" "$postgres_id" \
pg_restore -U "$POSTGRES_USER" -d jyotisha --exit-on-error
sudo -n docker exec -e PGPASSWORD="$POSTGRES_PASSWORD" "$postgres_id" \