A powerful, flexible migration framework for Azure Cosmos DB. Manage document schema changes, index updates, and container modifications with version-controlled, reversible migrations.
$ dotnet add package CosmigratorA migration framework for Azure Cosmos DB. Version-controlled, reversible schema changes for your NoSQL documents.
Cosmigrator manages document schema changes in Cosmos DB the same way EF Core migrations handle relational databases — except it's built for NoSQL. Write C# migration classes, run them forward or roll them back from the CLI.
Supports .NET 8, .NET 9, and .NET 10.
dotnet add package Cosmigrator
dotnet new console -n MyService.Migrations
cd MyService.Migrations
dotnet add package Cosmigrator
appsettings.json{
"CosmosDb": {
"ConnectionString": "AccountEndpoint=https://your-account.documents.azure.com:443/;AccountKey=your-key",
"DatabaseName": "MyDatabase"
}
}
Program.csOne line. MigrationHost.RunAsync handles host building, Serilog setup, Cosmos client creation, and CLI parsing.
using System.Reflection;
using Cosmigrator;
await MigrationHost.RunAsync(args, Assembly.GetExecutingAssembly());
Create a Migrations/ folder and add your first migration:
using System.Text.Json.Nodes;
using Microsoft.Azure.Cosmos;
using Microsoft.Extensions.Logging;
public class _20250219_000001_AddEmailToUsers : IMigration
{
public string Id => "20250219_000001";
public string Name => "AddEmailToUsers";
public string ContainerName => "Users";
public object? DefaultValue => "";
public async Task UpAsync(Container container, CosmosClient client)
{
using var loggerFactory = LoggerFactory.Create(b => b.AddConsole());
var helper = new BulkOperationHelper(loggerFactory.CreateLogger<BulkOperationHelper>());
var docs = await helper.ReadAllDocumentsAsync(container);
var toUpdate = docs.Where(d => d["email"] == null).ToList();
foreach (var doc in toUpdate)
doc["email"] = JsonValue.Create(DefaultValue);
if (toUpdate.Count > 0)
await helper.BulkUpsertAsync(container, toUpdate);
}
public async Task DownAsync(Container container, CosmosClient client)
{
using var loggerFactory = LoggerFactory.Create(b => b.AddConsole());
var helper = new BulkOperationHelper(loggerFactory.CreateLogger<BulkOperationHelper>());
var docs = await helper.ReadAllDocumentsAsync(container);
foreach (var doc in docs)
doc.Remove("email");
await helper.BulkUpsertAsync(container, docs);
}
}
dotnet run # Apply all pending migrations
dotnet run -- rollback # Undo the last migration
dotnet run -- rollback --steps 3 # Undo the last 3
dotnet run -- status # Show applied vs pending
dotnet run -- list # Show all discovered migrations
Cosmigrator does not create containers automatically. You must provision them yourself (Terraform, Bicep, Azure Portal, etc.) before running migrations.
| Container | Partition Key | Purpose |
|---|---|---|
__MigrationHistory | /id | Tracks applied migrations |
| Your target containers | Per your schema | Where migrations operate |
IMigration interfaceEvery migration implements this interface:
public interface IMigration
{
string Id { get; } // "20250219_000001" — used for ordering
string Name { get; } // Human-readable description
string ContainerName { get; } // Target container
object? DefaultValue => null; // Optional default for property additions
Task UpAsync(Container container, CosmosClient client);
Task DownAsync(Container container, CosmosClient client);
}
Migrations are discovered automatically via reflection and executed in Id order.
The sample project includes working examples for common patterns:
| Migration | What it does |
|---|---|
AddAgePropertyToUsers | Adds a property with a default value to all documents |
RemoveMiddleNameProperty | Removes a property from all documents |
RenameUserNameToDisplayName | Renames a property while preserving data |
AddUniqueKeyPolicyToOrders | Changes unique key policy by recreating the container |
AddCompositeIndexToUsers | Adds a composite index to the indexing policy |
If you need control over configuration and logging, use the overload that accepts IConfiguration and ILoggerFactory:
using System.Reflection;
using Cosmigrator;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Serilog;
Log.Logger = new LoggerConfiguration().WriteTo.Console().CreateLogger();
var host = Host.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((_, config) =>
{
config
.SetBasePath(AppContext.BaseDirectory)
.AddJsonFile("appsettings.json", optional: false)
.AddEnvironmentVariables()
.AddCommandLine(args);
})
.UseSerilog((ctx, cfg) => cfg.ReadFrom.Configuration(ctx.Configuration))
.Build();
var loggerFactory = host.Services.GetRequiredService<ILoggerFactory>();
var configuration = host.Services.GetRequiredService<IConfiguration>();
await MigrationHost.RunAsync(configuration, loggerFactory, args, Assembly.GetExecutingAssembly());
This is useful when you want to:
Cosmigrator exits with code 0 on success and 1 on failure. This makes it easy to use as:
# Example: Kubernetes init container
initContainers:
- name: migrations
image: myregistry/myservice-migrations:latest
command: ["dotnet", "MyService.Migrations.dll"]
src/Cosmigrator/ Core library (the NuGet package)
samples/Cosmigrator.Sample/ Example console app with 5 migration scenarios
tests/Cosmigrator.Tests/ Unit tests (xUnit + FluentAssertions)
Contributions welcome. Please read CONTRIBUTING.md before opening a PR.
git clone https://github.com/AdelSS04/Cosmigrator.git
cd Cosmigrator
dotnet build
dotnet test