.NET Aspire 10, released alongside .NET 10, represents Microsoft’s answer to the complexity of cloud-native development. It provides an opinionated, orchestrated approach to building distributed applications with built-in service discovery, health checks, telemetry, and deployment automation. This comprehensive guide explores Aspire’s architecture, the developer experience improvements in version 10, and patterns for deploying Aspire applications to Azure Container Apps and Kubernetes.
What is .NET Aspire?
.NET Aspire is a cloud-ready stack for building observable, production-ready distributed applications. It addresses the pain points of microservices development:
- Service Discovery: Automatic endpoint resolution between services
- Configuration: Centralized connection strings and settings management
- Health Checks: Built-in liveness and readiness probes
- Telemetry: Unified logging, metrics, and distributed tracing
- Local Development: Orchestrated containers with a visual dashboard
- Deployment: One-click deployment to Azure or Kubernetes
.NET Aspire 10 Architecture
graph TB
subgraph DevMachine ["Developer Machine"]
AppHost["AppHost Project"]
Dashboard["Aspire Dashboard"]
Docker["Docker Containers"]
end
subgraph AspireApp ["Aspire Application"]
API["API Service"]
Worker["Background Worker"]
Web["Web Frontend"]
end
subgraph Infrastructure ["Infrastructure Resources"]
Redis["Redis Cache"]
Postgres["PostgreSQL"]
RabbitMQ["RabbitMQ"]
Blob["Azure Blob Storage"]
end
subgraph Azure ["Azure Deployment"]
ACA["Container Apps"]
ACAE["Container Apps Environment"]
Monitor["Azure Monitor"]
end
AppHost --> Dashboard
AppHost --> Docker
Docker --> API
Docker --> Worker
Docker --> Web
Docker --> Redis
Docker --> Postgres
Docker --> RabbitMQ
API --> Redis
API --> Postgres
Worker --> RabbitMQ
Worker --> Blob
Web --> API
AppHost -.-> ACA
ACA --> ACAE
ACAE --> Monitor
style AppHost fill:#E8F5E9,stroke:#2E7D32
style Dashboard fill:#E3F2FD,stroke:#1565C0
style ACA fill:#FFF3E0,stroke:#EF6C00
What’s New in .NET Aspire 10
| Feature | Aspire 9 | Aspire 10 |
|---|---|---|
| Dashboard | Basic resource view | Enhanced metrics, log streaming, dependency graph |
| Kubernetes | Manual manifest generation | Native Helm chart generation |
| AI Integration | Basic OpenAI component | Aspire.AI with embeddings, agents, RAG |
| Testing | DistributedApplicationTestingBuilder | Improved test isolation, parallel execution |
| Components | 35 integrations | 50+ integrations including Dapr, Temporal |
| Hot Reload | Partial support | Full hot reload for all project types |
Getting Started with Aspire 10
Creating an Aspire Application
# Install/update the Aspire workload
dotnet workload update
dotnet workload install aspire
# Create a new Aspire application
dotnet new aspire-starter -n MyCloudApp
cd MyCloudApp
# Solution structure:
# MyCloudApp/
# ├── MyCloudApp.AppHost/ # Orchestration project
# ├── MyCloudApp.ServiceDefaults/ # Shared configuration
# ├── MyCloudApp.ApiService/ # Backend API
# └── MyCloudApp.Web/ # Blazor frontend
# Run the application
dotnet run --project MyCloudApp.AppHost
Understanding the AppHost
The AppHost project is the heart of Aspire—it defines your distributed application’s topology:
// Program.cs in MyCloudApp.AppHost
var builder = DistributedApplication.CreateBuilder(args);
// Infrastructure resources
var cache = builder.AddRedis("cache")
.WithDataVolume() // Persist data across restarts
.WithRedisInsight(); // Add Redis Insight for debugging
var postgres = builder.AddPostgres("postgres")
.WithDataVolume()
.WithPgAdmin() // Add pgAdmin UI
.AddDatabase("ordersdb");
var rabbitmq = builder.AddRabbitMQ("messaging")
.WithManagementPlugin(); // Enable management UI
var storage = builder.AddAzureStorage("storage")
.RunAsEmulator() // Use Azurite locally
.AddBlobs("blobs");
// Application services
var api = builder.AddProject<Projects.MyCloudApp_ApiService>("api")
.WithReference(cache)
.WithReference(postgres)
.WithExternalHttpEndpoints(); // Expose to internet
var worker = builder.AddProject<Projects.MyCloudApp_Worker>("worker")
.WithReference(rabbitmq)
.WithReference(storage)
.WithReplicas(3); // Run 3 instances
builder.AddProject<Projects.MyCloudApp_Web>("web")
.WithReference(api)
.WithExternalHttpEndpoints();
builder.Build().Run();
Service Defaults: Opinionated Best Practices
The ServiceDefaults project provides pre-configured settings for resilience, telemetry, and health checks:
// Extensions.cs in ServiceDefaults
public static IHostApplicationBuilder AddServiceDefaults(
this IHostApplicationBuilder builder)
{
// OpenTelemetry for distributed tracing
builder.ConfigureOpenTelemetry();
// Health checks
builder.AddDefaultHealthChecks();
// Service discovery
builder.Services.AddServiceDiscovery();
// Resilient HTTP clients with Polly
builder.Services.ConfigureHttpClientDefaults(http =>
{
http.AddStandardResilienceHandler(); // Retry, circuit breaker, timeout
http.AddServiceDiscovery();
});
return builder;
}
// In your API service's Program.cs
var builder = WebApplication.CreateBuilder(args);
builder.AddServiceDefaults(); // One line adds everything!
builder.AddRedisClient("cache");
builder.AddNpgsqlDbContext<OrdersDbContext>("ordersdb");
var app = builder.Build();
app.MapDefaultEndpoints(); // Health check endpoints
app.MapControllers();
app.Run();
AddStandardResilienceHandler() automatically configures retry policies (exponential backoff), circuit breakers, and timeouts for all HTTP clients. No manual Polly configuration needed!
Aspire Dashboard: Visual Development Experience
The Aspire Dashboard (accessible at https://localhost:17888) provides real-time visibility into your distributed application:
- Resources: View all services, containers, and their status
- Console Logs: Aggregated, color-coded logs from all services
- Structured Logs: Searchable log entries with structured data
- Traces: Distributed traces showing request flow across services
- Metrics: Real-time CPU, memory, and custom metrics
- Dependency Graph: Visual representation of service dependencies (new in Aspire 10)
Aspire 10 AI Integration
Aspire 10 introduces first-class AI support through the Aspire.AI package:
// In AppHost
var openai = builder.AddAzureOpenAI("openai")
.AddDeployment(new AzureOpenAIDeployment("gpt-5", "gpt-5", "2026-01-01"))
.AddDeployment(new AzureOpenAIDeployment("embeddings", "text-embedding-3-large", "2024-01-01"));
var vectorDb = builder.AddQdrant("vectors")
.WithDataVolume();
builder.AddProject<Projects.MyCloudApp_AiService>("ai")
.WithReference(openai)
.WithReference(vectorDb);
// In your AI service
public class RagService
{
private readonly ChatClient _chat;
private readonly EmbeddingClient _embeddings;
private readonly QdrantClient _vectors;
public RagService(
[FromKeyedServices("gpt-5")] ChatClient chat,
[FromKeyedServices("embeddings")] EmbeddingClient embeddings,
QdrantClient vectors)
{
_chat = chat;
_embeddings = embeddings;
_vectors = vectors;
}
public async Task<string> QueryAsync(string question)
{
// Generate embedding for the question
var embedding = await _embeddings.GenerateEmbeddingAsync(question);
// Search vector database
var results = await _vectors.SearchAsync("documents", embedding.Vector, limit: 5);
// Generate response with context
var context = string.Join("
", results.Select(r => r.Payload["content"]));
var response = await _chat.CompleteChatAsync($"""
Context: {context}
Question: {question}
Answer based on the context above:
""");
return response.Value.Content[0].Text;
}
}
Testing Aspire Applications
public class IntegrationTests : IAsyncLifetime
{
private DistributedApplication _app = null!;
private ResourceNotificationService _notifications = null!;
public async Task InitializeAsync()
{
var appHost = await DistributedApplicationTestingBuilder
.CreateAsync<Projects.MyCloudApp_AppHost>();
// Override resources for testing
appHost.Services.ConfigureHttpClientDefaults(http =>
{
http.AddStandardResilienceHandler(options =>
{
options.TotalRequestTimeout.Timeout = TimeSpan.FromSeconds(30);
});
});
_app = await appHost.BuildAsync();
_notifications = _app.Services.GetRequiredService<ResourceNotificationService>();
await _app.StartAsync();
// Wait for all resources to be ready
await _notifications.WaitForResourceHealthyAsync("api");
await _notifications.WaitForResourceHealthyAsync("postgres");
}
[Fact]
public async Task Api_CreateOrder_ReturnsCreated()
{
// Get the API endpoint
var apiUrl = _app.GetEndpoint("api");
using var client = new HttpClient { BaseAddress = new Uri(apiUrl) };
var order = new { CustomerId = "123", Items = new[] { new { ProductId = "ABC", Quantity = 2 } } };
var response = await client.PostAsJsonAsync("/orders", order);
Assert.Equal(HttpStatusCode.Created, response.StatusCode);
}
public async Task DisposeAsync()
{
await _app.DisposeAsync();
}
}
Deploying to Azure Container Apps
Aspire 10 provides seamless deployment to Azure Container Apps with a single command:
# Login to Azure
az login
azd auth login
# Initialize Azure Developer CLI
azd init
# Deploy to Azure Container Apps
azd up
# What happens behind the scenes:
# 1. Builds container images for all projects
# 2. Pushes to Azure Container Registry
# 3. Creates Container Apps Environment
# 4. Deploys all services with proper configuration
# 5. Sets up managed identities and connections
# 6. Configures Azure Monitor for observability
Generated Bicep/ARM
The azd infra synth command generates Bicep templates you can customize:
// Generated infra/main.bicep
resource containerAppsEnvironment 'Microsoft.App/managedEnvironments@2024-03-01' = {
name: 'cae-${resourceToken}'
location: location
properties: {
appLogsConfiguration: {
destination: 'azure-monitor'
}
workloadProfiles: [
{ name: 'Consumption', workloadProfileType: 'Consumption' }
]
}
}
resource apiContainerApp 'Microsoft.App/containerApps@2024-03-01' = {
name: 'api'
location: location
properties: {
environmentId: containerAppsEnvironment.id
configuration: {
ingress: {
external: true
targetPort: 8080
}
secrets: [
{ name: 'connection-string', value: postgresConnectionString }
]
}
template: {
containers: [
{
name: 'api'
image: '${containerRegistry.properties.loginServer}/api:${imageTag}'
resources: { cpu: json('0.5'), memory: '1Gi' }
}
]
scale: {
minReplicas: 1
maxReplicas: 10
rules: [
{ name: 'http', http: { metadata: { concurrentRequests: '100' } } }
]
}
}
}
}
Deploying to Kubernetes with Helm
Aspire 10 introduces native Helm chart generation:
# Generate Helm charts
dotnet run --project MyCloudApp.AppHost -- --publisher helm --output-path ./charts
# Deploy to Kubernetes
helm install myapp ./charts/myapp --namespace myapp --create-namespace --set image.tag=v1.0.0 --set postgresql.auth.password=$PG_PASSWORD
Helm charts include Kubernetes-native service discovery (DNS), ConfigMaps for configuration, and Secrets for sensitive data. The Aspire service discovery seamlessly switches to Kubernetes DNS in production.
Available Aspire Components
| Category | Components |
|---|---|
| Databases | PostgreSQL, SQL Server, MySQL, MongoDB, CosmosDB, Oracle |
| Caching | Redis, Valkey, Garnet, Memcached |
| Messaging | RabbitMQ, Kafka, Azure Service Bus, AWS SQS |
| Storage | Azure Blob, AWS S3, MinIO |
| Search | Elasticsearch, Azure AI Search, Meilisearch |
| AI/ML | Azure OpenAI, Ollama, Qdrant, Milvus |
| Orchestration | Dapr, Temporal, Conductor |
| Observability | Seq, Grafana, Jaeger, Zipkin |
Key Takeaways
- .NET Aspire 10 provides an opinionated framework for building cloud-native distributed applications with built-in observability and resilience.
- The AppHost project defines your entire distributed system topology, including services, databases, and infrastructure.
- ServiceDefaults provides one-line configuration for health checks, telemetry, service discovery, and resilient HTTP clients.
- The Aspire Dashboard offers real-time visibility into logs, traces, metrics, and dependencies during development.
- Deployment to Azure Container Apps or Kubernetes is automated with
azd upor Helm chart generation.
Conclusion
.NET Aspire 10 significantly reduces the complexity of building and deploying distributed applications. By providing opinionated defaults for the hard parts—service discovery, health checks, observability, and deployment—it lets developers focus on business logic rather than infrastructure plumbing. For teams building microservices on .NET, Aspire is now the recommended starting point, offering a smooth path from local development to production in Azure or Kubernetes.
References
- .NET Aspire Documentation
- Announcing .NET Aspire 10
- Azure Container Apps Documentation
- .NET Aspire GitHub Repository
Discover more from C4: Container, Code, Cloud & Context
Subscribe to get the latest posts sent to your email.