One of my favorite Aspire tricks is publishing your local orchestration to a plain Docker Compose file. No bespoke YAML hand-crafting: define your resources in C#, run one command, and get a production-shaped compose you can run anywhere Docker is available.
This post shows a tiny demo that publishes PostgreSQL, Redis, and the Aspire Dashboard to Docker Compose.
AppHost: enable the Docker publisher
var builder = DistributedApplication.CreateBuilder(args);
// Enables Docker publisher (gives the compose project a stable name)
builder.AddDockerComposeEnvironment("aspire-docker-demo");
var postgres = builder.AddPostgres("database")
.WithDataVolume();
var database = postgres.AddDatabase("demo-db");
var redis = builder.AddRedis("cache");
builder. Build().Run();
What this does
AddDockerComposeEnvironment("aspire-docker-demo")
Opts the app into the Docker publisher and sets the Compose project name (used for networks, volume names, etc.).- Postgres + volume
WithDataVolume()ensures a named Docker volume is created for data durability. AddDatabase("demo-db")
Declares a logical DB (helpful when apps bind to it; not strictly needed for Compose output).- Redis
Adds a simple cache service.
Publish to Docker Compose
From the AppHost directory:
aspire publish -o docker-compose-artifacts
This generates a docker-compose.yaml (and related files) under ./docker-compose-artifacts/.
Example output (trimmed for length)
services:
aspire-docker-demo-dashboard:
image: mcr.microsoft.com/dotnet/nightly/aspire-dashboard:latest
expose: ["18888","18889"]
networks: ["aspire"]
restart: always
database:
image: docker.io/library/postgres:17.6
environment:
POSTGRES_HOST_AUTH_METHOD: scram-sha-256
POSTGRES_INITDB_ARGS: "--auth-host=scram-sha-256 --auth-local=scram-sha-256"
POSTGRES_USER: postgres
POSTGRES_PASSWORD: ${DATABASE_PASSWORD}
expose: ["5432"]
volumes:
- type: volume
target: /var/lib/postgresql/data
source: dockerpublisher-...-database-data
networks: ["aspire"]
cache:
image: docker.io/library/redis:8.2
entrypoint: ["/bin/sh"]
command: ["-c","redis-server --requirepass $$REDIS_PASSWORD"]
environment:
REDIS_PASSWORD: ${CACHE_PASSWORD}
expose: ["6379"]
networks: ["aspire"]
networks:
aspire: { driver: bridge }
volumes:
dockerpublisher-...-database-data: { driver: local }
A few details worth noting:
- Aspire Dashboard is included by default so you can inspect logs/health even when running via Compose.
- Secrets via env:
DATABASE_PASSWORDandCACHE_PASSWORDare referenced as environment variables (read atdocker compose uptime). - Named volume for Postgres data provides persistence across container restarts.
- Single network keeps services discoverable and isolated.
Why publish this way?
- Parity: Your C# orchestration becomes a portable Compose file—perfect for teammate onboarding, CI smoke tests, or quick demos.
- Security knobs: Compose uses env-vars for secrets; wire these to your secrets manager or CI variables.
- Batteries included: You get the Aspire Dashboard out of the box for troubleshooting.
Next steps
- Bind your actual apps (Web/API) to these resources and republish.
- Add profiles or overrides for staging vs. prod (e.g., external ports, resource limits).
- Push images to a registry in CI and use the published Compose as your single-file deployment recipe.
For more advanced pipelines and examples, see Aspire Pipelines by David Fowler:https://github.com/davidfowl/AspirePipelines
That’s all folks!
Cheers!
Gašper Rupnik
{End.}

Leave a comment