.NET Aspire and Cloud-Native Development
|
This section documents ASP.NET Core on .NET 10 (LTS), the current release — the minimal hosting model, the middleware pipeline, dependency injection, Minimal APIs, MVC & Razor Pages, Blazor with the current render modes, SignalR and gRPC, EF Core, ASP.NET Core Identity and policy-based authorization, output caching, rate limiting, and Native-AOT-aware building — as described by the official documentation at Microsoft Learn, which is the reference these pages are written and verified against. This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production. .NET ships a major release every November and its APIs continue to evolve: the examples here target .NET 10 / C# 14. This section’s bibliography lists the reference material consulted while preparing these pages. |
ASP.NET Core API, a Blazor front end, a database, a cache, and a message broker, wired together, observed, and started with one command.
The AppHost project
dotnet new aspire-apphost (or the full aspire starter template) scaffolds an AppHost project: an
executable .csproj whose Program.cs declares every resource the distributed application needs and how they
connect:
var builder = DistributedApplication.CreateBuilder(args);
var cache = builder.AddRedis("cache");
var db = builder.AddPostgres("postgres").AddDatabase("ordersdb");
var api = builder.AddProject<Projects.OrdersApi>("orders-api")
.WithReference(db)
.WithReference(cache);
builder.AddProject<Projects.Web>("web")
.WithReference(api)
.WithExternalHttpEndpoints();
builder.Build().Run();
Running the AppHost starts every referenced project and container resource together, opens the Aspire dashboard, and injects each resource’s connection details into the projects that reference it. See .NET Aspire overview and .NET Aspire orchestration overview.
The ServiceDefaults project
A shared ServiceDefaults project (also scaffolded by the template) centralizes the cross-cutting setup every
service in the solution wants — OpenTelemetry, health checks, service discovery, and resilient
HttpClient defaults:
// ServiceDefaults/Extensions.cs
public static class Extensions
{
public static TBuilder AddServiceDefaults<TBuilder>(this TBuilder builder)
where TBuilder : IHostApplicationBuilder
{
builder.ConfigureOpenTelemetry();
builder.AddDefaultHealthChecks();
builder.Services.AddServiceDiscovery();
builder.Services.ConfigureHttpClientDefaults(http =>
{
http.AddServiceDiscovery();
http.AddStandardResilienceHandler(); // see
// xref:web/aspnet/core/http-client-and-resilience.adoc[HTTP Client and Resilience]
});
return builder;
}
}
// OrdersApi/Program.cs
var builder = WebApplication.CreateBuilder(args);
builder.AddServiceDefaults();
Every project referenced by the AppHost calls AddServiceDefaults() once, so telemetry, health checks, and
resilient outbound HTTP calls are consistent across the whole distributed app instead of configured
per-project. See
.NET Aspire service defaults.
Resources and references
An Aspire resource is anything the app depends on — another project, a container (Redis, PostgreSQL,
RabbitMQ), or an external endpoint. WithReference(resource) on a project injects that resource’s connection
information (connection string, endpoint URL) into the referencing project’s configuration at startup, so the
referencing code just reads IConfiguration/a typed client as usual, with the actual value supplied by the
AppHost rather than an appsettings.json file.
Service discovery
Projects that reference each other resolve one another by their logical name ("orders-api" above), not a
hardcoded host/port — AddServiceDiscovery() (part of ServiceDefaults) resolves
https://orders-api to the actual running endpoint, which differs between local runs (a localhost port
Aspire allocates) and a deployed environment (a real service address), with no code change:
builder.Services.AddHttpClient<OrdersApiClient>(c => c.BaseAddress = new Uri("https://orders-api"));
The dashboard
The Aspire dashboard (opened automatically when the AppHost runs) gives one place to see the whole distributed app while developing:
-
Structured logs — aggregated across every resource, filterable by resource and level.
-
Traces — distributed traces spanning multiple services for one logical request, built on the same OpenTelemetry data covered in Error Handling, Logging, and Observability.
-
Metrics — live charts per resource (request rate, duration, custom
System.Diagnostics.Metricsinstruments).
This replaces manually juggling multiple terminal windows and separately wiring up an OpenTelemetry collector just to see what a local multi-service run is doing. See .NET Aspire dashboard overview.
Integrations
An Aspire integration is a NuGet package pairing an AppHost-side resource type with a client-side
Add<Resource>Client() extension preconfigured with health checks, telemetry, and resilience for that specific
dependency:
// AppHost
var postgres = builder.AddPostgres("postgres").AddDatabase("ordersdb");
// OrdersApi
builder.AddNpgsqlDbContext<OrdersDbContext>("ordersdb"); // Aspire.Npgsql.EntityFrameworkCore.PostgreSQL
Official integrations cover PostgreSQL, Redis, RabbitMQ, SQL Server, MongoDB, Kafka, and more, each following the same "AppHost declares the resource, client project adds the matching integration" shape. See .NET Aspire integrations overview.
Local orchestration vs. deployment
Aspire’s AppHost is a local development and orchestration tool — it is not itself a production hosting runtime. Deploying an Aspire-built app means translating its resource graph to a real target:
-
azd(Azure Developer CLI) reads the AppHost’s resource graph and provisions/deploys matching Azure resources (Container Apps, Azure Database for PostgreSQL, Azure Cache for Redis, …). -
A manifest export (
dotnet run --project AppHost — --publisher manifest) produces a target-agnostic JSON description of the resource graph other tooling (including third-party deployment tools) can consume.
See .NET Aspire deployment overview and Deployment for the underlying container/hosting mechanics being deployed to.
Aspire vs. Docker Compose / Kubernetes
| Tool | Role |
|---|---|
.NET Aspire AppHost |
Describes the resource graph in C#, drives local multi-project/container startup with service discovery and one dashboard; not a production orchestrator itself. |
Docker Compose |
A container-only, YAML-described local orchestration tool — Aspire can run containers too, but also directly launches .NET projects (no Dockerfile needed for local iteration) and adds the dashboard/telemetry/service-discovery layer Compose does not provide out of the box. |
Kubernetes |
A production container orchestrator — Aspire is not a Kubernetes alternative; an Aspire app is typically deployed to Kubernetes (or another target) rather than run there via the AppHost. |
Aspire and Docker Compose/Kubernetes are complementary at different stages: Aspire for the local
inner-loop experience, Compose/Kubernetes (or a cloud-native platform reached via azd) for how the same
services actually run in production.