.NET Aspire: Building Cloud-Native Apps Made Simple
.NET Aspire is the most impactful addition to the .NET ecosystem since minimal APIs. It focuses on the developer inner loop — making local development of distributed apps genuinely pleasant. Let's explore what it does and why the built-in dashboard alone is worth the migration.
What Aspire Actually Is
Aspire bundles three capabilities: an orchestrator (AppHost wiring services together), a component library (pre-built integrations for Redis, PostgreSQL, RabbitMQ, etc.), and a dashboard (real-time logs, traces, and metrics across all services).
var builder = DistributedApplication.CreateBuilder(args);
var cache = builder.AddRedis("cache");
var postgres = builder.AddPostgres("db").AddDatabase("orders");
var messaging = builder.AddRabbitMQ("messaging");
var catalogApi = builder.AddProject<Projects.CatalogApi>("catalog-api")
.WithReference(postgres)
.WithReference(cache);
var orderApi = builder.AddProject<Projects.OrderApi>("order-api")
.WithReference(postgres)
.WithReference(messaging)
.WithReference(catalogApi);
builder.AddProject<Projects.WebFrontend>("web-frontend")
.WithExternalHttpEndpoints()
.WithReference(catalogApi)
.WithReference(orderApi);
builder.Build().Run();
No Docker Compose. No environment variable juggling. Aspire pulls container images automatically, assigns ports, injects connection strings, and sets up service discovery. The dashboard appears at https://localhost:15888.
Service Discovery
When you add .WithReference(catalogApi), Aspire configures HTTP client factory so you call services by name:
builder.AddServiceDefaults();
builder.Services.AddHttpClient<CatalogClient>(client =>
{
client.BaseAddress = new Uri("https+http://catalog-api");
});
public class CatalogClient(HttpClient httpClient)
{
public async Task<Product?> GetProductAsync(int id)
=> await httpClient.GetFromJsonAsync<Product>($"/api/products/{id}");
}
The https+http:// scheme tries HTTPS first, falls back to HTTP. Note: Aspire's built-in service discovery is designed for the development inner loop — for production AKS deployments, use DNS-based discovery.
Integrations and Resilience
Each integration configures health checks, connection pooling, retry policies, and telemetry automatically:
builder.AddServiceDefaults();
builder.AddRedisDistributedCache("cache");
builder.AddNpgsqlDbContext<OrderDbContext>("orders");
builder.AddRabbitMQClient("messaging");
Combine with Microsoft.Extensions.Http.Resilience for production-grade HTTP calls:
builder.Services.AddHttpClient<CatalogClient>(client =>
{
client.BaseAddress = new Uri("https+http://catalog-api");
})
.AddStandardResilienceHandler(options =>
{
options.Retry.MaxRetryAttempts = 3;
options.CircuitBreaker.SamplingDuration = TimeSpan.FromSeconds(10);
options.AttemptTimeout.Timeout = TimeSpan.FromSeconds(5);
});
Deployment
Aspire orchestrates local development but doesn't deploy your app. Options include:
- Azure Container Apps via
azd init && azd up(first-class support) - Kubernetes/AKS via Aspirate tool (
aspirate generate) for Helm charts - Docker Compose via
aspirate generate --output-format compose
Key Takeaways
- Start with the AppHost and dashboard. The orchestration and observability alone justify Aspire.
- Don't fight the conventions. Aspire is opinionated about connection strings, health checks, and telemetry — embrace the patterns.
- Use service discovery for development, DNS for production.
- Plan your deployment strategy early — decide between ACA, AKS, or Compose before going deep.
If you're building anything with more than two services in .NET, Aspire should be your starting point.
Comments
Ajit Gangurde
Software Engineer II at Microsoft | 15+ years in .NET & Azure
Related Posts
Mar 14, 2026
Feb 28, 2026