Skip to content

Core Concepts ​

New to database migrations? This page explains the mental model migronaut is built on. Five minutes here makes every other page click.

What is a migration? ​

A migration is a small, versioned script that makes a single change to your database — add an index, rename a field, backfill data — and knows how to undo that change.

You commit migrations alongside your code. When a teammate pulls your branch or a deploy runs, the same change is applied to their database, in the same order, exactly once. No more "works on my machine" schema drift.

Each migration has two halves:

  • up — apply the change (create the index).
  • down — revert it (drop the index).

Indexes and validators without the history

When only the current shape of a collection's indexes and validator matters, you can declare it instead and let migronaut converge apply the difference — no migration file per change.

The changelog ​

migronaut records every migration it has applied in a collection called _migronaut_migrations (the changelog). Before running, it reads this collection to know what's already done, so a migration is never applied twice.

Each record stores an audit trail: when it ran, how long it took, its checksum, the environment, who ran it, and its batch.

History is never deleted

Rolling a migration back doesn't remove its record — it sets the record's status to reverted and stamps revertedAt. The full history is always preserved.

Pending vs. applied ​

At any moment, every migration file is in one of two states:

  • Pending — the file exists on disk but isn't in the changelog yet.
  • Applied — it's recorded in the changelog as run.

migronaut up applies pending migrations; migronaut status shows you the full picture.

The changelog keeps a little more detail than that — a record is never deleted, so its status says how a migration got where it is:

A reverted migration is pending again, and so is a failed one: its record is only a trace that says the attempt happened, and the next up runs it.

Ordering ​

Migrations run in ascending filename order. That's why files are prefixed with a timestamp (20260605143021-add-users-index.js) — it guarantees a deterministic, chronological order across your whole team. Never reorder or rename an applied migration.

Batches ​

Every time you run migronaut up, all the migrations that run together are tagged with the same batch number. Batches are how rollbacks know what "the last thing I did" was:

  • migronaut down reverts the whole last batch (everything from the most recent migronaut up).
  • migronaut down --batch 3 reverts a specific batch.
  • migronaut down --steps 2 ignores batches and reverts the last 2 migrations individually.
  • migronaut up --step puts each file in its own batch, so you can peel them off one at a time later.

Think of a batch as "one deploy's worth of migrations."

Safety mechanisms ​

Two things protect you from common production mistakes:

  • Lock — before writing, migronaut acquires an atomic lock in MongoDB so two deploys can't run migrations at the same time. It auto-expires and renews while a long migration runs. See migronaut unlock.
  • Checksum — migronaut stores a SHA-256 hash of each file when it runs. If a file is edited after it was applied, migronaut status flags it as a mismatch, and --strict mode will refuse to continue — catching accidental edits to history.

Putting it together ​

A typical run looks like this:

How It Works shows the same run in full — the heartbeat, the hooks, where a stop is honoured — and how the other parts of migronaut fit around it.

Ready to do it for real? → Tutorial

Released under the MIT License.