Migrations
Turn schema changes into versioned migrations and apply them.
Updated
Your Go schema is the source of truth. Migrations are how that truth becomes a durable, versioned, replayable record of your data model. Railbase generates them by diffing your DSL against the last applied schema.
Railbase stores everything in Vault — a single-file embedded document store, not SQL. A migration is the captured diff between two schema snapshots: which collections appeared, which fields and indexes changed. Because Vault documents are schema-free, adding a field needs no table rewrite — the change is just a new field your code starts writing, plus any indexes you asked for. Migrations create collections and indexes; they never issue column DDL.
Generate a migration
After you change schema/, generate a migration from the diff:
railbase migrate diff add_posts_status
This writes migrations/NNNN_add_posts_status.up.sql. User migrations are
numbered from 1000 up (system migrations occupy the lower range). If the diff
is empty you'll see "schema unchanged — no migration emitted."
The .up.sql file is a human-readable record of the change — collection and
index operations, one per line. It is the versioned, reviewable history of your
model, not a script that gets executed against a SQL engine (there is none).
Railbase applies the equivalent Vault operations directly.
Note
If a change can't be made safely automatically (for example a type change that
could lose data), migrate diff refuses and lists the incompatible changes so
you can hand-write the migration. Field additions are always safe — a new
field is simply absent on older documents until you write it.
Apply migrations
railbase migrate up # apply all pending, then snapshot the schema
railbase migrate status # VERSION · NAME · STATUS(applied|pending) · SOURCE
migrate up records each pending migration in the _migrations collection and
writes a fresh schema snapshot — the baseline the next migrate diff compares
against. Vault collections themselves are created implicitly on first write, so a
new collection works the moment your code writes to it; the migration record and
snapshot are what give you a versioned, drift-checked history.
On serve, Railbase applies its own system migrations automatically and
registers every code-defined collection from your DSL (the init() in
schema/), so a freshly-deployed binary boots ready to serve. Run migrate up
to record your user migrations and refresh the schema snapshot — the scaffolded
Makefile provides a standalone migrate-up target you run before serving; it
is not auto-chained ahead of dev/serve.
Rolling back
railbase migrate down → not implemented (refuses)
There is no automatic down-migration. To reverse a change, write a new forward migration that undoes it — explicit and reviewable beats a magic rollback. And before anything risky, take a snapshot:
railbase backup
See Backups & restore.
Where migrations live
migrations/
├── 1000_initial_schema.up.sql
├── 1001_add_posts_status.up.sql
└── …
Commit the migrations/ directory to source control alongside schema/ — that
pair is the full, replayable history of your data model.