EF Core - Deployment: Migration Bundles
1. Overview
A Migration Bundle is a single-file executable introduced in Entity Framework Core 6. It encapsulates all database migrations and the logic required to apply them, allowing for database schema updates without requiring the .NET SDK or source code on the target environment.
Key Benefits
Zero Dependencies: Runs on target servers (Linux/Windows) without the .NET SDK.
Idempotency: Automatically checks the database state and applies only missing migrations.
CI/CD Compatibility: Designed to be executed as a standalone step in deployment pipelines (Azure DevOps, GitHub Actions, Docker).
Safety: Eliminates the concurrency risks associated with running
Database.Migrate()during application startup.
Comparison of Deployment Methods
| Feature | dotnet ef database update | Database.Migrate() (In-Code) | Migration Bundle |
| Requires .NET SDK? | Yes | No | No |
| Production Ready? | No (Dev only) | No (Concurrency risks) | Yes |
| Execution Context | Developer Machine | Application Startup | CI/CD Pipeline / Release Step |
| Artifact Type | CLI Command | DLL Method Call | Executable Binary |
2. The Development & Build Lifecycle
Phase 1: Local Development
Database changes originate from developers modifying the Entity models.
Change Implementation: Developers modify C# entity classes (e.g., adding properties).
Migration Generation: The command
dotnet ef migrations add <Name>generates:A timestamped migration file (e.g.,
20231027_AddCol.cs) containingUp()andDown()methods.An updated
ModelSnapshot.csrepresenting the current database state.
Conflict Resolution:
If multiple developers modify the database schema simultaneously, git merge conflicts will occur in
ModelSnapshot.cs.Resolution: The developer merging last must revert their local migration, pull the latest changes, and regenerate their migration to ensure it builds on top of the correct snapshot.
Phase 2: Build Process (CI)
The Continuous Integration system compiles the code into artifacts. Two distinct artifacts are generated:
Application Artifact: The compiled web application/API (DLLs).
Database Artifact: The Migration Bundle (Executable).
Command to Generate Bundle:
dotnet ef migrations bundle --self-contained -r linux-x64 --output efbundle
--self-contained: Bundles the .NET runtime (increases size, removes dependencies).-r <rid>: Specifies the target OS (e.g.,linux-x64,win-x64).--context: Specifies the DbContext if multiple exist.
3. Deployment Process (CD)
Execution Flow
Deployments follow a "Database First, App Second" approach to ensure the schema supports the application before traffic begins.
Deploy Bundle: The
efbundleexecutable is copied to the target environment (Staging/Production).Execute Bundle: The bundle is executed with the target connection string.
./efbundle --connection "Server=...;Database=...;"- Alternative: The bundle can read connection strings from environment variables (e.g.,
ConnectionStrings__DefaultConnection) automatically.
- Alternative: The bundle can read connection strings from environment variables (e.g.,
History Check: The bundle queries the
__EFMigrationsHistorytable.Apply Deltas: It identifies and executes only the migrations that have not yet been recorded in the history table.
Deploy Application: Once the bundle exits successfully (Exit Code 0), the new application version is deployed.
Multi-Developer Commits
When multiple developers contribute different migration files, the bundle aggregates them.
- Result: Upon execution, the
__EFMigrationsHistorytable will record individual entries for every applied migration file, preserving the complete audit trail of changes committed by all developers.
4. Review Process & Quality Control
Since the bundle is a binary file, it cannot be reviewed directly. The review process focuses on the source and generated SQL.
Artifacts for Review
Source Code (C#):
Focus: Check
Up()methods for destructive operations (DropColumn,DropTable).SQL Injection: Scrutinize any usage of
migrationBuilder.Sql("...").
Generated SQL Script (Recommended):
The CI pipeline should generate a SQL script artifact for review by DBAs.
Command:
dotnet ef migrations script --output preview.sql --idempotentThis provides a human-readable verification of exactly what SQL commands will run against production.
5. Rollback & Recovery Strategies
Strategy A: The "Down" Migration (Targeted Rollback)
This strategy reverts the database schema to a previous state using the Down() methods defined in the migration files.
Requirement: Developers must implement robust
Down()logic in every migration.Execution: Run the bundle with the
--targetflag specifying the last known good migration../efbundle --target 20231029_PreviousGoodStateOutcome: The bundle executes the
Down()logic of all migrations newer than the target and removes them from the history table.Post-Action: The application must be immediately rolled back to the matching binary version.
Strategy B: Roll Forward (Hotfix)
Preferred for production environments to avoid data loss risks associated with Down() scripts.
Process:
Do not revert the database.
Create a new migration that fixes the issue or manually reverts changes (e.g.,
RenameColumnback to original).Build and deploy a new bundle.
Benefit: Maintains a linear history and avoids destructive reverse operations.
Strategy C: Expand and Contract (Zero Downtime)
Designed to decouple deployment from release.
Expand: Add new columns/tables but keep old ones. Deploy. The DB supports both old and new app versions.
Migrate: Update the application to use new columns.
Contract: After stability is confirmed, deploy a cleanup migration to remove old columns.
6. Best Practices
Separation of Concerns: Never run migrations inside
Program.csor application startup code in production. Run them as a distinct "Release" or "Init" step.Environment Variables: Do not embed connection strings in the bundle build. Inject them at runtime via environment variables to maintain security.
Idempotency Checks: Always verify generated SQL scripts (
dotnet ef migrations script) in critical environments to ensure no accidental data loss occurs.Non-Destructive Migrations: Prefer adding nullable columns over renaming or dropping columns to ensure backward compatibility during deployment.
Build Once, Deploy Many: The same
efbundleexecutable generated in the Build phase should be promoted through environments (Dev -> Staging -> Prod) to ensure consistency.

