Snapshots

Backups exist for every site, so a restore is a routine

Waiting on you
  • Production keeps point-in-time recovery for 7 days and 14 daily backups.
  • A nightly export at 21:30 UTC lands in a separate bucket that survives deleting the instance.
  • The 25 Oct restore test passed: every brand's row counts matched live production.
  • After a restore run the connector ping, and never reconnect Arata.
  • The monthly test is due, and no later run is recorded.
7Days of point-in-time recovery
Your call

Who runs the monthly restore test, and on which day?

Ops and infrastructure · 8.3

Restore

DraftLast checked 2026-10-26Owner Param

Answer

This page says what backups exist for each site, how to bring back the whole production database, one brand or one file, and how the monthly restore test is run, so a restore is a known routine rather than a first attempt under pressure.

Today

What is kept

Site Kind Kept for Source
Production database Point-in-time recovery 7 days, to the second, into a new instance brew/.wt-prod/docs/production.md:37
Production database Automated backups daily, 14 kept brew/.wt-prod/docs/production.md:38
Production database On-demand backup before every promote until deleted brew/tools/promote.sh:54-62
Production database Nightly logical export, Cloud Scheduler brew-prod-nightly-export at 21:30 UTC, to gs://brew-prod-backups/nightly/brew.sql.gz separate bucket, survives deleting the instance brew/.wt-prod/docs/production.md:40
Production files gs://brew-prod versioning plus 7-day soft delete old versions until removed brew/.wt-prod/docs/production.md:41
Staging files gs://brew-prototype versioning; noncurrent versions deleted after 30 days 30 days memory brew-storage
Staging records Every row also mirrored to the bucket while OBJECTS_MIRROR=1 as the bucket brew/deploy.sh:30-32
Staging jam and brand resets A copy in _deleted/jams-<time>/ or _deleted/brand-<slug>-<time>/ unless --no-archive until removed brew/db/reset-jams.mjs:4 brew/db/wipe-cli.js:18-19
Production brand reset or deletion No _deleted/ copy; the database backups hold the brand until they age out 7 days of logs, 14 daily backups brew/db/wipe-cli.js:18-20 brew/db/brand-wipe.js:236-238
  • Staging's Cloud SQL backup settings are not written down anywhere we read. brew/.wt-prod/docs/production.md:11
  • The demo has no backups recorded; its brands come again from staging with tools/demo-sync.sh. brew/tools/demo-sync.sh:1-7

The restore test

  • 25 Oct 2026: the export was imported into a scratch database on brew-staging; every brand's row and part counts matched live production. brew/.wt-prod/docs/production.md:43
  • It is to be repeated monthly. No later run is recorded. brew/.wt-prod/docs/production.md:43

Sign-ins after a restore

A whole-database restore also brings back old sign-ins. Arata's tokens do not rotate, so this is safe for Arata, but db/connector-ping.mjs must be run afterwards, and Arata is never reconnected. brew/.wt-prod/docs/production.md:50 memory never-disconnect-arata-meta

Tomorrow

Item Version Tag
Point-in-time recovery, daily backups, nightly export, versioned files v1 Stays
Monthly restore test, logged with its date and result v1 New
A one-brand restore job written and tested, from db/partner-copy.js v1 New
Staging backups written down, or staging declared rebuildable v1 New
Point-in-time window longer than 7 days once partners pay v2 Changes, Param's call

How we do it

The whole production database to a moment

  1. Clone, about 20 to 40 minutes:
gcloud sql instances clone brew-prod brew-prod-restore --point-in-time=<RFC3339>
  1. Check the clone. Prefer copying the damaged rows back. Only if that is not possible, point BREW_PROD_DATABASE_URL at the clone and promote.
  2. Run db/connector-ping.mjs arata-in through the brew-prod-ping job. Never reconnect Arata.

brew/.wt-prod/docs/production.md:47-50

One brand

  1. Clone to a moment before the damage, as above.
  2. Copy that brand's objects and doc_parts rows from the clone with a small one-off SQL job, modelled on db/partner-copy.js minus its rule that keeps production's own rows. Keep the current private/vault/* rows unless told otherwise.
  3. Bring the brand's files in gs://brew-prod/brands/<slug>/ back from object versions.

brew/.wt-prod/docs/production.md:52-55

Files only

gcloud storage ls -a gs://brew-prod/brands/<slug>/<path>
gcloud storage cp gs://brew-prod/brands/<slug>/<path>#<generation> gs://brew-prod/brands/<slug>/<path>

brew/.wt-prod/docs/production.md:57

Monthly restore test

  1. gcloud scheduler jobs run brew-prod-nightly-export, then wait for the EXPORT operation.
  2. Give the brew-staging Cloud SQL service account read access on gs://brew-prod-backups for the duration.
  3. gcloud sql databases create restore_drill --instance=brew-staging
  4. gcloud sql import sql brew-staging gs://brew-prod-backups/nightly/brew.sql.gz --database=restore_drill
  5. Count rows per brand in both databases with a one-off job inside the VPC, compare, then drop restore_drill and remove the temporary access.
  6. Write the date and result in Numbers below.

brew/.wt-prod/docs/production.md:59-64

Numbers

Metric Definition Where measured
Last restore test Date and pass or fail of the monthly test This page; 25 Oct 2026, passed brew/.wt-prod/docs/production.md:43
Restore time Minutes from clone start to a checked clone Proposed; production.md estimates 20 to 40
Nightly export age Hours since the last successful export Cloud Monitoring alert "export failed" brew/.wt-prod/docs/production.md:68

Open

  1. Who runs the monthly restore test, and on which day? Proposal: Param, on the first Monday of each month, starting 2 Nov 2026, with Claude doing the steps and Param checking the counts.
  2. Is staging data worth backing up? The partners now live on production. Proposal: treat staging as rebuildable, keep only the bucket versioning, and say so here.
  3. Is 7 days of point-in-time recovery enough? Proposal: keep 7 days during the design partnership and revisit when the first partner pays.
Sources
  • brew/.wt-prod/docs/production.md:35-64
  • brew origin/main d6154b3: tools/promote.sh:54-62, db/connector-ping.mjs:1-24, db/partner-copy.js:1-11, db/wipe-cli.js:18-20, db/brand-wipe.js:236-238, docs/production.md:108-110, db/reset-jams.mjs:4
  • memory brew-storage, brew-prod-backend, never-disconnect-arata-meta

Source file content/ops-and-infrastructure/restore.md · For agents: Markdown · JSON