Prisma Migrations in Production: migrate dev vs migrate deploy
How to run Prisma migrations safely: migrate dev locally, migrate deploy in production, why db push and migrate reset don't belong near real data, where to run migrations in CI/CD, handling failed migrations with migrate resolve, baselining an existing database, and avoiding destructive changes.
Prisma has several commands that change your database schema. Using the wrong one in production is one of the most common ways AI-built apps lose data. Here's which to use where. (Database migrations explained, What is an ORM?)
The commands
| Command | What it does | Where |
|---|---|---|
prisma migrate dev |
Generates a new migration from schema changes, applies it, may reset the dev database | Development only |
prisma migrate deploy |
Applies pending migration files, in order. Never generates, never resets | Production, staging, CI |
prisma db push |
Syncs the database to the schema without migration files; may drop data | Prototyping only |
prisma migrate reset |
Drops the database and re-runs all migrations | Development only |
prisma migrate status |
Shows which migrations are applied/pending | Anywhere |
prisma migrate resolve |
Marks a migration as applied or rolled back | Fixing failed deploys |
Production gets migrate deploy. Nothing else.
The workflow
1. Develop
Change schema.prisma, then:
npx prisma migrate dev --name add_invoice_due_date
This creates prisma/migrations/20261002_add_invoice_due_date/migration.sql. Read the SQL. It's what will run against production.
2. Commit the migration files
The migrations/ folder is part of your code. Review it in pull requests like any other change. (What is a pull request?)
3. Deploy
In your deployment, before the new app version starts:
npx prisma migrate deploy
It applies any migrations not yet recorded in the _prisma_migrations table, and does nothing if there are none.
Where to run it:
- a release step in your deploy pipeline, or
- a separate job before switching traffic, or
- at container start (simple, but if several instances start at once, let one run it — Prisma takes an advisory lock to avoid concurrent runs). (Postgres advisory locks)
(Deploy to a VPS with GitHub Actions)
Configuration
Recent Prisma versions configure the datasource URL in prisma.config.ts rather than only in schema.prisma. Make sure production commands see the production connection string and, if you use a connection pooler, that migrations use a direct connection — migrations don't work well through transaction-mode poolers. (Postgres connection pooling)
Why db push doesn't belong in production
db push makes the database match the schema by whatever means necessary. Rename a field in the schema and it may drop the old column and create a new one — data gone. It also leaves no migration history to review or replay. Great for prototypes; dangerous for real data.
AI coding agents sometimes reach for db push --accept-data-loss or migrate reset to "fix" a migration error. Block those commands for agents working against anything but a throwaway database. (How to stop an AI agent deleting your production database, Claude Code settings)
Dangerous changes to watch for
Read every generated migration for:
DROP COLUMN/DROP TABLE— irreversible without a backup.- Renames — Prisma may generate drop + add; edit the SQL to
ALTER TABLE ... RENAME COLUMN. - New
NOT NULLcolumns without defaults on tables with data — fails or needs a backfill. - Type changes that rewrite large tables and lock them.
- New indexes on big tables — use
CREATE INDEX CONCURRENTLYin a custom migration.
For large tables, use the expand-and-contract pattern: add the new column, backfill, switch code, then remove the old one in a later deploy. (Postgres migrations on large tables, Zero-downtime deploys)
You can edit the generated migration.sql before committing, or create an empty migration with --create-only and write the SQL yourself.
When a migration fails in production
migrate deploy stops and records the failure. Further deploys refuse to run until you resolve it.
- Look at the error and the database state (
migrate status). - Fix the database manually or fix the migration.
- Tell Prisma what happened:
npx prisma migrate resolve --rolled-back 20261002_add_invoice_due_date
# or, if you completed it manually:
npx prisma migrate resolve --applied 20261002_add_invoice_due_date
Never edit a migration that has already run successfully anywhere — create a new one.
Existing database without migrations (baselining)
If production was built with db push or by hand, create a baseline migration from the current schema and mark it as applied, so future migrate deploy runs start from there. Prisma's docs describe this as "baselining".
Always have a backup first
Take (or confirm) a backup before running migrations that change or remove data, and know how to restore it. (Postgres backup and restore)
EasySpawn runs your app and its Postgres database with daily backups already in place, and preview deployments let you try a migration on a branch before it touches production. See how it works or join the waitlist.
Related: Database Migrations Explained · Postgres Migrations on Large Tables · How to Stop an AI Agent From Deleting Your Production Database · What Is an ORM?
Keep reading
Is SQLite Good Enough for Production? When It Works and When It Doesn't
SQLite now runs real production apps. When a single-file database is a great choice, the settings you must change (WAL mode, busy timeout, foreign keys), backups with Litestream, the single-writer limit, and the hosting setups where SQLite will lose your data.
Zero-Downtime Deploys for a Small App
You don't need Kubernetes to deploy without dropping requests. What actually causes downtime during a deploy — stopping before starting, no health checks, killed requests, and database changes the old code can't handle — and the four practices that fix each one.