Aspire AppHost: Orchestrating Your .NET Applications
If you have ever juggled multiple terminal windows to start an API, a worker service, a Redis instance, and a PostgreSQL database just to run your application locally, .NET Aspire's AppHost project exists specifically to solve that problem. It is the orchestration layer that ties your entire distributed application together into a single runnable unit.
What Is the AppHost?
The AppHost is a standard .NET project — typically a console application — that references every service and resource your application needs. When you run it, Aspire starts containers, configures connection strings, injects environment variables, and launches your .NET projects in the correct order.
Think of it as a docker-compose.yml replacement that speaks C# and understands .NET natively.
Creating an AppHost
When you create a new Aspire project via dotnet new aspire, you get an AppHost project automatically. You can also add one to an existing solution:
dotnet new aspire-apphost -n MyApp.AppHost
The entry point is Program.cs, where you declare your application's topology using the DistributedApplication builder:
var builder = DistributedApplication.CreateBuilder(args);
var cache = builder.AddRedis("cache");
var db = builder.AddPostgres("postgres")
.AddDatabase("catalogdb");
var catalogApi = builder.AddProject<Projects.CatalogApi>("catalog-api")
.WithReference(db)
.WithReference(cache);
var frontend = builder.AddProject<Projects.Frontend>("frontend")
.WithReference(catalogApi);
builder.Build().Run();
This small amount of code does a remarkable amount of work. It pulls Redis and PostgreSQL container images, creates a database called catalogdb, starts both .NET projects, and wires up connection strings and service discovery endpoints automatically.
How References Work
The WithReference method is the glue. When you write .WithReference(db), Aspire injects the connection string for catalogdb into the catalog-api project's configuration. Your service code simply reads it the standard way:
builder.AddNpgsqlDbContext<CatalogContext>("catalogdb");
No hardcoded connection strings. No manual environment variable management. The AppHost handles it all.
For project-to-project references, .WithReference(catalogApi) sets up service discovery so the frontend can call the catalog API by name rather than by a specific port number.
Resource Types
The AppHost supports several categories of resources:
- Projects — your .NET applications, added with
AddProject<T> - Containers — any OCI container, added with
AddContainer - Built-in components — first-party integrations like
AddRedis,AddPostgres,AddRabbitMQ - Executables — non-.NET processes, added with
AddExecutable
Each resource type participates in the same lifecycle: Aspire starts them, monitors their health, and displays their status on the dashboard.
Configuring Resources
You can customise resources with additional methods. For example, to expose a specific port or add environment variables:
var api = builder.AddProject<Projects.MyApi>("api")
.WithHttpEndpoint(port: 5100)
.WithEnvironment("FEATURE_FLAG", "true");
var worker = builder.AddProject<Projects.MyWorker>("worker")
.WithReference(cache)
.WithReplicas(3);
The WithReplicas method spins up multiple instances of a service, which is useful for testing load-balanced scenarios locally.
Lifecycle and Startup Order
Aspire does not simply start everything at once. It understands the dependency graph you have defined through WithReference calls. If your API depends on a database, Aspire ensures the database container is healthy before starting the API project.
You can also hook into the lifecycle with custom health checks and wait conditions:
var db = builder.AddPostgres("postgres")
.AddDatabase("mydb");
builder.AddProject<Projects.Migrator>("migrator")
.WithReference(db)
.WaitFor(db);
builder.AddProject<Projects.Api>("api")
.WithReference(db)
.WaitForCompletion(builder.GetResource("migrator"));
Here the API waits not just for the database to be ready, but for the migrator project to finish running before it starts.
Why This Matters
The AppHost removes an entire category of "works on my machine" problems. Every developer on the team runs the same topology with the same configuration. There are no stale README instructions about which containers to start or which ports to use.
It also serves as living documentation. A glance at the AppHost's Program.cs tells you exactly what your distributed application looks like — which services exist, what they depend on, and how they connect.
If you are building anything beyond a single-project application in .NET, the AppHost is the starting point that makes everything else in Aspire possible.