Soft-drop recovery
VOLTRO_SOFT_DROP=1 renames dropped columns instead of deleting them. voltro db restore-snapshot brings them back. (Distinct from Rollback — this is the one path that recovers DROP-COLUMN data.)
This page is the ONLY mechanism that recovers data from a
DROP COLUMN. The sibling Rollback page covers reversing structural changes via forward-apply and file-baseddownbodies — neither of which brings back dropped data. If you dropped a column and want its data back, you neededVOLTRO_SOFT_DROP=1set at apply time; that's what this page is about.
By default Voltro's applier issues ALTER TABLE ... DROP COLUMN
for any lossy migration. Once that runs the data is gone — your
only options are a full DB restore or replaying from a backup.
VOLTRO_SOFT_DROP=1 rewrites every drop-column op into a
RENAME. The data stays in a sidecar column named
<original>__dropped_<timestamp>. voltro db restore-snapshot <plan-id> walks the migration's operations + RENAMEs them back.
When to use
Set VOLTRO_SOFT_DROP=1 in your apply pipeline as a default
safety net. The cost is one extra column per drop (data still
takes disk space until GC); the benefit is a one-command recovery
window.
Specifically helpful for:
- Reversible production migrations — drop a column, realize 30 minutes later it broke a downstream report, restore it.
- Pre-release schema churn — drop columns liberally during pre-release, recover when you change your mind.
- High-stakes drops — set
VOLTRO_SOFT_DROP=1per-migration via shell env var for the specific apply.
Apply with soft-drop
VOLTRO_SOFT_DROP=1 voltro db applyThe applier looks at every op in the plan. For drop-column ops
it emits a RENAME instead of a DROP:
-- Without VOLTRO_SOFT_DROP
ALTER TABLE "users" DROP COLUMN IF EXISTS "legacy_email";
-- With VOLTRO_SOFT_DROP=1
ALTER TABLE "users" RENAME COLUMN "legacy_email" TO "legacy_email__dropped_20260603145522";The migration row still says "drop-column applied" in
_voltro_migration_plans — the planner doesn't know the rename
happened. The post-apply fingerprint is what matters for boot
checks (and that's based on the declared schema, where the column
genuinely doesn't exist anymore).
Restore
voltro db restore-snapshot plan_01j5xkqyz...↩ restored users.legacy_email (from legacy_email__dropped_20260603145522)
✓ restored 1 column(s) from plan 'plan_01j5xkqyz...'The command:
- Loads the migration plan by id.
- Walks every op in
operations, finds thedrop-columnones. - For each
(table, column), queriesinformation_schema.columnsfor a<column>__dropped_*match. - RENAMEs the most-recent match back to the original name.
If no snapshot matches (the plan was applied without
VOLTRO_SOFT_DROP), the command logs a warning + skips that
column but continues with the others.
Workflow
- Apply a risky migration with soft-drop enabled.
- The dropped column survives as
<col>__dropped_<stamp>. - Test the post-apply state.
- Either:
- It's fine. Wait for GC (see below) to actually drop the column.
- It broke something. Run
voltro db restore-snapshot <plan-id>to bring the column back.
- Re-fix your schema in code (re-declare the column in the
.entity.tsfile) + run a normalvoltro db applyto put the schema back in shape.
Step 5 is important — the framework's declared schema is the source of truth. Just restoring the snapshot brings the data back but the declared schema still says the column shouldn't exist; the next apply would re-drop it.
GC
Snapshot columns aren't automatically dropped. They survive until you:
- Run a manual
ALTER TABLE ... DROP COLUMN <col>__dropped_<stamp>(or a wrapping migration that handles it). - Drop the table entirely.
Reclaim the space once your "is the migration confirmed safe?" review
window has passed with voltro db gc-snapshots --before <date> — it
permanently drops every <name>__dropped_<ts> column AND table older
than the date (the <ts> stamp drives the comparison). Add --dry-run
to preview:
voltro db gc-snapshots --before 2026-01-01 --dry-run # preview
voltro db gc-snapshots --before 2026-01-01 # drop them (NOT reversible)Limitations
- Restore needs
VOLTRO_SOFT_DROP=1at apply time. Without it the drop is a hardDROP COLUMN/DROP TABLEand the data is gone —restore-snapshotcan only bring back what was soft-dropped (renamed to<name>__dropped_<ts>). It restores both columns AND tables. - Lossy at the row level. This recovers DROP COLUMN (everything from before the drop is in the snapshot column). It does NOT recover UPDATE/DELETE row-data — those are forever. For that, use database backups.
- No effect on planner classification. Lossy ops are still
classified as lossy by
voltro db plan.VOLTRO_SOFT_DROPis about HOW the drop happens, not WHETHER it's classified safe. The lossy refusal still requires explicitdropped()annotation orVOLTRO_DESTRUCTIVE_OK=1.
See also
- Migration overview — the full lifecycle
- Operation classes —
the 7-class taxonomy including
lossy - Drift detection — for out-of-band changes