Prisma ORM 8 is here.Read the docs

Applying a migration

The db migrate command walks the graph from where your database is to where you want it, with a preview, a checkpoint at every step, and safe retries.

Applying is the one step that touches a database, via a single command:

bunx prisma@latest db migrate

Note that the command is db migrate, not migration apply. The migration ... subcommands manage files on disk; db migrate moves a database. It reads where the database currently is (its marker), finds a path through the migration graph to the target, and applies each migration along the path, running every operation's precheck, execute, and postcheck as it goes.

By default, the connection comes from db.connection in prisma.config.ts. Pass --db to override it:

bunx prisma@latest db migrate --db $DATABASE_URL

A successful run reports every operation it performed and the marker that it left behind:

✔ Applied 3 operation(s) across 1 contract space

App space
  ├─ Add column "displayName" to "user"
  ├─ Data transform: backfill-user-displayName
  └─ Set NOT NULL on "user"."displayName" (destructive)
  marker: e6b5c2849eca8d24ff1e8e88ab2a4234db8e74c497c035cb7ce42e814f31cd63

Next: prisma migration status

App space is your application's migration lane, one of the run's contract spaces. Projects that use database extensions gain additional spaces; this is covered below in Extension spaces.

Check before, preview, then apply

When working on shared databases, it helps to always follow these three steps:

# 1. Where is the database, what's pending?
bunx prisma@latest migration status --db $DATABASE_URL

# 2. What exactly would run?
bunx prisma@latest db migrate --show --db $DATABASE_URL

# 3. Run it.
bunx prisma@latest db migrate --db $DATABASE_URL

migration status draws the path between the database's marker and the target, flagging each migration as applied or pending:

*   925198f  @contract
|^  20260707T1006_add_user_phone  705b1a6 -> 925198f  1 ops  > pending
*   705b1a6  @db (db)
|^  20260707T1005_init                  - -> 705b1a6  2 ops  + applied
*   -

1 pending — run `prisma db migrate --to 925198f3cc27`

Read the markers on the right: @db is where the database is, @contract is where your emitted contract is, and (db) is the ref of that name pointing at the same node.

db migrate --show is the read-only dry run: it draws the path from the database's position to the target and stops. Nothing touches the database:

│↑  20260707T1006_add_user_phone  705b1a6 → 925198f  ↑ will run
○   705b1a6
│↑  20260707T1005_init                  ∅ → 705b1a6  ↑ will run
○   ∅  @db

The following 2 migrations will run:
  20260707T1005_init                ∅ → 705b1a6
  20260707T1006_add_user_phone  705b1a6 → 925198f

After applying, migration log shows the database's own record of what ran: an append-only ledger the runner writes alongside the marker:

 Applied at             Migration                       Change                 Ops
---------------------- ------------------------------- -------------------- ------
 2026-07-07 10:05:32Z   20260707T1005_init              - -> 705b1a6         2 ops
 2026-07-07 10:09:55Z   20260707T1006_add_user_phone    705b1a6 -> 925198f   1 ops

Choosing a target

With no --to, db migrate advances toward your emitted contract. To aim somewhere specific, name the target with --to, for example:

bunx prisma@latest db migrate --to prod --db $DATABASE_URL

If the graph has branched and more than one tip is reachable, db migrate stops and asks for an explicit --to. That's the graph protecting you: two feature branches may both be valid futures, and picking one is a human decision.

--advance-ref moves a named ref to the post-apply state in the same step, which is what keeps migration plan incremental. Plain db migrate never moves a ref. That is deliberate: a deploy or a CI apply should not move a ref that lives in the repository, so in development you pass --advance-ref db and in production you leave it off. The db migrate reference has the full --to grammar (refs, hashes, migration names, <dir>^) and the flag contract.

When something goes wrong

As soon as an operation fails, the runner will stop, alert you to what caused the problem and why, as well as what to do to resolve the problem:

✖ Operation pgvector.install-vector-extension failed during execution: create extension "vector" (MIGRATION.RUNNER_FAILED)
  Why: extension "vector" is not available
  Fix: Fix the issue and re-run `prisma db migrate --to <contract>` — previously applied migrations are preserved.

Three properties make failure manageable instead of something to fear:

  • On PostgreSQL, a failed run leaves nothing behind. The entire db migrate run executes inside one transaction, so when an operation fails, everything from that run rolls back and the database is exactly where it was before you started. Migrations applied in earlier runs are untouched. That's what "previously applied migrations are preserved" means.
  • The error is specific. It names the operation, the phase (precheck, execute, or postcheck), and the check that failed, which is enough to fix the cause without spelunking.
  • Re-running is safe. Operations are idempotent: before running one, the runner evaluates its postcheck and skips it if the database already satisfies it. A change made outside of migrations doesn't break the run. The runner skips the operation instead. On MongoDB, where cross-collection transactions don't exist, this same mechanism is what makes a partially-applied run converge on retry. See Rollbacks and recovery for the full failure playbook.

Before running any DDL, db migrate also verifies the database's marker is a state the graph knows. A database that was changed outside of migrations fails fast with a marker mismatch instead of getting SQL applied on top of unknown drift.

Development vs. production

The commands are the same everywhere. What changes is where the files come from and who runs them.

In development, you're the one planning and editing, and you apply immediately:

bunx prisma@latest migration plan --name my_change && npx prisma@latest db migrate --advance-ref db

In CI and production, migrations arrive via your repo, already planned, reviewed, and merged. That's why the editing rule matters: commit migration.ts and ops.json together, and run migration check to catch a stale recompile before deployment. See Generating a migration for its exit codes. The deploy step is:

bunx prisma@latest migration check          # files intact, graph well-formed (offline)
bunx prisma@latest db migrate --show --db $DATABASE_URL   # log what's about to run
bunx prisma@latest db migrate --db $DATABASE_URL

The runner executes only ops.json, which is plain data, so your migration.ts files, and any TypeScript they import, are never executed with production credentials.

Concurrent deploys are safe: on PostgreSQL the whole apply runs inside a transaction guarded by an advisory lock, so two db migrate runs serialize instead of interleaving. On MongoDB, cross-collection DDL transactions don't exist. Instead, each migration advances the marker with compare-and-swap, and the runner verifies the resulting schema before committing the marker, so a re-run converges rather than double-applying.

Extension spaces

If your project uses database extensions (say pgvector), you'll see more than one contract space in the output. Extensions ship their own migrations (for example CREATE EXTENSION vector), tracked in migrations/<extension>/ next to your app's. One db migrate run walks them all (extensions first, then your app) and reports each space separately:

✔ Applied 20 operation(s) across 2 contract spaces

Extension space: pgvector
  └─ Enable extension "vector"

App space
  ├─ Create table "user"
  └─ ...

Prompt your coding agent

Projects scaffolded with create-prisma@latest install Prisma ORM skills for your coding agent. Ask your agent to:

  • "Check migration status against staging and apply whatever is pending."
  • "Preview what db migrate --to prod would run and summarize the destructive operations."
  • "Apply the pending migrations and advance the db ref."

See also

On this page