Chuck Runbook
Chuck Runbook
Operational procedures for Chuck (Strapi 5 in apps/chuck/meat/).
Tier: secondary. Chuck is a content source for marketing pages on brisket. An outage degrades marketing copy and FAQ rendering. It does not affect signup, checkout, billing, or media generation. See chuck-oncall for SLO and escalation tier.
Deploy
Routine deploy
The Strapi-managed deploy command is the supported path
(apps/chuck/meat/package.json → "deploy": "strapi deploy").
cd apps/chuck/meatpnpm install # if dependencies changedpnpm build # admin panel bundlepnpm strapi deploystrapi deploy is the Strapi Cloud deploy
command (the @strapi/plugin-cloud plugin is installed in
apps/chuck/meat/package.json, but not wired in
apps/chuck/meat/config/plugins.ts). TODO(@marty): document the
actual production deployment target — Strapi Cloud, Railway, or a
custom host. There is no apps/chuck/railway.json or
apps/chuck/meat/railway.json checked in.
Pre-deploy checklist
pnpm installsucceeds and lockfile is committed.pnpm buildsucceeds locally with the production env.- New / changed content types reviewed — see Schema Migrations.
- brisket is not blocking on a chuck change. If brisket needs a new field, deploy chuck first, then brisket.
- API token rotations coordinated with brisket (chuck-env).
Post-deploy verification
# Replace with the live hostcurl -sf "https://<chuck-host>/_health" -o /dev/null && echo OKcurl -sf "https://<chuck-host>/api/checkout-faq-section?populate=*" \ -H "Authorization: Bearer $STRAPI_TOKEN" | jq '.data.id'TODO(@marty): confirm the actual healthcheck path used by the
runtime — Strapi 5 exposes /_health by default and the repo does not
override it (no health route in apps/chuck/meat/src/index.ts or
config/); admin login (/admin) remains the most reliable smoke
test.
Schema Migrations
Strapi treats content-type schemas (apps/chuck/meat/src/api/*/content-types/*/schema.json)
as code. Changing a schema adds, renames, or drops a column on next boot.
Discipline
- Never edit schema.json on a running prod instance. The admin UI
also writes schema.json — disable editing in prod via
config/admin.tsif not already. TODO(@marty): confirm the currentapps/chuck/meat/config/admin.tssetting in production — the checked-in file does not include adisableblock for content-type builder. - Add fields, do not rename in place. Renames drop the old column. Add a new field, backfill via the admin or a script, then remove the old field in a follow-up deploy.
- Test migrations locally first. Run with a clone of the prod DB (see Backup / Restore) and verify Strapi boots and data is intact.
- Check brisket coupling. Field renames break
apps/brisket/src/server/api/routers/*.tsconsumers. Grep brisket for the field name before merging. - Components have shared schemas. A breaking change to a component
(
apps/chuck/meat/src/components/<cat>/<name>.json) propagates to every content type that uses it.
Drafts and publish
Every content type sets "options": { "draftAndPublish": true }. The
default Strapi find query returns published only. Updates land as
drafts until an editor publishes — runbook implication: a “missing
content” report after a deploy may be unpublished drafts, not a bug.
Plugin Updates
@strapi/strapi, @strapi/plugin-cloud, and
@strapi/plugin-users-permissions must move together at the same minor
version (currently 5.5.1). Procedure:
- Branch.
- Update
apps/chuck/meat/package.jsonversions in lockstep. pnpm install.pnpm build— admin panel must build clean.- Boot with a copy of prod data; click through admin panel; smoke-test
/api/checkout-faq-section. - Read the Strapi 5 changelog for deprecations.
Third-party providers (strapi-provider-cloudflare-r2,
strapi-plugin-tablify, @strapi/provider-email-nodemailer) move
independently — verify each against the matching Strapi major.
Backup / Restore
Postgres dump
pg_dump "$DATABASE_URL" --format=custom --file=chuck-$(date -u +%Y%m%dT%H%M%SZ).dumpRestore:
pg_restore --clean --if-exists --no-owner -d "$DATABASE_URL_TARGET" chuck-...dumpStrapi transfer (DB + uploads, app-level)
cd apps/chuck/meatpnpm strapi transfer --to file://./chuck-$(date -u +%Y%m%dT%H%M%SZ).tar.gzThe token is salted by TRANSFER_TOKEN_SALT
(chuck-env).
Transfers include uploads metadata; the R2 bucket itself must be
backed up out-of-band (Cloudflare R2 versioning or scripted copy).
Cadence
No automated backup is checked into the repo (find apps/chuck -name 'backup*' -o -name 'cron*' returns no matches; no GitHub workflow
under .github/workflows/ targets chuck). TODO(@marty): set up daily
pg_dump to long-term storage and a quarterly transfer test.
Rollback
Chuck has two state surfaces: code (schema.json, components, plugins) and data (Postgres + R2).
Code-only rollback
git revert the deploy commit and redeploy via the steps above. Safe
when the bad change was a schema addition or a code-only bug.
Schema-destructive rollback
If the bad deploy dropped a column or renamed a content type,
restoring the previous deploy will not bring data back. Restore from
the most recent pg_dump taken before the deploy:
- Put chuck into maintenance — TODO(@marty): document the chosen maintenance gate (Strapi 5 has no first-party maintenance toggle, so the choice between proxy-level gating and stopping the process needs to be recorded; the repo does not pin one).
pg_restoreto a fresh DB.- Redeploy the previous code revision pointed at the restored DB.
- Notify brisket if the public surface changed.
Token / secret rollback
Rotating API_TOKEN_SALT invalidates every issued API token, including
brisket’s STRAPI_TOKEN. Rollback:
- Issue a new API token in
/admin→ Settings → API Tokens. - Update
STRAPI_TOKENin brisket’s deployment. - Redeploy brisket.
Do not rotate APP_KEYS and ADMIN_JWT_SECRET together — admin
sessions will hard-fail. Rotate APP_KEYS first (prepend new key,
keep old until next deploy), then rotate ADMIN_JWT_SECRET separately.
Incident Triage Quick Reference
| Symptom | First check | See |
|---|---|---|
| brisket marketing pages render with empty FAQ / hero copy | Is the relevant single-type published? Hit /api/<plural>?populate=* with STRAPI_TOKEN. | chuck-api |
| brisket gets 401 from chuck | STRAPI_TOKEN revoked or salt rotated | chuck-env |
Admin login fails with Invalid token | ADMIN_JWT_SECRET rotated | chuck-env |
| Image upload returns 500 | R2 creds or bucket | chuck-errors |
Boot fails with ECONNREFUSED | Postgres unreachable | chuck-errors |
| Admin returns CSP errors on uploaded images | CF_PUBLIC_ACCESS_URL mismatch | chuck-env |
Anchor Docs
- API surface: chuck-api
- Errors: chuck-errors
- On-call: chuck-oncall
- Local dev: chuck-local-dev
- Railway: operations/railway
- Deployment env: standards/deployment-env