Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
This guide helps you understand how to migrate applications from Docker Compose to Aspire, highlighting the key conceptual differences and providing practical examples for common migration scenarios.
Understand the differences
While Docker Compose and Aspire might seem similar at first glance, they serve different purposes and operate at different levels of abstraction.
Docker Compose vs Aspire
| Docker Compose | Aspire | |
|---|---|---|
| Primary purpose | Container orchestration | Development-time orchestration and app composition. |
| Scope | Container-focused | Multi-resource (containers, .NET projects, cloud resources). |
| Configuration | YAML-based | C#-based, strongly typed. |
| Target environment | Any Docker runtime | Development and cloud deployment. |
| Service discovery | DNS-based container discovery | Built-in service discovery with environment variables. |
| Development experience | Manual container management | Integrated tooling, dashboard, and telemetry. |
For more information, see Docker Compose to Aspire AppHost API reference.
Key conceptual shifts
When migrating from Docker Compose to Aspire, consider these conceptual differences:
- From YAML to C#: Configuration moves from declarative YAML to imperative, strongly-typed C# code.
- From containers to resources: Aspire manages not just containers, but .NET projects, executables, parameters, all as resources.
- From manual networking to service discovery: Aspire automatically configures service discovery and connection strings.
- From development gaps to integrated experience: Aspire provides dashboard, telemetry, and debugging integration.
For a comprehensive reference mapping Docker Compose YAML syntax to Aspire C# API calls, see Docker Compose to Aspire AppHost API reference.
Related links:
Common migration patterns
This section demonstrates practical migration scenarios that you'll likely encounter when moving from Docker Compose to Aspire. Each pattern shows a complete Docker Compose example alongside its Aspire equivalent, highlighting the key differences and benefits of the migration.
The patterns covered include:
- Multi-service web applications with frontend, API, and database components.
- Container-based services using existing Docker images.
- Environment variables and configuration management strategies.
- Custom networks and volumes for data persistence and service isolation.
These examples represent the most common use cases, but Aspire's flexibility allows for many other scenarios. If your specific use case isn't covered here, you can combine these patterns or refer to the comprehensive API reference above.
Multi-service web application
Docker Compose example:
version: '3.8'
services:
frontend:
build: ./frontend
ports:
- "3000:3000"
depends_on:
- api
environment:
- API_URL=http://api:5000
api:
build: ./api
ports:
- "5000:5000"
depends_on:
- database
environment:
- ConnectionStrings__DefaultConnection=Host=database;Database=myapp;Username=postgres;Password=secret
database:
image: postgres:15
environment:
- POSTGRES_DB=myapp
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=secret
volumes:
- postgres_data:/var/lib/postgresql/data
volumes:
postgres_data:
Aspire equivalent:
var builder = DistributedApplication.CreateBuilder(args);
// Add the database
var database = builder.AddPostgres("postgres")
.WithEnvironment("POSTGRES_DB", "myapp")
.AddDatabase("myapp");
// Add the API project
var api = builder.AddProject<Projects.MyApp_Api>("api")
.WithReference(database);
// Add the frontend project
var frontend = builder.AddProject<Projects.MyApp_Frontend>("frontend")
.WithReference(api);
builder.Build().Run();
Key differences:
- Service dependencies:
depends_onbecomesWithReference()which also configures service discovery. - Environment variables: Connection strings are automatically generated and injected.
- Build context: Build context is capable of using Dockerfiles, .NET projects, Node.js apps, and more instead of just Dockerfile builds.
- Data persistence: Volumes are automatically managed by Aspire.
Container-based services
Docker Compose example:
version: '3.8'
services:
web:
build: .
ports:
- "8080:8080"
depends_on:
- redis
- postgres
redis:
image: redis:7
ports:
- "6379:6379"
postgres:
image: postgres:15
environment:
POSTGRES_PASSWORD: secret
volumes:
- postgres_data:/var/lib/postgresql/data
volumes:
postgres_data:
Aspire equivalent:
var builder = DistributedApplication.CreateBuilder(args);
// Add backing services
var cache = builder.AddRedis("redis");
var database = builder.AddPostgres("postgres", password: "secret")
.AddDatabase("main");
// Add the containerized web application
var web = builder.AddContainer("web", "myapp:latest")
.WithHttpEndpoint(port: 8080, targetPort: 8080)
.WithReference(cache)
.WithReference(database);
builder.Build().Run();
Environment variables and configuration
Docker Compose approach:
services:
app:
image: myapp:latest
environment:
- DATABASE_URL=postgresql://user:pass@db:5432/myapp
- REDIS_URL=redis://cache:6379
- API_KEY=${API_KEY}
- LOG_LEVEL=info
Aspire approach:
var builder = DistributedApplication.CreateBuilder(args);
// Add external parameter
var apiKey = builder.AddParameter("apiKey", secret: true);
var database = builder.AddPostgres("db")
.AddDatabase("myapp");
var cache = builder.AddRedis("cache");
var app = builder.AddContainer("app", "myapp:latest")
.WithReference(database) // Automatically sets DATABASE_URL
.WithReference(cache) // Automatically sets REDIS_URL
.WithEnvironment("API_KEY", apiKey)
.WithEnvironment("LOG_LEVEL", "info");
builder.Build().Run();
Key differences:
- Automatic connection strings: Database and cache URLs are automatically generated.
- Typed parameters: External configuration uses strongly-typed parameters instead of environment variable substitution.
- Service discovery: References automatically configure the correct service endpoints.
Custom networks and volumes
Docker Compose example:
version: '3.8'
services:
app:
image: myapp:latest
volumes:
- app_data:/data
- ./config:/app/config:ro
networks:
- backend
worker:
image: myworker:latest
volumes:
- app_data:/shared
networks:
- backend
networks:
backend:
volumes:
app_data:
Aspire equivalent:
var builder = DistributedApplication.CreateBuilder(args);
// Create a named volume
var appData = builder.AddVolume("app-data");
var app = builder.AddContainer("app", "myapp:latest")
.WithVolume(appData, "/data")
.WithBindMount("./config", "/app/config", isReadOnly: true);
var worker = builder.AddContainer("worker", "myworker:latest")
.WithVolume(appData, "/shared");
builder.Build().Run();
Key differences:
- Simplified networking: Aspire automatically handles container networking.
- Volume management: Named volumes are created and managed through the resource model.
- Bind mounts: Host directories can be mounted with
WithBindMount().
Related links:
Migration strategy
Successfully migrating from Docker Compose to Aspire requires a systematic approach. The following steps provide a proven methodology for moving your applications while minimizing disruption and ensuring all components work correctly in the new environment.
1. Assess your current setup
Before migrating, inventory your Docker Compose setup:
- Services: Identify all services including databases, caches, APIs, and web applications.
- Dependencies: Map out service dependencies from
depends_ondeclarations. - Data persistence: Catalog all volumes and bind mounts used for data storage.
- Environment variables: List all configuration variables and secrets.
- Custom networks: Identify any custom networking requirements and configurations.
2. Create the Aspire AppHost
Start by creating a new Aspire project:
dotnet new aspire-apphost -o MyApp.AppHost
3. Migrate services incrementally
Migrate services one by one, starting with backing services (databases, caches) and then applications:
- Add backing services like PostgreSQL, Redis, and other infrastructure components.
- Convert .NET applications to project references for better integration.
- Convert other containers using
AddContainer()for existing Docker images. - Configure dependencies with
WithReference()to establish service relationships. - Set up environment variables and parameters for configuration management.
4. Handle data migration
For persistent data:
- Use
WithVolume()for named volumes that need to persist data. - Use
WithBindMount()for host directory mounts when you need direct access to host files. - Consider using
WithDataVolume()for database persistence with automatic volume management.
5. Test and validate
- Start the Aspire AppHost and verify all services start correctly.
- Check the dashboard to confirm service health and connectivity status.
- Validate that inter-service communication works as expected.
- Test with your existing client applications to ensure compatibility.
Related links:
Migration troubleshooting
When migrating from Docker Compose to Aspire, you might encounter some common challenges. This section provides solutions to frequently encountered issues and guidance on how to troubleshoot problems that arise during the migration process.
Common issues and solutions
Service discovery not working
- Ensure you're using
WithReference()to establish dependencies between services. - Check that services are using the correct environment variable names for connections.
- Review the dashboard to verify environment variables are injected correctly.
Database connections failing
Verify the database is fully started before dependent services attempt to connect.
Use
WaitFor()to ensure proper startup ordering:var api = builder.AddProject<Projects.Api>("api") .WithReference(database) .WaitFor(database);
Volume mounting issues
- Use absolute paths for bind mounts to avoid path resolution issues.
- Ensure the host directory exists and has proper permissions before mounting.
- Consider using named volumes instead of bind mounts where possible for better portability.
Port conflicts
- Aspire automatically assigns ports to avoid conflicts between services.
- Use
WithHttpEndpoint()to specify custom ports if needed for external access. - Check the dashboard for actual assigned ports during development.
Related links:
- Docker Compose FAQ
- Container runtime unhealthy
- Container runtime 'podman' could not be found in WSL
- Aspire dashboard overview
Next steps
After migrating to Aspire:
- Explore Aspire integrations to replace custom container configurations.
- Set up health checks for better monitoring.
- Configure telemetry for observability.
- Learn about deployment options for production environments.
- Consider testing your distributed application.