
Aspire
- 19 installs
- 466 repo stars
- Updated July 25, 2026
- managedcode/dotnet-skills
Helps with ai & agent building tasks.
About
aspire is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted coding.
- aspire
- AI & Agent Building
- AI-coding skill
Aspire by the numbers
- 19 all-time installs (skills.sh)
- +1 installs in the week ending Aug 2, 2026 (Skillselion tracking)
- Ranked #10,571 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 3, 2026 (Skillselion catalog sync)
npx skills add https://github.com/managedcode/dotnet-skills --skill aspireAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 19 |
|---|---|
| repo stars | ★ 466 |
| Last updated | July 25, 2026 |
| Repository | managedcode/dotnet-skills ↗ |
What it does
Helps with ai & agent building tasks.
Files
.NET Aspire
Trigger On
Aspire.AppHost.Sdk,Aspire.Hosting.*,DistributedApplication.CreateBuilder,WithReference,WaitFor,AddProject,AddRedis,AddPostgres,aspire run,aspire init,aspire add, oraspire updateAspire.Hosting.Testing,DistributedApplicationTestingBuilder, or a test harness that mixes an Aspire AppHost withWebApplicationFactory- orchestrating multiple services and resources with an AppHost for local development or cloud deployment
- setting up
ServiceDefaults, service discovery, OpenTelemetry, health checks, or the Aspire Dashboard - choosing between official first-party Aspire integrations and
CommunityToolkit/Aspire - upgrading older 8.x or 9.x Aspire solutions to the current CLI and AppHost SDK model
- wiring polyglot services into an Aspire topology, especially when Go, Java, Python, or extra dev-time tools enter the picture
Workflow
1. Classify the task first: new AppHost creation, existing-solution enlistment, integration wiring, testing and observability, deployment, or version upgrade. 2. Prefer the current Aspire toolchain. For greenfield or modernized work, use the Aspire CLI and current AppHost SDK instead of writing new guidance around the deprecated legacy workload. 3. Treat 13.4.x releases as servicing and feature updates for the current CLI-first app model, not a topology rewrite. Keep the Aspire CLI, Aspire.AppHost.Sdk, and closely coupled hosting or testing packages on the same line, then rerun the AppHost and deployment checks after aspire update. 4. Keep the AppHost code-first and topology-focused. Model services, resources, dependencies, endpoints, lifetimes, and parameters there; keep business logic out. 5. Keep ServiceDefaults narrow. It exists for telemetry, health checks, resilience, and service discovery, not shared domain models or general utility code. 6. Prefer official first-party Aspire integrations when they cover the requirement. Use CommunityToolkit/Aspire only when the capability gap is real: unsupported language hosts, extra dev infrastructure, or extension packages the official project does not provide. 7. Validate the whole distributed system, not one project in isolation. Local success means the AppHost starts cleanly, dependencies resolve through WithReference, the dashboard shows the expected resource graph, and end-to-end tests can exercise the topology. 8. For integration tests, keep one shared AppHost fixture per test session. Use Aspire.Hosting.Testing to boot the distributed app, create HttpClient or SignalR clients from the AppHost, and layer WebApplicationFactory on top only when tests need direct Host DI, grains, or runtime services. 9. When publishing, switch from local containers or emulators to managed resources deliberately and verify which services truly need external endpoints.
Architecture
flowchart LR
A["Distributed-app task"] --> B{"Need code-first orchestration?"}
B -->|No| C["Stay in service-level skills such as ASP.NET Core, Worker, or Orleans"]
B -->|Yes| D["Create or update the AppHost"]
D --> E["Model resources and services with `WithReference` and `WaitFor`"]
E --> F{"Official Aspire integration exists?"}
F -->|Yes| G["Use first-party Aspire integration"]
F -->|No or gap remains| H["Evaluate `CommunityToolkit/Aspire`"]
G --> I["Apply `ServiceDefaults`, dashboard, and tests"]
H --> I
I --> J{"Publishing now?"}
J -->|No| K["Run locally with `aspire run` or the AppHost project"]
J -->|Yes| L["Choose `azd`, App Service, or the CLI deploy/publish pipeline"]Current Guidance
- AppHost shape: prefer current SDK-style AppHost projects using
Aspire.AppHost.Sdk/<version>or a file-based AppHost when that repo intentionally uses the single-file model. Recognize both as valid current patterns. - CLI entry points: use
aspire newfor starter projects,aspire initto add Aspire support to an existing solution or create a single-file AppHost,aspire addto add integrations or starter pieces,aspire runfor local orchestration,aspire start/aspire stop/aspire psfor detached lifecycle management,aspire describefor live resource inspection,aspire doctorfor environment diagnostics,aspire secretfor user secrets,aspire docsfor terminal documentation lookup,aspire agentfor AI agent integration,aspire deployfor the current CLI deploy pipeline,aspire restorefor AppHost and TypeScript resource refresh, andaspire updatefor version-aware upgrades.aspire publishstill exists for explicit artifact-generation flows and remains preview-sensitive. - Patch posture: Aspire
13.4.4is the current 13.4 servicing release. Treat it as a reliability and MCP-tooling refresh, not a new topology model; align package versions, rerunaspire update, then revalidate local orchestration and the chosen deployment path. - MCP and agent tooling:
ExcludeFromMcp()filtering is now consistently honored by CLI MCP tools such as resource, log, command, and trace listings. Use it deliberately for resources that should not leak into agent context. - DCP reliability: 13.4.4 retries DCP requests when the underlying connection drops mid-request. If AppHost resource commands or dashboard-backed CLI calls were flaky, upgrade before adding local retry wrappers.
- App model wiring: use
WithReference(...)for dependency and configuration flow, andWaitFor(...)for startup ordering. UseWithExternalHttpEndpoints()only when the resource truly needs an externally reachable endpoint for the chosen runtime or publish target. - ServiceDefaults boundaries:
AddServiceDefaults()should stay focused on OpenTelemetry, health endpoints, service discovery,HttpClientresilience, and related cross-cutting infrastructure. - Testing model: prefer Aspire closed-box testing when you need to run the distributed application as a system. Use
DistributedApplicationTestingBuilderplus a shared fixture for AppHost lifecycle,App.CreateHttpClient(...)for resource-bound clients, and aWebApplicationFactory<TEntryPoint>wrapper only when the test must resolve DI services or in-process runtime state from the hosted app. For UI flows, initialize Playwright once in the shared fixture, create a fresh browser context per test, and capture failure artifacts. - Dashboard usage: treat the Aspire Dashboard as the development observability surface. It is valuable in AppHost runs and standalone OTLP scenarios, but it is not a production monitoring replacement.
- Upgrade posture: older 8.x or 9.x solutions need explicit migration work. Current guidance favors the Aspire CLI upgrade path and the newer AppHost SDK structure on
.NET 10.
Selection Rules
- Use first-party Aspire when the package and docs exist for the resource or platform, especially for core .NET, Azure, cache, database, messaging, Microsoft Foundry, and standard local-container flows.
- Use
CommunityToolkit/Aspirewhen you need polyglot app hosts beyond official coverage, extra dev-time tools around a resource, or community-maintained integrations such as SQLite, Java, Go, PowerShell, k6, MailPit, MinIO, or Meilisearch. - Prefer the smallest surface that solves the problem. Do not add a broad toolkit extension pack when an existing first-party integration plus a normal library already fits.
- Treat toolkit packages as community-supported. Verify maturity, maintenance, external container images, and security or licensing assumptions before making them part of a production baseline.
Official Sources
- Aspire docs home
- What's new in Aspire 13.4
- AppHost
- Service defaults
- Integrations overview
- Build your first app
- Aspire CLI reference
- Aspire 13.4.4 release
- Testing overview
- microsoft/aspire
- CommunityToolkit/Aspire
Anti-Patterns
- hardcoding service URLs or connection strings instead of using
WithReference - putting business logic, data migrations, or large configuration transforms inside the AppHost
- turning
ServiceDefaultsinto a dumping ground for shared models or helpers - adding external HTTP endpoints everywhere instead of only where runtime or publish needs them
- defaulting to
CommunityToolkit/Aspirewhen first-party Aspire already covers the requirement - assuming the dashboard or local containers automatically mean production readiness
- treating Aspire tests as a mocking framework; they run the application as a real distributed system
Deliver
- a version-aware Aspire architecture or upgrade direction
- the right AppHost, ServiceDefaults, integration, and CLI workflow
- an explicit first-party versus
CommunityToolkit/Aspirepackage decision - an end-to-end validation path for local orchestration, testing, and deployment
Validate
- the AppHost starts cleanly via
aspire runor the AppHost project - resources and projects are modeled with explicit
WithReferenceandWaitForrelationships where needed - consuming apps resolve endpoints and connection strings without hardcoded values
ServiceDefaultscontains only cross-cutting infrastructure concerns- dashboard, health checks, logs, and traces reflect the expected resource graph
- Aspire-backed integration tests reuse a shared AppHost fixture instead of booting the distributed app inside each test
- any
WebApplicationFactorylayer reuses connection strings and endpoints from the AppHost instead of duplicating local config - testing and deployment guidance matches the chosen runtime: local AppHost, standalone dashboard, ACA/App Service, or the CLI deploy/publish pipeline
References
- patterns.md - Current CLI-first setup flows, AppHost patterns,
ServiceDefaults, testing, and upgrade checkpoints - testing.md - Shared AppHost fixtures,
DistributedApplicationTestingBuilder,WebApplicationFactoryintegration, Playwright bootstrapping, and diagnostics - deployment.md - ACA, App Service, publish-mode, and manifest-oriented deployment guidance
- community-toolkit.md - Practical guide to
CommunityToolkit/Aspirepackages, capability gaps, and selection rules
{
"version": "1.4.0",
"category": "Cloud",
"package_prefix": "Aspire"
}
CommunityToolkit/Aspire Reference
Use this reference when official first-party Aspire integrations do not cover the scenario or when the user explicitly asks about CommunityToolkit/Aspire.
Last verified against:
CommunityToolkit/Aspirereleasev13.4.0published on2026-06-02- the current repository README package index and Microsoft Learn Community Toolkit pages
Table of Contents
When to use the toolkit
Reach for CommunityToolkit/Aspire when you need one of these:
- polyglot host integrations beyond first-party Aspire coverage
- community-maintained resource integrations that official Aspire does not provide
- extra development-time utilities around existing resource types
- niche infrastructure or test-tool resources that should still live in the AppHost model
Do not reach for it simply because it exists. First-party Aspire remains the default for core official scenarios.
Selection rules
Prefer first-party Aspire when
- official Aspire already has the integration you need
- the scenario is a mainstream database, cache, Azure resource, messaging broker, or normal .NET service topology
- the repo benefits from staying as close as possible to Microsoft-maintained docs and examples
Prefer CommunityToolkit/Aspire when
- you need to host non-.NET apps such as Go, Java, PowerShell, Deno, Bun, or Rust in the AppHost
- you need SQLite-specific hosting or related EF and client wiring
- you want extra dev-time tools such as MailPit, ngrok, k6, McpInspector, Adminer, or DbGate in the topology
- you need community-maintained integrations such as Meilisearch, MinIO, RavenDB, SurrealDB, KurrentDB, LavinMQ, or Zitadel
- you need extension packages around existing first-party resources, such as Redis, PostgreSQL, SQL Server, MySQL, MongoDB, Keycloak, Elasticsearch, or OpenTelemetry Collector support
- you need newer 13.4 toolkit additions such as DuckDB hosting/client support, the bacon hosting integration, or analyzer support around Aspire integration usage
Package families
The toolkit surface is large. Keep the selection practical and grouped by problem type rather than trying to memorize every package name.
Polyglot and executable app hosting
Use these when the AppHost must orchestrate non-.NET executable projects:
CommunityToolkit.Aspire.Hosting.GolangCommunityToolkit.Aspire.Hosting.JavaCommunityToolkit.Aspire.Hosting.Python.ExtensionsCommunityToolkit.Aspire.Hosting.JavaScript.ExtensionsCommunityToolkit.Aspire.Hosting.PowerShellCommunityToolkit.Aspire.Hosting.DenoCommunityToolkit.Aspire.Hosting.BunCommunityToolkit.Aspire.Hosting.Rust
These are especially important because current AppHost guidance explicitly points to toolkit integrations for Go and Java, while JavaScript has first-party coverage and Python often routes through the toolkit extension path. In the 13.4 release train, prefer core Aspire.Hosting.JavaScript over the now-deprecated toolkit Bun hosting integration when first-party JavaScript hosting covers the scenario.
Databases, object stores, and search
Use these when the missing capability is a specific backing technology:
CommunityToolkit.Aspire.Hosting.SqliteCommunityToolkit.Aspire.Microsoft.Data.SqliteCommunityToolkit.Aspire.Microsoft.EntityFrameworkCore.SqliteCommunityToolkit.Aspire.Hosting.RavenDBCommunityToolkit.Aspire.RavenDB.ClientCommunityToolkit.Aspire.Hosting.MeilisearchCommunityToolkit.Aspire.MeilisearchCommunityToolkit.Aspire.Hosting.MinioCommunityToolkit.Aspire.Minio.ClientCommunityToolkit.Aspire.Hosting.KurrentDBCommunityToolkit.Aspire.KurrentDBCommunityToolkit.Aspire.Hosting.SurrealDbCommunityToolkit.Aspire.SurrealDbCommunityToolkit.Aspire.Hosting.SqlDatabaseProjectsCommunityToolkit.Aspire.Hosting.DuckDBCommunityToolkit.Aspire.DuckDB.Client
This family is often the fastest way to keep uncommon data dependencies inside the Aspire app model instead of documenting them as external setup steps.
Messaging, eventing, and feature flags
CommunityToolkit.Aspire.Hosting.ActiveMQCommunityToolkit.Aspire.Hosting.LavinMQCommunityToolkit.Aspire.MassTransit.RabbitMQCommunityToolkit.Aspire.Hosting.FlagdCommunityToolkit.Aspire.Hosting.GoFeatureFlagCommunityToolkit.Aspire.GoFeatureFlagCommunityToolkit.Aspire.Hosting.DaprCommunityToolkit.Aspire.Hosting.Azure.DaprCommunityToolkit.Aspire.Hosting.Azure.Dapr.Redis
Use this family when the distributed topology needs a real eventing, pub-sub, Dapr, or feature-flagged development story instead of a plain HTTP-only graph.
Platform extensions around existing resources
These extend or deepen the official resource story:
CommunityToolkit.Aspire.Hosting.PostgreSQL.ExtensionsCommunityToolkit.Aspire.Hosting.SqlServer.ExtensionsCommunityToolkit.Aspire.Hosting.Redis.ExtensionsCommunityToolkit.Aspire.Hosting.MySql.ExtensionsCommunityToolkit.Aspire.Hosting.MongoDB.ExtensionsCommunityToolkit.Aspire.Hosting.Elasticsearch.ExtensionsCommunityToolkit.Aspire.Hosting.Keycloak.ExtensionsCommunityToolkit.Aspire.Hosting.Azure.ExtensionsCommunityToolkit.Aspire.Hosting.Azure.DataApiBuilderCommunityToolkit.Aspire.Hosting.OpenTelemetryCollectorCommunityToolkit.Aspire.Hosting.Flyway
Choose these when first-party Aspire gets you most of the way there, but the real missing piece is a dev tool, extension, or advanced integration surface.
Dev-time utilities and diagnostics
CommunityToolkit.Aspire.Hosting.MailPitCommunityToolkit.Aspire.Hosting.PapercutSmtpCommunityToolkit.Aspire.Hosting.NgrokCommunityToolkit.Aspire.Hosting.McpInspectorCommunityToolkit.Aspire.Hosting.DbGateCommunityToolkit.Aspire.Hosting.AdminerCommunityToolkit.Aspire.Hosting.k6CommunityToolkit.Aspire.Hosting.Umami
Use these to keep development-only infrastructure visible in the AppHost instead of managing them out-of-band.
AI, file transfer, commerce, and identity
CommunityToolkit.Aspire.Hosting.OllamaCommunityToolkit.Aspire.OllamaSharpCommunityToolkit.Aspire.Hosting.SftpCommunityToolkit.Aspire.SftpCommunityToolkit.Aspire.Hosting.StripeCommunityToolkit.Aspire.Hosting.Zitadel
These are useful when a distributed application needs more than the typical database and cache story.
What not to do
- Do not add toolkit packages just because they look interesting. Tie each choice to a concrete gap.
- Do not present community-maintained integrations as if they were automatically equivalent to first-party Aspire support.
- Do not assume every package family is equally mature. Verify package recency, README quality, and real maintenance activity.
- Do not let a dev-only tool become a hidden production dependency.
.NET Aspire Deployment Reference
Use this reference when the question is specifically about deployment shape, publish mode, Azure Container Apps, App Service, or manifest-oriented Aspire workflows.
Current docs home and CLI reference now live on aspire.dev. The homepage and CLI docs emphasize aspire deploy as the current deployment command, while aspire publish remains a preview-oriented artifact-generation path.
Table of Contents
- Deployment defaults
- Azure Container Apps with `azd`
- App Service-specific guidance
- Publish-mode resource switching
- CLI deploy and preview publish flows
- Operational checks
Deployment defaults
Current official guidance strongly favors Azure Developer CLI for the normal Azure path.
Use this ordering by default:
1. Local development and debugging with aspire run 2. Azure Container Apps deployment with azd 3. App Service only when the hosting constraints or team standards point there 4. The CLI deploy or publish pipeline only when you explicitly need Aspire-managed deployment steps or artifact generation outside the normal azd flow
Avoid inventing a custom deployment path before checking whether azd already covers the scenario.
Azure Container Apps with azd
Recommended path
azd init
azd upWhy this path is the default:
azd initdetects the AppHost and generates the right Azure-facing scaffoldingazd upprovisions and deploys from the distributed application model- the workflow aligns with the official Aspire deployment tutorials
What azd init is doing
In an Aspire solution, azd init typically:
1. scans the current directory for the AppHost 2. confirms the detected distributed application 3. generates Azure deployment files such as azure.yaml 4. creates environment-specific configuration under .azure/
Post-deploy dashboard access
Use:
azd monitorThis is the preferred operational follow-up when the team wants the deployed Aspire Dashboard experience instead of hunting for URLs manually.
App Service-specific guidance
App Service is valid, but it is not identical to ACA in how service-to-service traffic works.
Add the integration
The current quickstart uses the Aspire App Service hosting integration:
aspire add azure-appserviceThen model the App Service environment in the AppHost:
var builder = DistributedApplication.CreateBuilder(args);
builder.AddAzureAppServiceEnvironment("app-service-env");
var api = builder.AddProject<Projects.MyShop_Api>("api")
.WithExternalHttpEndpoints()
.WithHttpHealthCheck("/health");Key point:
- App Service currently needs externally reachable HTTP endpoints for service-to-service communication in this hosting model
Do not copy a Container Apps mental model here and assume internal-only AppHost endpoints will just work.
Publish-mode resource switching
Use publish-mode branching to move from local development resources to managed Azure services cleanly:
var builder = DistributedApplication.CreateBuilder(args);
var database = builder.ExecutionContext.IsPublishMode
? builder.AddAzurePostgresFlexibleServer("db").AddDatabase("catalog")
: builder.AddPostgres("db").AddDatabase("catalog");
var cache = builder.ExecutionContext.IsPublishMode
? builder.AddAzureRedis("cache")
: builder.AddRedis("cache");
builder.AddProject<Projects.MyShop_Api>("api")
.WithReference(database)
.WithReference(cache)
.WaitFor(database)
.WaitFor(cache);This is the normal way to express:
- local containers or emulators for dev
- managed Azure services for publish mode
Do not keep separate hand-maintained topologies unless there is a real deployment-system constraint.
CLI deploy and preview publish flows
The current Aspire CLI exposes two closely related preview-sensitive paths:
aspire deployfor running the deploy pipeline registered in the app modelaspire publishfor explicit publish and artifact-generation scenarios
Use aspire deploy when the task explicitly calls for:
- driving the app model's deploy pipeline directly from the CLI
- generating deploy output while letting the AppHost select and run dependent pipeline steps
- validating a repo-specific Aspire deployment story outside
azd
Use aspire publish when the task explicitly calls for:
- deployment artifact generation
- manifest-oriented handoff to another toolchain
- inspection of the publish output instead of a direct
azddeployment
Treat both flows as version-sensitive and preview-sensitive. Verify current docs and command behavior before encoding them into long-lived repo automation.
Operational checks
Before calling a deployment story "done", verify:
1. The local AppHost still runs after any publish-mode branching changes. 2. Resources that must be external are explicitly external, and resources that should remain internal are not accidentally exposed. 3. The dashboard, traces, and health endpoints reflect the expected topology after deployment. 4. Environment-specific parameters and secrets are injected through the AppHost model or deployment tooling rather than copy-pasted config files. 5. The team can explain why the target is ACA, App Service, aspire deploy, or aspire publish.
.NET Aspire Patterns Reference
Use this reference when the task is clearly about current Aspire application-host design, CLI-first workflows, ServiceDefaults, testing, or upgrade checkpoints.
Table of Contents
- CLI-first setup flows
- AppHost shapes
- Current AppHost modeling patterns
- Dependency and configuration flow
- ServiceDefaults boundaries
- Servicing patch posture
- Closed-box testing
- Upgrade checkpoints
- Anti-patterns
CLI-first setup flows
Create a new starter application
Use the current Aspire CLI when the user wants a fresh distributed-app baseline:
aspire new aspire-starter --name MyShop
cd MyShop
aspire runThis gives you the modern baseline:
- an AppHost for orchestration
- a ServiceDefaults project for cross-cutting infrastructure
- sample service projects wired into the AppHost
- the Aspire Dashboard for local observability
Enlist an existing solution
When the repo already has working services and you want to add orchestration instead of recreating the solution:
aspire initUse aspire init when you need one of these:
- an AppHost added to an existing solution
- a file-based AppHost created quickly
- Aspire support layered onto code that already exists
Add capabilities with the CLI
Use the CLI to add official integrations or starter assets instead of hand-editing packages when the command exists:
aspire add <integration-or-starter>Use aspire add when it improves repeatability, especially for:
- common first-party integrations
- starter resources that should match current Aspire conventions
- reducing hand-written AppHost or project-file drift
Servicing patch posture
Aspire 13.4.4 is the current servicing release in the 13.4 line, not a new application model. Treat 13.4.x updates as CLI, AppHost, DCP, and MCP-tooling servicing work that should preserve the existing topology and only refine the toolchain surface.
When you roll a 13.4.x patch:
1. Keep the Aspire CLI and Aspire.AppHost.Sdk on the same patch line. 2. Update adjacent Aspire packages that move with the AppHost, especially hosting and testing packages. 3. Run aspire update before hand-editing package versions unless the repo intentionally pins them. 4. Revalidate the AppHost start path, resource graph, dashboard, and any deployment scripts after the patch lands. 5. Re-check current CLI and MCP-facing commands, especially aspire start/aspire stop/aspire ps, aspire describe, aspire docs, aspire agent, aspire restore, and resource/log/trace MCP tools. 6. For agent-integrated AppHosts, verify resources marked with ExcludeFromMcp() do not appear in MCP resource, log, command, trace, or structured-trace outputs.
Do not re-architect the AppHost just because a servicing release shipped.
AppHost shapes
Current Aspire supports two valid AppHost styles.
Project-based AppHost
Use this when the repo already uses the normal solution and project structure:
<Project Sdk="Aspire.AppHost.Sdk/<version>">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\MyShop.Api\MyShop.Api.csproj" />
<ProjectReference Include="..\MyShop.Web\MyShop.Web.csproj" />
</ItemGroup>
</Project>Prefer the SDK-style AppHost in current projects. Do not create new 13-era examples that manually model the older AppHost package layout as if it were the default.
File-based AppHost
Use this when a lightweight single-file orchestration layer is the better fit:
var builder = DistributedApplication.CreateBuilder(args);
var postgres = builder.AddPostgres("db")
.AddDatabase("appdata");
builder.AddProject<Projects.MyShop_Api>("api")
.WithReference(postgres)
.WaitFor(postgres);
builder.Build().Run();File-based AppHosts are useful for experimentation, smaller repos, and incremental adoption. They do not automatically include a ServiceDefaults project, so create one when the services need it.
Current AppHost modeling patterns
Minimal multi-service topology
var builder = DistributedApplication.CreateBuilder(args);
var postgres = builder.AddPostgres("postgres")
.AddDatabase("catalog")
.WithDataVolume();
var cache = builder.AddRedis("cache");
var api = builder.AddProject<Projects.MyShop_Api>("api")
.WithReference(postgres)
.WithReference(cache)
.WaitFor(postgres)
.WaitFor(cache);
builder.AddProject<Projects.MyShop_Web>("web")
.WithReference(api);
builder.Build().Run();What matters:
- infrastructure resources are modeled explicitly
- consuming services get their config through
WithReference(...) - startup ordering is intentional through
WaitFor(...) - the AppHost stays at topology level
Persistent local resources
If the slow path is repeated container bootstrap rather than code changes, keep state across local AppHost restarts:
var postgres = builder.AddPostgres("postgres")
.WithDataVolume()
.WithLifetime(ContainerLifetime.Persistent)
.AddDatabase("catalog");Use persistence deliberately. It is helpful for realistic local development, but it can hide initialization bugs if the team forgets the difference between a cold start and a reused container.
Publish-mode resource switching
Use publish-mode branching when local development should use containers or emulators while published environments should use managed Azure resources:
var builder = DistributedApplication.CreateBuilder(args);
var cache = builder.ExecutionContext.IsPublishMode
? builder.AddAzureRedis("cache")
: builder.AddRedis("cache").WithDataVolume();
var database = builder.ExecutionContext.IsPublishMode
? builder.AddAzurePostgresFlexibleServer("db").AddDatabase("catalog")
: builder.AddPostgres("db").AddDatabase("catalog");
builder.AddProject<Projects.MyShop_Api>("api")
.WithReference(cache)
.WithReference(database)
.WaitFor(cache)
.WaitFor(database);This pattern is better than trying to maintain separate hand-written local and cloud topologies.
Dependency and configuration flow
Official Aspire guidance treats integrations as two related but independent layers:
- hosting integrations extend
IDistributedApplicationBuilderand model resources in the AppHost - client integrations wire client libraries into DI, health checks, resiliency, and telemetry
Use that split intentionally.
WithReference is the default wiring mechanism
WithReference(...) is the normal way to pass endpoints, connection strings, credentials, or other configuration between resources.
Use it instead of:
- hardcoded URLs
- copy-pasted connection strings
- manually synchronized environment variables
WaitFor is for startup order, not configuration
WaitFor(...) solves a different problem than WithReference(...).
WithReference(...)injects configuration and expresses the dependency edgeWaitFor(...)delays startup until the dependency is ready or healthy
Use both when the consuming service should not even begin until the dependency is available.
Named endpoints
Use named endpoints when a service exposes more than one surface:
var api = builder.AddProject<Projects.MyShop_Api>("api")
.WithHttpEndpoint(port: 5001, name: "public")
.WithHttpEndpoint(port: 5002, name: "internal");Named endpoints are useful for:
- separating public versus internal traffic
- gRPC and HTTP on the same service
- routing test-only or admin endpoints distinctly
ServiceDefaults boundaries
The current ServiceDefaults template exists to centralize cross-cutting infrastructure, not shared business code.
Keep it focused
Good content for ServiceDefaults:
- OpenTelemetry logging, metrics, and tracing
- health checks
- service discovery
HttpClientresilience defaults- default endpoint mapping for health endpoints
Bad content for ServiceDefaults:
- domain models
- repository implementations
- DTOs
- application-specific utilities unrelated to cross-cutting infrastructure
Typical structure
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.AddStandardResilienceHandler();
http.AddServiceDiscovery();
});
return builder;
}
}Current official guidance also keeps health endpoints and tracing filters aligned with these defaults. Do not fork this pattern casually unless the repo truly needs a custom shared-hosting baseline.
Closed-box testing
Use Aspire testing when the requirement is to exercise the distributed system as a system instead of unit-testing a single component.
Prefer Aspire testing for:
- end-to-end flows across multiple services
- verifying resource startup and wiring
- validating service discovery and real HTTP or messaging paths
- regression tests around the AppHost topology
Do not reach for Aspire testing when:
- a plain xUnit or NUnit test against one class is enough
- the service logic can be verified entirely in-process
- the AppHost topology is irrelevant to the assertion
Testing is especially important for:
- ensuring
WithReference(...)andWaitFor(...)match the intended runtime graph - catching broken resource names or renamed endpoints
- proving a version upgrade did not silently break orchestration
Load testing.md when the repo mixes AppHost lifecycle, DistributedApplicationTestingBuilder, WebApplicationFactory, SignalR, or Playwright instead of using Aspire as a pure black-box API harness.
Upgrade checkpoints
When modernizing older Aspire solutions, verify these points explicitly:
1. The team is using the current Aspire CLI, and the upgrade path starts there. 2. The AppHost project uses the current SDK-style layout rather than retaining older manual AppHost package wiring by inertia. 3. The AppHost target framework is aligned with current tooling expectations for Aspire 13-era projects. 4. The repo has removed assumptions about the old workload-based setup when those assumptions no longer apply. 5. ServiceDefaults remains a narrow infrastructure project instead of having accumulated random shared code over time.
Use:
aspire updatePair the CLI upgrade with a review of:
- AppHost project structure
- project references
- integration package versions
- test coverage for the distributed topology
- local run and dashboard behavior
Anti-Patterns
- Treating the AppHost like an ordinary application project with business logic, service implementations, or repo-specific glue.
- Using
WithEnvironment(...)as the first answer whenWithReference(...)or a normal integration already models the dependency correctly. - Assuming
WithExternalHttpEndpoints()belongs on every web project. It should follow runtime needs, especially publish targets such as App Service. - Modeling a large topology with vague resource names like
service1anddb2, then expecting logs and traces to remain understandable. - Carrying an obsolete 8.x or 9.x setup pattern into new samples or new repos without a compatibility reason.
.NET Aspire Testing Patterns
Use this reference when the task is about AppHost-backed integration tests, DistributedApplicationTestingBuilder, mixing Aspire with WebApplicationFactory, Playwright UI automation, or practical diagnostics.
These patterns are grounded in working production-style test harnesses used in AIBase and WA.Storied.Agents: one shared AppHost fixture per test session, optional WebApplicationFactory layering for Host DI/grains, and browser contexts created per test rather than per session.
Fixture Selection
flowchart LR
A["Test needs Aspire resources"] --> B{"Needs only external surfaces?"}
B -->|Yes| C["Shared AppHost fixture"]
C --> D["Create HttpClient / HubConnection from DistributedApplication"]
B -->|No, needs Host DI/grains| E["Shared AppHost fixture + WebApplicationFactory"]
E --> F["Resolve connection strings from AppHost into test host config"]
C --> G{"Needs UI automation?"}
G -->|Yes| H["Initialize Playwright once, create BrowserContext per test"]
G -->|No| I["Stay API/SignalR only"]Package Baseline
Typical Aspire-backed integration suites need:
<ItemGroup>
<PackageReference Include="Aspire.Hosting.Testing" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" />
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" />
<PackageReference Include="Microsoft.Playwright" />
</ItemGroup>Add the actual test framework package separately: TUnit, xunit, or NUnit.
Shared AppHost Fixture
Boot the AppHost once, wait for the real resources, and hand out clients from the distributed app:
using Aspire.Hosting.Testing;
public sealed class AspireTestFixture : IAsyncDisposable
{
private DistributedApplication? _app;
public DistributedApplication App =>
_app ?? throw new InvalidOperationException("App not initialized.");
public string ApiUrl { get; private set; } = string.Empty;
public async Task InitializeAsync()
{
var builder = await DistributedApplicationTestingBuilder.CreateAsync<Projects.My_AppHost>();
builder.Services.AddLogging(logging =>
{
logging.ClearProviders();
logging.AddConsole();
logging.SetMinimumLevel(LogLevel.Warning);
logging.AddFilter("Aspire.Hosting.Dcp", LogLevel.Warning);
logging.AddFilter("Aspire.Hosting.Backchannel", LogLevel.Critical);
});
_app = await builder.BuildAsync();
await _app.StartAsync();
using var cts = new CancellationTokenSource(TimeSpan.FromMinutes(1));
await _app.ResourceNotifications.WaitForResourceHealthyAsync("api", cts.Token);
ApiUrl = _app.CreateHttpClient("api", "http").BaseAddress?.ToString()
?? throw new InvalidOperationException("API URL was not resolved.");
}
public HttpClient CreateApiClient() => App.CreateHttpClient("api", "http");
public async Task DisposeAsync()
{
if (_app is not null)
{
await _app.StopAsync();
await _app.DisposeAsync();
}
}
}Use this shape when the test only needs the real distributed topology: API, SignalR, SSE, workers, or resource health.
Layer WebApplicationFactory On Top Of Aspire Infra
When the test needs Host DI services, direct IGrainFactory access, or in-process runtime services, reuse the AppHost infrastructure and inject its connection strings into the web host:
public sealed class TestApplication
: WebApplicationFactory<HostEntryPointMarker>, IAsyncDisposable
{
private static readonly AspireTestFixture SharedFixture = new();
private readonly Dictionary<string, string?> _overrides = new(StringComparer.OrdinalIgnoreCase);
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.UseEnvironment(Environments.Development);
builder.ConfigureAppConfiguration((_, config) =>
{
config.AddInMemoryCollection(new Dictionary<string, string?>
{
["Logging:LogLevel:Default"] = "Warning",
["Logging:LogLevel:Orleans"] = "Warning",
["Logging:LogLevel:Aspire"] = "Warning",
});
config.AddInMemoryCollection(_overrides);
});
}
public async Task InitializeAsync()
{
await SharedFixture.InitializeAsync();
var tables = await SharedFixture.App.GetConnectionStringAsync("tables");
var blobs = await SharedFixture.App.GetConnectionStringAsync("blobs");
_overrides["ConnectionStrings:Tables"] = tables;
_overrides["ConnectionStrings:Blobs"] = blobs;
Environment.SetEnvironmentVariable("ConnectionStrings__Tables", tables);
Environment.SetEnvironmentVariable("ConnectionStrings__Blobs", blobs);
CreateClient();
}
public new HttpClient CreateClient()
{
var client = base.CreateClient();
client.Timeout = TimeSpan.FromMinutes(5);
return client;
}
public async Task DisposeAsync()
{
Dispose();
await SharedFixture.DisposeAsync();
}
}Rules that matter:
- boot Aspire once, not per test
- resolve connection strings and endpoints from
SharedFixture.App, not from copied local config - keep
WebApplicationFactoryfocused on DI/runtime access, not infrastructure provisioning - adapt the lifecycle surface to the active test framework: xUnit
IAsyncLifetime, TUnitIAsyncInitializer, or the repo's own fixture abstraction
Playwright In The Shared Fixture
Initialize the browser once, then create a fresh browser context per test:
public async Task InitializePlaywrightAsync()
{
await InitializeAsync();
var exitCode = Microsoft.Playwright.Program.Main(["install", "chromium"]);
if (exitCode != 0)
{
throw new InvalidOperationException($"Playwright install failed: {exitCode}");
}
Playwright ??= await Microsoft.Playwright.Playwright.CreateAsync();
Browser ??= await Playwright.Chromium.LaunchAsync(new BrowserTypeLaunchOptions
{
Headless = true,
Args =
[
"--disable-dev-shm-usage",
"--disable-gpu",
"--window-size=1920,1080",
],
});
}
public async Task<IBrowserContext> CreateBrowserContextAsync()
{
await InitializePlaywrightAsync();
return await Browser!.NewContextAsync(new BrowserNewContextOptions
{
BaseURL = ApiUrl,
IgnoreHTTPSErrors = true,
ViewportSize = new ViewportSize { Width = 1920, Height = 1080 },
ScreenSize = new ScreenSize { Width = 1920, Height = 1080 },
});
}Do not share pages or browser contexts across tests. Reuse the browser process, not mutable browser state.
Diagnostics And Failure Capture
Useful low-noise defaults:
logging.ClearProviders(); logging.AddConsole();logging.SetMinimumLevel(LogLevel.Warning);logging.AddFilter("Aspire.Hosting.Dcp", LogLevel.Warning);logging.AddFilter("Aspire.Hosting.Backchannel", LogLevel.Critical);
For failures:
- dump server-side error logs from the shared fixture or
WebApplicationFactorylogger provider - capture Playwright screenshots and HTML into an
artifacts/folder - include resource logs from the AppHost when a resource fails to become healthy
Example pattern:
try
{
var response = await client.GetAsync("/health");
response.EnsureSuccessStatusCode();
}
catch
{
Console.WriteLine(testApplication.GetHostLogDump(LogLevel.Error));
throw;
}Practical Rules
- Use plain service-level tests when the AppHost topology does not matter.
- Use Aspire testing when the assertion depends on real resource wiring, service discovery, health, or cross-service flows.
- Mix Aspire with
WebApplicationFactoryonly when the test needs direct access to DI, grains, or in-process runtime services. - Keep one shared AppHost fixture per test session and one browser context per UI test.
- Prefer explicit resource names and explicit
WaitForResourceHealthyAsync(...)checks so failures point to the right resource quickly.