Devstack Migration Snapshot & Rollback
On this page
Overview
cargo xtask migrate snapshot and cargo xtask migrate rollback are
developer-ergonomics tooling for the Docker devstack: snapshot every
Canopy database into .devstack/snapshots/<timestamp>/ before applying
an experimental migration, then roll back if it breaks.
This is dev/CI tooling only. Production rollback uses pg_basebackup
plus WAL point-in-time recovery — see
Database Backup & Restore.
| Concern | Tool |
|---|---|
Production point-in-time recovery |
|
Local dev — snapshot before applying a new migration |
|
Local dev — undo a broken migration |
|
Down migrations (forward fix-up vs. rollback) |
Forward-only is the modern pattern; emergency manual rollback uses this tool, not down-migration files (tracked as a follow-up issue). |
When to use
-
About to merge a migration MR that touches a table you don’t own — snapshot first, run
cargo xtask dev refreshto apply, smoke-test, roll back if the migration breaks something unexpected. -
Reviewing a migration MR locally — snapshot, check out the branch, apply migrations via
cargo xtask dev refresh, then roll back to your clean state when done. -
Reproducing a determined-to-be-broken state in CI for a bug — capture a snapshot from an affected developer’s box, ship it as an attachment, restore on your own devstack with
cargo xtask migrate rollback --id <ts>.
Commands
| Command | Effect |
|---|---|
|
Runs |
|
Prints all available snapshots in chronological order with their size, entry count, and git HEAD SHA at snapshot time. |
|
Restores from the most recent snapshot. Each archive is piped into
|
|
Restore from a specific snapshot by id. |
|
Restore only the named database(s). Pass |
Output paths
.devstack/snapshots/
20260430T174056Z/
manifest.json
canopy_appeals.dump
canopy_applications.dump
...
canopy_wic.dump
The .devstack/ directory is gitignored — snapshots live on the
developer’s machine only and never get checked in.
Manifest schema
{
"snapshot_id": "20260430T174056Z",
"git_sha": "ca0f836f44a773493a5fb6161f336724a612efdb",
"shared_db": false,
"entries": [
{
"database": "canopy_snap",
"container": "canopy-postgres-snap-1",
"archive": "20260430T174056Z/canopy_snap.dump",
"size_bytes": 371320
}
]
}
Limitations
-
Devstack only. The container names are hardcoded to
canopy-postgres-*-1. Production databases live behind a different naming scheme (cloud-managed Postgres, host:port endpoints with TLS) —pg_basebackupis the right tool there. -
Logical dump, not physical. Restored databases get the same data but may differ in toast/WAL state. Fine for dev; not equivalent to a physical replica.
-
No incremental snapshots. Each snapshot is a full dump of every DB. The 19 Canopy DBs total ~15 MB after seed data; a snapshot takes roughly 5-10 seconds and is cheap to take.
-
Per-database rollback uses the
--dbflag (issue #344, shipped 2026-05-01); no need to drop into rawpg_restorefor the common case any more.
Implementation notes
Source: xtask/src/cmd/migrate.rs. Operational-infrastructure plan
Step 4. Down-migration templates per critical table tracked as a
separate follow-up issue (see plan footer).