Remigiusz ZalewskiRemigiusz Zalewski

.NET Aspire basics with Azure deployment

dotnet-aspire-basics-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 up finishes provisioning.
  • Hardcoding URLs instead of using service discovery, which quietly breaks the "just works across environments" benefit Aspire provides.
  • Forgetting WithReference for a dependency, so the connection string or service URL never gets injected and the app fails at startup.
  • Assuming azd up is 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.
  • WithReference automatically 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 up turns 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.

Resources