Writing
Database
Mingtindu Sherpa8 min read

Prisma Migration Baseline for an Existing Database

Why Prisma Migrate reports the schema is not empty on an existing database, and how to baseline it correctly without a destructive reset.

On this page

Prisma Migrate assumes it owns the full history of your schema, tracked as an ordered list of migration files. That assumption breaks the first time you point it at a database that already has tables — created by hand, by a previous tool, or by a version of the project that predates Prisma Migrate. The result is a specific, well-defined error, not a bug:

Error: P3005
 
The database schema is not empty. Read more about how to baseline
an existing production database:
https://pris.ly/d/migrate-baseline

This article covers why that happens and how to baseline the database correctly — recording that its current state already satisfies your intended schema, without dropping or recreating anything.

Why this happens

Prisma Migrate tracks applied migrations in a table it manages itself: _prisma_migrations. On a brand-new, empty database, the first prisma migrate deploy creates every table from scratch and records that migration as applied. On a database that already has tables — but no _prisma_migrations history — Prisma has no record of how that schema came to exist. Running a migration that tries to CREATE TABLE on tables that already exist fails, and Prisma surfaces P3005 specifically to distinguish "the target isn't empty" from other migration failures. See the Prisma error reference for the full list of migration error codes.

This is not a sign of a corrupted schema or a Prisma bug. It's Prisma correctly refusing to guess at your existing schema's history.

Step 1: back up the database first

Before running any of the following commands against a database that matters, take a verified backup using your database or hosting provider's own backup mechanism. Baselining itself does not modify schema or data — but you are about to reconcile Prisma's expectations with a live database, and mistakes in that process (an incorrect migration file, a schema drift you didn't notice) are much cheaper to recover from with a backup than without one.

Step 2: capture the actual current schema

Prisma can generate a schema file from the database's real structure via introspection:

npx prisma db pull

This overwrites prisma/schema.prisma's models with what actually exists in the database right now — column types, relations, indexes, and constraints as Prisma can detect them. Review the diff carefully before proceeding:

  • Confirm every table and column you expect is present.
  • Check for relation and naming decisions Prisma's introspection made automatically (for example @@map or @map directives) that may not match how your application code already references these models.
  • If the project already has a hand-written schema.prisma that differs from the introspected one, resolve that difference deliberately — don't let introspection silently overwrite intentional customizations without review.

Do not run prisma migrate dev at this stage. It's a development command that expects to manage schema drift itself, including creating a shadow database — that's a different workflow from reconciling an already-correct production schema, and using it here is how unwanted schema changes get applied to a database that didn't need any.

Step 3: generate a baseline migration

The Prisma Migrate baselining guide documents this workflow:

mkdir -p prisma/migrations/0_init
 
npx prisma migrate diff \
  --from-empty \
  --to-schema-datamodel prisma/schema.prisma \
  --script > prisma/migrations/0_init/migration.sql

This generates a .sql file that would, if run against an empty database, produce the current schema — but you will not actually run it against this database, because the database is already in that state. The 0_ prefix keeps this migration ordered first, before any migrations that come after it.

Review migration.sql the same way you'd review any migration before it's treated as part of your permanent history: read it, and confirm it matches your actual schema's intent, not just its current accidental state.

Step 4: mark the baseline as applied — without running it

npx prisma migrate resolve --applied 0_init

This is the step that's easy to misunderstand. prisma migrate resolve --applied records an entry in _prisma_migrations saying this migration has already run — it does not execute the SQL inside it. Per Prisma's baselining documentation, this command exists specifically so migrations that already reflect reality don't get re-run against a database that already has that structure.

If you find yourself expecting resolve --applied to create anything, stop — that's a sign the schema doesn't actually match the migration yet, and you need Step 2 and Step 3 again before proceeding.

Step 5: verify before trusting it

After baselining, confirm the reconciliation actually matches reality instead of assuming it worked:

npx prisma migrate status

This reports whether the migration history matches what Prisma expects. Follow it with a non-destructive comparison between your Prisma schema and the live database:

npx prisma migrate diff \
  --from-schema-datamodel prisma/schema.prisma \
  --to-url "$DATABASE_URL" \
  --exit-code

A non-empty diff here means the schema and your migration history disagree about something — worth resolving before you build further migrations on top of an inaccurate baseline. Avoid printing $DATABASE_URL itself into logs; the command needs the variable set in the environment, not echoed to output.

Development vs. production workflow differences

prisma migrate dev and prisma migrate deploy solve different problems and are not interchangeable once a database is baselined:

migrate devmigrate deploy
Intended environmentLocal developmentCI/CD, staging, production
Creates new migrations from schema changesYesNo — only applies existing migration files
Uses a shadow databaseYesNo
Safe for a shared/production databaseNoYes, for applying already-reviewed migrations

After baselining, ongoing schema changes should still go through prisma migrate dev locally to generate new migration files, which then get committed and applied to the baselined database with prisma migrate deploy — the baseline migration simply becomes migration zero in that same history going forward.

Common mistakes

Reaching for a reset command to "start clean"

prisma migrate reset drops the target database and reapplies migrations from scratch. It is a development convenience command, not a way to resolve P3005 on a database with real data — using it against a production or shared database destroys existing data. This article does not present it as a normal solution to baselining.

Assuming resolve --applied verifies the schema matches

It records history; it does not check that migration.sql actually matches the live schema. That verification is your job, in Steps 2, 3, and 5 above — resolve trusts you.

Skipping the introspection review step

Prisma's introspection makes reasonable automatic decisions about naming and relations, but "reasonable" and "correct for your application" aren't guaranteed to be the same thing. Review the generated schema before it becomes the foundation every future migration builds on.

Baselining against the wrong environment's database

Confirm DATABASE_URL points at the database you intend to baseline before running any of these commands — the same care described in PostgreSQL Connection URL Explained applies directly here, since a misdirected URL points these commands at the wrong database entirely.

Verification checklist

  • A verified backup of the database exists before starting.
  • prisma db pull output was reviewed against the actual expected schema, not accepted blindly.
  • migration.sql was read and confirmed to represent the intended schema.
  • prisma migrate resolve --applied was run against the correct environment's database.
  • prisma migrate status reports no pending or missing migrations.
  • prisma migrate diff between the schema and the live database shows no unexpected difference.
  • No DATABASE_URL value or credential appears in logs, screenshots, or committed files.

Claims to manually verify before publishing

  • Claim: Your project's actual error was P3005 with this exact message.

    • Why verification is needed: This article uses Prisma's documented error text, not a captured incident from this project.
    • Suggested evidence: A sanitized terminal copy of the actual error output.
  • Claim: The baselining steps above match the Prisma CLI version this project uses.

    • Why verification is needed: Prisma Migrate's CLI flags (for example --to-schema-datamodel vs. older --to-schema) have changed between versions.
    • Suggested evidence: npx prisma -v output and the baselining documentation page matching that version.
  • Claim: This baselining process was actually performed successfully against a real project database.

    • Why verification is needed: No successful run, output, or outcome has been supplied for this draft.
    • Suggested evidence: Sanitized before/after prisma migrate status output and confirmation that subsequent migrate deploy runs succeeded.
  • Claim: A backup was taken before baselining in your actual incident.

    • Why verification is needed: This is presented as a required precaution, not a confirmed step you performed.
    • Suggested evidence: Backup timestamp or provider confirmation from the relevant database.

Related writing

Share