Migration squashing
voltro db squash — Rails-style consolidation. Mark every applied-before-T migration as squashed, leave a single snapshot row that new envs boot against.
After 2 years of incremental migrations the history table is
hundreds of entries. Fresh environments take minutes to bootstrap.
voltro db squash consolidates everything applied before a cut-off
date into one synthetic snapshot row.
When to squash
- A new environment (laptop, staging) takes >30s to boot because of the migration replay.
- The migration history has hundreds of entries and is becoming
unreadable in
voltro db plans. - You've shipped a major schema overhaul and the pre-overhaul history is no longer useful for debugging.
Don't squash:
- If you're still iterating on the schema in dev — the history is your audit trail.
- Right before a major release — wait until the release lands everywhere first.
- File-based migrations — squashing only affects the declarative-
diff (
auto-diff) entries. File-based migrations keep their separate rollback path.
Quick start
voltro db squash --before 2026-06-01 --note 'consolidate v1 migrations'✓ squashed 14 migration plan(s)
snapshot id: plan_squash_l4f2m1
fingerprint: sha256:a8f2…What the command does
- SELECT every
auto-diffrow in_voltro_migration_plansapplied before the--beforecut-off (wheresquashedAt IS NULL). - Validates the latest pre-squash row's fingerprint matches the current declared-schema fingerprint. If they diverge, the squash refuses — your working tree carries un-applied changes that would silently be locked in.
- Marks all eligible rows squashed by setting
squashedAtto the current timestamp. The rows STAY (audit trail preserved); they just no longer participate in boot-time replay. - Inserts a synthetic snapshot row with:
source: 'squash-snapshot'fingerprint: <current declared fingerprint>notes: <user's --note>operations: { kind: 'squash-snapshot', rowsSquashed: <N> }
New environments booting against the squashed history skip past the squashed rows + use the snapshot's fingerprint as their starting point.
The fingerprint check
The most common gotcha:
$ voltro db squash --before 2026-06-01
squash refused: latest pre-squash fingerprint (a8f2…) differs from
current declared-schema fingerprint (b3c1…). This means the working
tree carries un-applied changes — squashing now would lock the drift in.
Fix: run `voltro db apply` first to align the live schema with the
working tree, then re-squash.The squash captures the fingerprint of the most recent applied migration. If your declared schema has uncommitted changes, the snapshot would say "this is the post-squash schema" but the actual schema (live DB) doesn't match. New envs would boot against the snapshot fingerprint, see a different declared fingerprint, and refuse to start.
Resolution: apply pending migrations FIRST, then squash.
CLI flags
| Flag | Required | Description |
|---|---|---|
--before |
yes | ISO-8601 date — rows applied before this point are squashed |
--note |
no | Human-readable note recorded on the snapshot row |
What happens on next boot
A new env booting against a squashed history:
- Reads the latest
_voltro_migration_plansrow. - If
source = 'squash-snapshot', treats its fingerprint as the baseline + skips replaying the squashed entries. - Diffs current declared schema against the baseline.
- Applies only post-squash migrations (anything with
squashedAt IS NULLANDappliedAt > snapshot.createdAt).
What stays available
- The squashed rows — still in
_voltro_migration_plans. Query them viavoltro db plansor read the table directly. - File-based migrations — untouched by squash. Their up/down pairs remain available for rollback.
- Drift detection —
voltro db driftcompares the live DB fingerprint against the most recent_voltro_migration_plansrow (squash snapshot OR a post-squash entry).
What's GONE
- Rolling back a squashed migration — the rollback path is
one-way once a row is marked squashed. To recover, manually
UPDATE the row's
squashedAtback to NULL + DELETE the snapshot row. The framework doesn't auto-emit a reverse squash. - Replay against a fresh DB — new environments use the snapshot fingerprint, not the original migration sequence. The squashed rows are audit, not replayable.
See also
- Migration overview — the full plan/apply lifecycle the squash slots into
- Drift detection —
voltro db driftcompares against whichever row is most recent (squash or apply) - Soft-drop recovery — the orthogonal "I dropped something and want it back" path