EF Core 10 Migrations are easy in development and unforgiving in production. A local dotnet ef database update touches a database you control, but a production server has real users, real data, and no room for a half-applied schema. The professional workflow generates an idempotent SQL script, runs it through CI/CD, and treats migrations like the deploys they actually are.
This article covers the production side of EF Core 10 Migrations: what idempotent means, how to generate an idempotent script, where the step fits in a pipeline, and the operational habits that keep schema deploys boring. Boring, in this context, is a compliment — the best production migrations are the ones nobody has to think about twice.
Why EF Core 10 Migrations Need Idempotent Scripts
Idempotent means safe to run more than once. dotnet ef migrations script --idempotent generates SQL that checks whether each migration is already applied and executes only the missing ones. In production you cannot assume a deploy succeeds on the first attempt — a timeout, a deadlock, a restart, a rollback — so the same script must be re-runnable without side effects.
Contrast that with dotnet ef database update against a production box. It needs the CLI tool, the design-time packages, and a developer account with write access to the database. That is the anti-pattern this article exists to remove: the production schema should be changed by the release pipeline, not by a developer's laptop.
Idempotency also removes a whole class of deployment failures. A pipeline retry, a flaky connection, an operator who runs the script twice by accident — none of these corrupt anything, because the history table decides what happens next. Compare that with a hand-written ALTER statement, which fails loudly on the second run and can leave a half-changed schema behind. The migration script is designed for exactly the uncertainty that production actually has.
Generating Idempotent SQL from EF Core 10 Migrations
dotnet ef migrations script --idempotent -o deploy.sql
The generated file wraps every migration in a conditional against the __EFMigrationsHistory table — the ledger EF Core keeps of every applied migration. Run the file once and it applies the pending migrations; run it twice and it does nothing. That property converts a database deploy into a simple, repeatable step your operations team already knows how to run.
There are two reasons to prefer the script over pointing the application at the database. First, it is plain SQL: DBAs can read it, review it, and execute it with their own tooling. Second, each migration runs inside a transaction, so a failure leaves a consistent schema and a clear record in the history table.
The command is deterministic given the same code: the same migration chain always produces the same script, which makes the artifact part of your release notes. Your CI job can even fail fast when the generated script is unchanged for a release that claims schema changes — a reliable tripwire for the classic mistake of merging entity changes without generating a migration.
Applying EF Core 10 Migrations in a CI/CD Pipeline
The cleanest pattern is a dedicated release step that applies the schema before the new application version is swapped in. A typical pipeline looks like this:
- Build: compile the solution and run the test suite in CI.
- Generate: run the script command and keep deploy.sql as a build artifact.
- Deploy: execute the script against the target database with a dedicated deploy account.
- Release: roll out the application; the schema is already waiting for it.
Running migrations before the app starts means the new version never sees an outdated schema, and the previous version keeps working during the switch. For a Blazor Server application behind a load balancer, that ordering eliminates the classic HTTP 500 after release — the one that only happens until someone remembers to run the migration manually.
Keep the deploy account narrowly scoped. The user that runs the script needs DDL rights on the application schema and nothing else — no server admin, no access to other databases. Credentials belong in the pipeline's secret store, never in the repository, and the connection string used to generate the script in CI should point at a throwaway database, not production. A failed script generation must never be able to touch real data.
EF Core 10 Migrations in Production: Safe Apply Practices
A few habits keep production migrations unexciting, and unexciting is what you want.
- Back up the database before every schema change.
- Lock the version of dotnet-ef used to generate the script so scripts stay reproducible.
- Test the exact script against a staging clone that mirrors production data volume.
- Run migrations in a maintenance window, with the app scaled to a single instance.
- Never run database update from a developer machine.
Apply these together and an EF Core 10 Migrations deploy becomes the most predictable step in your pipeline: a script, a log entry, and a schema that matches the model snapshot exactly. When the history table says applied and the release goes out, the database is simply no longer a variable in the deployment equation.
Finally, monitor the deploy. The migration history table is a perfect health check: after release it should list every migration the build contained, in order, with nothing missing. A single query for the last applied migration is the fastest way to confirm the schema is in sync before you tell anyone the deploy succeeded — and it doubles as the first troubleshooting step if something still feels off.
Key Takeaways
- Idempotent scripts are safe to run multiple times — essential for production retries.
dotnet ef migrations script --idempotentreads the migration history table and applies only what is missing.- Apply the schema before the app release, using a dedicated deploy account.
- Back up, stage-test, and version-lock your migration tooling.
- Keep production schema changes in the pipeline, never on a developer laptop.
FAQ
What is the difference between --idempotent and a plain script?
A plain script runs every migration unconditionally and will fail if a migration is already applied. An idempotent script checks the history table first and skips applied migrations, so it can be re-run safely.
Should the application apply migrations on startup?
In production, no. Startup migration couples schema changes to instance rollout and can run the same migration twice under a load balancer. Apply the script once in the pipeline, and let the app assume the schema is ready.
What if a migration fails halfway through the script?
The failed migration rolls back its transaction, and the history table records nothing for it. Fix the migration, regenerate the script, and re-run — the script starts exactly where it stopped.
Where can I see a production-grade EF Core 10 Migrations setup?
Indotalent products ship complete .NET 10 source code — including EF Core 10 schema, migrations, and deployment-ready scripts — for $21 each.