.NET Aspire basics with Azure deployment

Introduction
A real app is rarely one project - an API, a background worker, a database, maybe a cache, all needing to run together locally and then land somewhere in the cloud with sane wiring between them. .NET Aspire is Microsoft's answer to the "how do I even run all this together" problem: an orchestration layer for local development that doubles as a deployment model once you're ready for Azure. This video covers both halves.
🎬 Watch the full video here:
The AppHost project is the orchestrator
An Aspire solution has a dedicated AppHost project whose only job is describing what runs and how it connects - it isn't part of the app's runtime, it's the thing that starts everything else:
var builder = DistributedApplication.CreateBuilder(args);
var sql = builder.AddSqlServer("sql").AddDatabase("appdb");
var api = builder.AddProject<Projects.Api>("api").WithReference(sql);
var web = builder.AddProject<Projects.Web>("web").WithReference(api);
builder.Build().Run();
WithReference is the key piece - it injects the referenced resource's connection info into the dependent project's configuration automatically, so api gets a working ConnectionStrings:appdb without you hand-copying a connection string between appsettings.json files.
One dashboard, every service
Running the AppHost launches every referenced project (and spins up containers for things like SQL Server or Redis) together, plus Aspire's own dashboard - a single page showing live logs, traces, and metrics across every project at once. Debugging a request that touches three services no longer means three separate console windows.
Service discovery replaces hardcoded URLs
Instead of the web app's config hardcoding https://localhost:5001 for the API, Aspire's service discovery resolves it by the logical name given in the AppHost ("api"):
builder.Services.AddHttpClient<ApiClient>(client =>
{
client.BaseAddress = new("https://api");
});
The AppHost rewrites https://api to whatever port the API actually started on that run - the same code works whether the API landed on port 5001 or 5017 this time.
From local orchestration to an Azure deployment
The Azure Developer CLI (azd) reads the same AppHost model and turns it into actual Azure resources - the SQL Server resource becomes Azure SQL, each project becomes a Container App, and the WithReference wiring becomes the equivalent Azure configuration:
azd init
azd up
azd up provisions the infrastructure and deploys in one command, using Bicep templates it generates from the AppHost's resource graph - the same dependency model that ran everything locally now describes what to stand up in the cloud.
Common pitfalls
- Treating the AppHost as part of the runtime. It only orchestrates - it isn't deployed itself and has no role once
azd upfinishes provisioning. - Hardcoding URLs instead of using service discovery, which quietly breaks the "just works across environments" benefit Aspire provides.
- Forgetting
WithReferencefor a dependency, so the connection string or service URL never gets injected and the app fails at startup. - Assuming
azd upis free. It provisions real, billable Azure resources - review what gets created before running it against a subscription you care about. - Not checking the Aspire dashboard first when a local multi-project run misbehaves - it's usually faster than digging through separate log files.
Key Takeaways
- The AppHost project describes what runs and how services connect - it orchestrates but is never itself deployed.
WithReferenceautomatically injects a dependency's connection info, replacing manually copied connection strings.- The Aspire dashboard gives one view of logs, traces, and metrics across every orchestrated project.
- Service discovery resolves logical service names to actual URLs, so code doesn't hardcode ports that change between runs.
azd upturns the same AppHost resource graph into real Azure infrastructure and deploys to it in one command.
Get the Full Source Code
The complete runnable solution - the AppHost project, the referenced API and web projects, and the azd configuration - is available to Patreon supporters. If you want the orchestration model ready to adapt to your own multi-project app instead of rebuilding it from the walkthrough above, you can find it on Patreon.