~/blog · Tooling

Aspire for orchestrating .NET services in Docker

20 Mar, 2026 · 03 Mins read

The first time I introduced .NET Aspire to a team, half of them asked the right question: “we already have docker compose, what does this give us?” It is a fair question, and the answer is narrower than the marketing suggests. Aspire is a developer-environment tool. Once you keep that scope in mind, deciding whether to adopt it gets simpler.

The actual problem Aspire solves

A typical .NET system in 2026 is half a dozen services, a database, a message broker, a cache, and a couple of background workers. Getting a new engineer running on day one is the tax that nobody puts on the roadmap. Connection strings, seed data, the right local ports, the right environment variables, the order things start in. A docker-compose.yml covers the infrastructure parts but not the wiring inside .NET projects, which is where most of the friction actually lives.

Aspire’s AppHost project models the system as a graph in C#. You declare a Postgres resource, a Redis resource, a RabbitMQ resource, and your own services. You then express references between them: this API depends on this database; this worker depends on this broker. Aspire generates the connection strings, injects them as configuration, and stands the whole graph up with a single F5.

The two things I genuinely value:

  • Type-safe wiring. A reference between two resources is C#, not a string in YAML. Renames are refactor-safe; missing dependencies fail at startup, not in production at 2am.
  • Observability defaults. OpenTelemetry instrumentation for ASP.NET Core, HttpClient, EF Core, and the Aspire-managed resources is on by default, with an OTLP endpoint pointing at the Aspire dashboard. The dashboard gives you traces, logs, and metrics for the whole local graph in one place, with no configuration.

Combined, these two cut the “I cannot reproduce it on my machine” surface area significantly.

How it compares to plain docker compose

docker compose is general, language-agnostic, and runs the same in CI as locally. Aspire is .NET-flavored: the AppHost is a .NET project, resource references are .NET types, and the dashboard is opinionated about .NET telemetry. That is a feature if your stack is .NET, and a non-starter if it is not.

The trade-offs I see in practice:

  • With compose, you maintain a docker-compose.yml that tends to drift from the application configuration. With Aspire, the configuration is the application code, and drift is a compile error.
  • Compose requires that every service has a Docker image. Aspire happily orchestrates a project running directly via dotnet run alongside containerized infrastructure. For inner-loop development that is faster: you keep edit-and-continue and hot reload on your services, and only containerize the parts that benefit from it.
  • Compose is what your CI is going to run anyway. Aspire is not aiming at CI; if you want repeatable infrastructure-test environments in a pipeline, you still write compose files (or, increasingly, use Aspire’s container-image build to feed those pipelines).

For a pure-.NET system, I now reach for Aspire first for local development and keep compose for CI and for shared dev environments where Aspire’s host model does not fit.

Where Aspire leaves you wanting

Aspire is honest about its scope, but teams adopting it sometimes are not. A few things worth being explicit about:

  • Production deployment is your problem. Aspire can emit a manifest that some hosting tools consume (Azure Container Apps, for instance), but for Kubernetes, ECS, or anything bespoke, you still write your manifests, charts, or Terraform yourself. Treat Aspire’s deployment surface as a starting point at best.
  • The dashboard is dev-only. It is not a production observability backend. Wire your services to a real OTLP collector for staging and production; Aspire’s defaults make this easy because the instrumentation is already in place, but the destination changes.
  • It is bound to .NET. A Node sidecar or a Python worker can be modeled as a generic resource, but you lose much of the type-safe wiring story. If your system is genuinely polyglot, your AppHost will start to feel awkward.

The short version

Aspire is the best inner-loop orchestration tool I have used for .NET-heavy systems. It is not a deployment platform, it is not a production observability stack, and it is not a replacement for compose in CI. Inside that scope, it removes a category of friction that I had stopped noticing because we had all learned to live with it.