Prisma db push vs migrate deploy: which commands can destroy data?
Quick answer: All three can.
db pushdrops columns and whole tables once you pass--accept-data-loss.migrate devstops on drift and points you atmigrate reset, which wipes the schema.migrate deployis the right command for production, but it runs whatever SQL is in the migration files, includingDROP TABLE. Every behaviour on this page was run against Prisma 7.10.0 and PostgreSQL 17.11 in containers.
What is the problem?
Section titled “What is the problem?”Prisma has three commands that change a relational database, and they are for
different situations. None of them guarantees that data will be preserved.
Everything below is PostgreSQL; MongoDB uses db push and has no
migrate deploy path at all.
| command | what it is for | what it can destroy |
|---|---|---|
prisma db push | prototyping, no migration files | columns and whole tables, with --accept-data-loss; everything in the schema, with --force-reset |
prisma migrate dev | local development | the whole schema, through the migrate reset it points you at on drift |
prisma migrate deploy | staging and production | whatever the pending migration files say to destroy |
The confusion is reasonable. db push is the command in most getting-started
guides, because at that point there is nothing to lose. And migrate deploy is
correctly described everywhere as the production command, which can be mistaken
for a safe command. Those are different claims.
The one that matters: migrate deploy runs your SQL
Section titled “The one that matters: migrate deploy runs your SQL”migrate deploy does not generate migrations, prompt, or reset. It also does not
inspect what it is about to run. Committed to
prisma/migrations/20260919000001_drop_order/migration.sql:
DROP TABLE "Order";Measured on Prisma 7.10.0 against PostgreSQL 17.11, with two rows in that table:
Applying migration `20260919000001_drop_order`
The following migration(s) have been applied:
migrations/ └─ 20260919000001_drop_order/ └─ migration.sql
All migrations have been successfully applied.The table and both rows were gone. The command exited 0 without a prompt, a warning, or a flag.
The safety decision therefore happens before the migration is committed, not
when it is deployed. migrate deploy is the production command because it is
reproducible and non-interactive. Those properties do not make it
non-destructive. Review the SQL, rehearse it on a copy with real volume, and
have a backup you have actually restored from.
It refuses in two narrow situations: a non-empty schema with no migration history, which is P3005, further down, and an earlier migration that failed, which is P3009. Neither is a data-loss check.
What each one actually does
Section titled “What each one actually does”db push stops, then does it anyway when told
Section titled “db push stops, then does it anyway when told”Run against PostgreSQL 17 with a memo column removed from the model and one
non-null value in the table:
⚠️ There might be data loss when applying the changes:
• You are about to drop the column `memo` on the `Order` table, which still contains 1 non-null values.
Error: Use the --accept-data-loss flag to ignore the data loss warningsNothing changed. The warning names the column and counts the values.
Re-run with the flag, and the column and its values were gone. The rest of the row survived. This is a dropped column, not a dropped table.
The problem is that --accept-data-loss is exactly what a CI script may acquire
the first time the pipeline fails on that error.
It is not only columns. db push makes the database match the schema, so a table
the schema does not mention is a table db push wants gone, whether Prisma
created it or not. Measured against a database holding an unrelated
legacy_audit table with one row:
⚠️ There might be data loss when applying the changes:
• You are about to drop the `legacy_audit` table, which is not empty (1 rows).The risky case is a schema file that does not mention a table, combined with a flag that suppresses the warning.
There is a second flag, --force-reset, described by the CLI’s own help as
“Force a reset of the database before push”. On PostgreSQL the destructive scope
is everything in the target schema. It is useful while prototyping, but it
should not reach a committed script.
Prisma 7 has a gate on that flag aimed specifically at AI agents. Measured on
7.10.0, prisma db push --force-reset refused to run at all:
Error: Prisma Migrate detected that it was invoked by an AI agent identifyingitself as "claude-code_2-1-271_agent".
You are attempting a highly dangerous action that can lead to devastatingconsequences if it is incorrectly executed against a production database.
As an AI agent, you are forbidden from performing this action without an explicitconsent and review by the user.It then requires an environment variable carrying the exact text of the user’s
consent. This check has two limits: it is a Prisma-side check based on
environment variables that agents set, so a person or a CI job running the same
command is not gated, and it covers --force-reset, --accept-data-loss and
migrate reset rather than every destructive path. migrate deploy applying a
committed DROP TABLE, above, was not gated.
migrate dev stops on drift, and tells you to reset
Section titled “migrate dev stops on drift, and tells you to reset”Run against a database that had been built with db push, so the schema exists
and the migration history does not:
Drift detected: Your database schema is not in sync with your migration history.
[+] Added tables - Order
We need to reset the "public" schema at "127.0.0.1:15432"
You may use prisma migrate reset to drop the development database.All data will be lost.It stopped there, with exit code 130, and it does the same with a terminal attached: it does not offer to reset. The next command it suggests wipes the schema, and running it at the end of a long afternoon is the risk.
This is not a bug. migrate dev keeps the database matching the migration files,
and a reset is the normal way back for a disposable development database. It is
a development command, and it stops rather than resetting on its own. For a
database with data you want to keep, the answer is baselining rather than a
reset.
migrate deploy refuses when it has no history
Section titled “migrate deploy refuses when it has no history”A database with a schema in it and no migration history:
Error: P3005
The database schema is not empty. Read more about how to baseline an existingproduction database: https://pris.ly/d/migrate-baselineThat refusal is about history, not about data. Prisma cannot tell which migrations are already applied, so it stops rather than guess. “Not empty” means objects rather than rows: measured on 7.10.0, one table containing zero rows was enough to produce P3005.
It also does not tell you how the schema got there. _prisma_migrations being
absent means Prisma Migrate has no history here, which is equally consistent with
db push, another migration tool, a restored dump, or hand-written SQL. Find out
which before choosing what to do, because the answer changes it.
The fix for a database you want to keep is baselining: generate an initial migration from the current schema, read it, and edit it where Prisma’s model cannot represent what is really there, such as triggers, stored procedures, or partitioning. Then mark it applied without running it. New databases run that same migration normally.
One more thing that changed in Prisma 7
Section titled “One more thing that changed in Prisma 7”A url in the datasource block no longer validates:
Error code: P1012error: The datasource property `url` is no longer supported in schema files.Connection URLs for the Prisma CLI, which is migrate and db and the commands
on this page, moved to prisma.config.ts. Runtime connection configuration for
Prisma Client is a separate question in Prisma 7 and is not what this error is
about.
This matters here because Prisma 6 era tutorials and the schema.prisma files
generated from them still carry the old form, and the resulting error reads as a
syntax problem rather than a version change.
Measured on 2026-09-19, npx prisma -v reported 7.10.0 and the CLI’s own update
notice named 8.0.0-rc.15 as the latest tag on npm. Pin an exact Prisma
version in your project rather than resolving latest, and check what your
tooling actually installed before trusting instructions about the command
surface, including this page.
How do I confirm what a command will do?
Section titled “How do I confirm what a command will do?”Ask what is pending first. migrate status names the migrations that have not
run:
2 migrations found in prisma/migrationsFollowing migration have not yet been applied:20260919000001_drop_orderThen ask for the SQL those migrations will execute by diffing the live database against the migration directory. On Prisma 7 the flags are:
prisma migrate diff \ --from-config-datasource \ --to-migrations ./prisma/migrations \ --scriptMeasured on 7.10.0 against the database above, that printed exactly the statement
migrate deploy was about to run:
-- DropTableDROP TABLE "public"."Order";Two things are required. prisma/migrations/migration_lock.toml has to be
present, or it cannot tell which connector the directory is for. And
datasource.shadowDatabaseUrl has to be set in prisma.config.ts, because
diffing a migrations directory means replaying it into a scratch database first.
Both failures produce explicit error messages rather than wrong answers.
The older --from-schema-datasource and --to-schema-datamodel flags are gone
in Prisma 7. They were also the wrong comparison: diffing the database against
the schema file tells you nothing about what the committed migrations will do.
Then read the SQL, not only for DROP. Anything that rewrites the table,
tightens a constraint, narrows a type, changes a collation, drops an index or an
enum value, or cascades through a foreign key is a decision rather than a step.
ALTER COLUMN ... TYPE is on that list because of the rewrite and the lock, not
because it is inherently destructive.
Check whether the database has a migration history at all, which decides
between the deploy path and the baselining path:
SELECT migration_name, finished_at, applied_steps_countFROM _prisma_migrationsORDER BY finished_at DESCLIMIT 10;If that table does not exist, Prisma Migrate has no history for this database and
migrate deploy will give you P3005. It does not tell you what built the schema.
Also confirm the current schema from the database rather than from the model file:
SELECT table_name, column_name, data_type, is_nullableFROM information_schema.columnsWHERE table_schema = 'public'ORDER BY table_name, ordinal_position;One detail that surprises people there: Prisma’s Decimal maps to
numeric(65,30) on PostgreSQL, which is what the column will be unless you
override it.
Why does AI-generated code cause this?
Section titled “Why does AI-generated code cause this?”db push is the command in the quickstart, so it is the command a model
suggests, and it is correct for the situation the quickstart describes. Nothing
in “update the schema and apply it” says which environment this is.
The second pattern is the flag that makes an error go away. An agent that sees
Use the --accept-data-loss flag will add that flag, because the message is an
instruction and following it makes the command succeed. That is the correct
reading of the text and the wrong thing to do.
The third is prisma migrate dev in a deploy script, because it is the command
from the development loop and it works.
The fourth is the situation this page addresses: an agent told to use
migrate deploy because it is the production command writes a migration that
drops a column and commits it to the migrations directory. Each step follows
the documented workflow. The destructive SQL is in a file nobody read.
How do I stop it coming back?
Section titled “How do I stop it coming back?”Only migrate deploy in CI and production. Make it the only Prisma command
the deploy role can run, if your tooling allows that. This limits which command
runs, not what the command does.
Review the migration SQL, because that is where the safety is. The file is
committed, so it goes through the same review as the code. A DROP in it is a
decision somebody made, and the deploy step will carry it out.
Never let --accept-data-loss or --force-reset into a committed script.
Grep for both in review; they are easy to spot and easy to overlook.
Baseline properly once, so that P3005 does not have to be worked around.
Preview with migrate status then migrate diff --to-migrations --script
for anything touching a table with data, and have a backup you have restored from
before you apply it. A reverse migration puts the column back; it does not put
the values back.
Rename with ALTER TABLE ... RENAME COLUMN, in a migration you edited. A
renamed field is otherwise inferred as a drop plus an add, which loses every
value in the column while looking like an ordinary schema change.
Which Prisma command is safe for production?
prisma migrate deploy is the right command for production, and it is not a safety mechanism. It applies whatever SQL the pending migration files contain, non-interactively. Measured on Prisma 7.10 and PostgreSQL 17.11, a committed migration containing DROP TABLE "Order" was applied by migrate deploy against a table holding two rows, with no prompt, no warning and no flag. The safety is in reviewing the migration before it is committed.
Can prisma migrate deploy destroy data?
Yes. It executes the SQL in the migration files. A DROP TABLE, DROP COLUMN or narrowing type change that somebody committed is applied without a warning, because deploy is a deployment mechanism rather than a data-loss interlock. It refuses when a non-empty schema has no migration history, which is error P3005, and when an earlier migration failed, which is error P3009. Neither is a data-loss check.
Does prisma db push delete data?
It will, with --accept-data-loss. Without the flag it stops: measured on Prisma 7.10, removing a column from the schema produced 'You are about to drop the column memo on the Order table, which still contains 1 non-null values' and an instruction to pass the flag. With the flag, the same push dropped the column and its values. The surviving rows were untouched.
Can prisma migrate dev wipe my database?
Not by itself on Prisma 7.10. Measured against a database with drift, it printed 'We need to reset the public schema' and 'All data will be lost.', then stopped with exit code 130 and pointed at prisma migrate reset, with or without a terminal attached. The reset it points at wipes the schema. It is a development command and should never run against anything you care about.
What is error P3005?
The database schema is not empty and Prisma has no migration history for it, so migrate deploy cannot know which migrations are already applied. Not empty means objects, not rows: measured on Prisma 7.10, one table with zero rows in it was enough. It happens whenever a schema predates Prisma Migrate, whether that is db push, another tool, a restored dump or hand-written SQL. The fix is baselining: generate the initial migration and mark it as already applied.
Did the datasource url move in Prisma 7?
Yes. Measured on Prisma 7.10, a url in the datasource block of schema.prisma fails validation with P1012: 'The datasource property url is no longer supported in schema files.' Connection URLs for Migrate now live in prisma.config.ts. Generated code and older tutorials still use the old form.