Edit

Share via


Aspire orchestration overview

Aspire provides APIs for expressing resources and dependencies within your distributed application. In addition to these APIs, there's tooling that enables several compelling scenarios. The orchestrator is intended for local development purposes and isn't supported in production environments.

Before continuing, consider some common terminology used in Aspire:

  • App model: A collection of resources that make up your distributed application (DistributedApplication), defined within the Aspire.Hosting.ApplicationModel namespace. For a more formal definition, see Define the app model.
  • AppHost/Orchestrator project: The .NET project that orchestrates the app model, named with the *.AppHost suffix (by convention).
  • Resource: A resource is a dependent part of an application, such as a .NET project, container, executable, database, cache, or cloud service. It represents any part of the application that can be managed or referenced.
  • Integration: An integration is a NuGet package for either the AppHost that models a resource or a package that configures a client for use in a consuming app. For more information, see Aspire integrations overview.
  • Reference: A reference defines a connection between resources, expressed as a dependency using the WithReference API. For more information, see Reference resources or Reference existing resources.

Note

Aspire's orchestration is designed to enhance your local development experience by simplifying the management of your cloud-native app's configuration and interconnections. While it's an invaluable tool for development, it's not intended to replace production environment systems like Kubernetes, which are specifically designed to excel in that context.

Define the app model

Aspire enables you to efficiently build, provision, deploy, configure, test, run, and monitor your distributed applications. These capabilities are powered by an app model, which defines the resources in your Aspire solution and their interconnections.

The app model is more than just a list of resources—it represents the complete topology of your application. This includes the relationships between resources, their dependencies, and their configurations. Resources can include projects, executables, containers, external services, and cloud resources that your application relies on.

In your Aspire AppHost project, your Program file defines your app model:

var builder = DistributedApplication.CreateBuilder(args);

// Add resources to the app model

builder.Build().Run();

When you call DistributedApplication.CreateBuilder, you get an instance of IDistributedApplicationBuilder, which is used to configure your app model. This builder provides methods to add resources, define dependencies, and set up the overall structure of your application. After you've added resources, call Build to create the app model. The templates include code that chains a call to Build()—which returns an DistributedApplication instance, and then calls Run().

AppHost project

The AppHost project handles running all of the projects that are part of the Aspire project. In other words, it's responsible for orchestrating all apps within the app model. The project itself is a .NET executable project that uses the Aspire SDK. Starting with Aspire 13.0, the Aspire.AppHost.Sdk can be set as the sole project SDK, which implicitly adds a package reference to the 📦 Aspire.Hosting.AppHost NuGet package:

<Project Sdk="Aspire.AppHost.Sdk/13.0.0">
    
    <PropertyGroup>
        <OutputType>Exe</OutputType>
        <TargetFramework>net10.0</TargetFramework>
        <!-- Omitted for brevity -->
    </PropertyGroup>

    <!-- Omitted for brevity -->

</Project>

Note

The alternative approach of explicitly listing the SDK and package reference still works and isn't a requirement to change existing projects:

<Project Sdk="Microsoft.NET.Sdk">

    <Sdk Name="Aspire.AppHost.Sdk" Version="13.0.0" />
    
    <PropertyGroup>
        <OutputType>Exe</OutputType>
        <TargetFramework>net10.0</TargetFramework>
        <!-- Omitted for brevity -->
    </PropertyGroup>

    <ItemGroup>
        <PackageReference Include="Aspire.Hosting.AppHost" Version="13.0.0" />
    </ItemGroup>

    <!-- Omitted for brevity -->

</Project>

The following code describes an AppHost Program with two project references and a Redis cache:

var builder = DistributedApplication.CreateBuilder(args);

var cache = builder.AddRedis("cache");

var apiservice = builder.AddProject<Projects.AspireApp_ApiService>("apiservice");

builder.AddProject<Projects.AspireApp_Web>("webfrontend")
       .WithExternalHttpEndpoints()
       .WithReference(cache)
       .WaitFor(cache)
       .WithReference(apiService)
       .WaitFor(apiService);

builder.Build().Run();

The preceding code:

  • Creates a new app model builder using the CreateBuilder method.
  • Adds a Redis cache resource named "cache" using the AddRedis method.
  • Adds a project resource named "apiservice" using the AddProject method.
  • Adds a project resource named "webfrontend" using the AddProject method.
    • Specifies that the project has external HTTP endpoints using the WithExternalHttpEndpoints method.
    • Adds a reference to the cache resource and waits for it to be ready using the WithReference and WaitFor methods.
    • Adds a reference to the apiservice resource and waits for it to be ready using the WithReference and WaitFor methods.
  • Builds and runs the app model using the Build and Run methods.

The example code uses the Aspire Redis hosting integration.

To help visualize the relationship between the AppHost project and the resources it describes, consider the following diagram:

The relationship between the projects in the Aspire Starter Application template.

Each resource must be uniquely named. This diagram shows each resource and the relationships between them. The container resource is named "cache" and the project resources are named "apiservice" and "webfrontend". The web frontend project references the cache and API service projects. When you're expressing references in this way, the web frontend project is saying that it depends on these two resources, the "cache" and "apiservice" respectively.

Built-in resource types

Aspire projects are made up of a set of resources. The primary base resource types in the 📦 Aspire.Hosting.AppHost NuGet package are described in the following table:

Method Resource type Description
AddProject ProjectResource A .NET project, for example, an ASP.NET Core web app.
AddCSharpApp CSharpAppResource A C# project or file-based app, for example, a *.cs file, *.csproj file, or project directory.
AddContainer ContainerResource A container image, such as a Docker image.
AddExecutable ExecutableResource An executable file, such as a Node.js app.
AddParameter ParameterResource A parameter resource that can be used to express external parameters.

Project resources represent .NET projects that are part of the app model. When you add a project reference to the AppHost project, the Aspire SDK generates a type in the Projects namespace for each referenced project. For more information, see Aspire SDK: Project references. Alternatively, you can add C# projects or file-based apps without a project reference using the AddCSharpApp method.

To add a project to the app model, use the AddProject method:

var builder = DistributedApplication.CreateBuilder(args);

// Adds the project "apiservice" of type "Projects.AspireApp_ApiService".
var apiservice = builder.AddProject<Projects.AspireApp_ApiService>("apiservice");

Projects can be replicated and scaled out by adding multiple instances of the same project to the app model. To configure replicas, use the WithReplicas method:

var builder = DistributedApplication.CreateBuilder(args);

// Adds the project "apiservice" of type "Projects.AspireApp_ApiService".
var apiservice = builder.AddProject<Projects.AspireApp_ApiService>("apiservice")
                        .WithReplicas(3);

The preceding code adds three replicas of the "apiservice" project resource to the app model. For more information, see Aspire dashboard: Resource replicas.

C# app resources represent C# projects or file-based apps that are part of the app model. Unlike AddProject, which requires a project reference, the AddCSharpApp method can add C# projects or file-based apps using a path to a *.cs file, *.csproj file, or project directory. This is useful for adding file-based apps introduced in .NET 10 or for including projects without adding a project reference to the AppHost.

To add a C# app to the app model, use the AddCSharpApp method:

var builder = DistributedApplication.CreateBuilder(args);

// Adds a file-based C# app "inventoryservice" from a .cs file.
var inventoryService = builder.AddCSharpApp("inventoryservice", @"..\InventoryService.cs");

// Adds a C# project "catalogservice" from a project directory.
var catalogService = builder.AddCSharpApp("catalogservice", @"..\CatalogService");

The AddCSharpApp method supports the same configuration options as AddProject, including replicas, environment variables, and resource dependencies.

Note

The AddCSharpApp method is marked as experimental and requires .NET 10 SDK for file-based C# app support. For more information on file-based apps, see the What's new in Aspire 9.5 documentation.

Reference resources

A reference represents a dependency between resources. For example, you can probably imagine a scenario where a web frontend depends on a Redis cache. Consider the following example AppHost Program C# code:

var builder = DistributedApplication.CreateBuilder(args);

var cache = builder.AddRedis("cache");

builder.AddProject<Projects.AspireApp_Web>("webfrontend")
       .WithReference(cache);

The "webfrontend" project resource uses WithReference to add a dependency on the "cache" container resource. These dependencies can represent connection strings or service discovery information. In the preceding example, an environment variable is injected into the "webfrontend" resource with the name ConnectionStrings__cache. This environment variable contains a connection string that the webfrontend uses to connect to Redis via the Aspire Redis integration, for example, ConnectionStrings__cache="localhost:62354".

Connection string and endpoint references

It's common to express dependencies between project resources. Consider the following example code:

var builder = DistributedApplication.CreateBuilder(args);

var cache = builder.AddRedis("cache");

var apiservice = builder.AddProject<Projects.AspireApp_ApiService>("apiservice");

builder.AddProject<Projects.AspireApp_Web>("webfrontend")
       .WithReference(cache)
       .WithReference(apiservice);

Project-to-project references are handled differently than resources that have well-defined connection strings. Instead of connection string being injected into the "webfrontend" resource, environment variables to support service discovery are injected.

Method Environment variable
WithReference(cache) ConnectionStrings__cache="localhost:62354"
WithReference(apiservice) APISERVICE_HTTP="http://localhost:5455"
APISERVICE_HTTPS="https://localhost:7356"
services__apiservice__http__0="http://localhost:5455"
services__apiservice__https__0="https://localhost:7356"

Adding a reference to the "apiservice" project results in service discovery environment variables being added to the frontend. This is because typically, project-to-project communication occurs over HTTP/gRPC.

Aspire injects two types of environment variables for service references:

  • Simplified format (e.g., APISERVICE_HTTP): Uses the pattern {RESOURCENAME}_{ENDPOINTNAME} in uppercase. This format is simpler and more suitable for non-.NET languages and polyglot scenarios.
  • .NET service discovery format (e.g., services__apiservice__http__0): Uses the pattern services__{servicename}__{endpointname}__{index} in lowercase. This format is used by .NET's configuration-based service discovery.

For more information, see Aspire service discovery.

To get specific endpoints from a ContainerResource or an ExecutableResource, use one of the following endpoint APIs:

Then call the GetEndpoint API to get the endpoint which can be used to reference the endpoint in the WithReference method:

var builder = DistributedApplication.CreateBuilder(args);

var customContainer = builder.AddContainer("myapp", "mycustomcontainer")
                             .WithHttpEndpoint(port: 9043, name: "endpoint");

var endpoint = customContainer.GetEndpoint("endpoint");

var apiservice = builder.AddProject<Projects.AspireApp_ApiService>("apiservice")
                        .WithReference(endpoint);
Method Environment variable
WithReference(endpoint) MYAPP_ENDPOINT="https://localhost:9043"
services__myapp__endpoint__0="https://localhost:9043"

The port parameter is the port that the container is listening on. For more information on container ports, see Container ports. For more information on service discovery, see Aspire service discovery.

Service endpoint environment variable format

In the preceding section, the WithReference method is used to express dependencies between resources. When service endpoints result in environment variables being injected into the dependent resource, the format might not be obvious. This section provides details on the available formats.

When one resource depends on another resource, the AppHost injects environment variables into the dependent resource. These environment variables configure the dependent resource to connect to the resource it depends on. Aspire provides two environment variable formats to support different scenarios:

Simplified format (polyglot-friendly)

The simplified format uses the pattern {RESOURCENAME}_{ENDPOINTNAME} in uppercase. This format is easier to use from non-.NET languages and is recommended for polyglot scenarios.

Consider the following environment variable examples:

APISERVICE_HTTP
APISERVICE_HTTPS

The preceding environment variables express HTTP and HTTPS endpoints for the apiservice service. A named endpoint might be expressed as follows:

APISERVICE_MYENDPOINT

In the preceding example, the apiservice service has a named endpoint called myendpoint.

Note

The environment variable name is based on the resource name, not the optional connection name parameter. Even when using WithReference(resource, "customname") to specify a custom connection name, the generated environment variables still use the resource's name (e.g., APISERVICE_HTTP), not the custom name.

.NET service discovery format

The .NET service discovery format is used by .NET's configuration-based service discovery. Service endpoint environment variable names are prefixed with services__ (double underscore), then the service name, the endpoint name, and finally the index. The index supports multiple endpoints for a single service, starting with 0 for the first endpoint and incrementing for each endpoint.

Consider the following environment variable examples:

services__apiservice__http__0
services__apiservice__https__0

The preceding environment variables express the first HTTP and HTTPS endpoints for the apiservice service. A named endpoint might be expressed as follows:

APISERVICE_MYENDPOINT

In the preceding example, the apiservice service has a named endpoint called myendpoint.

Using a specific endpoint with WithEnvironment

To specify a custom environment variable name for a specific endpoint, use the WithEnvironment method combined with GetEndpoint:

var projectA = builder.AddProject<Projects.ProjectA>("projecta");
var projectB = builder.AddProject<Projects.ProjectB>("projectb")
                      .WithEnvironment("PROJECTA_URL", projectA.GetEndpoint("https"));

This generates a single environment variable PROJECTA_URL with the HTTPS endpoint URL of the projecta service.

Reference existing resources

Some situations warrant that you reference an existing resource, perhaps one that is deployed to a cloud provider. For example, you might want to reference an Azure database. In this case, you'd rely on the Execution context to dynamically determine whether the AppHost is running in "run" mode or "publish" mode. If you're running locally and want to rely on a cloud resource, you can use the IsRunMode property to conditionally add the reference. You might choose to instead create the resource in publish mode. Some hosting integrations support providing a connection string directly, which can be used to reference an existing resource.

Likewise, there might be use cases where you want to integrate Aspire into an existing solution. One common approach is to add the Aspire AppHost project to an existing solution. Within your AppHost, you express dependencies by adding project references to the AppHost and building out the app model. For example, one project might depend on another. These dependencies are expressed using the WithReference method. For more information, see Add Aspire to an existing .NET app.

Execution context

The IDistributedApplicationBuilder exposes an execution context (DistributedApplicationExecutionContext), which provides information about the current execution of the AppHost. This context can be used to evaluate whether or not the AppHost is executing as "run" mode, or as part of a publish operation. Consider the following properties:

  • IsRunMode: Returns true if the current operation is running.
  • IsPublishMode: Returns true if the current operation is publishing.

This information can be useful when you want to conditionally execute code based on the current operation. Consider the following example that demonstrates using the IsRunMode property. In this case, an extension method is used to generate a stable node name for RabbitMQ for local development runs.

private static IResourceBuilder<RabbitMQServerResource> RunWithStableNodeName(
    this IResourceBuilder<RabbitMQServerResource> builder)
{
    if (builder.ApplicationBuilder.ExecutionContext.IsRunMode)
    {
        builder.WithEnvironment(context =>
        {
            // Set a stable node name so queue storage is consistent between sessions
            var nodeName = $"{builder.Resource.Name}@localhost";
            context.EnvironmentVariables["RABBITMQ_NODENAME"] = nodeName;
        });
    }

    return builder;
}

The execution context is often used to conditionally add resources or connection strings that point to existing resources. Consider the following example that demonstrates conditionally adding Redis or a connection string based on the execution context:

var builder = DistributedApplication.CreateBuilder(args);

var redis = builder.ExecutionContext.IsRunMode
    ? builder.AddRedis("redis")
    : builder.AddConnectionString("redis");

builder.AddProject<Projects.WebApplication>("api")
       .WithReference(redis);

builder.Build().Run();

In the preceding code:

  • If the AppHost is in "run" mode, a Redis container resource is added.
  • If the AppHost is in "publish" mode, a connection string is added.

This logic can easily be inverted to connect to an existing Redis resource when you're running locally, and create a new Redis resource when you're publishing.

Important

Aspire provides common APIs to control the modality of resource builders, allowing resources to behave differently based on the execution mode. The fluent APIs are prefixed with RunAs* and PublishAs*. The RunAs* APIs influence the local development (or run mode) behavior, whereas the PublishAs* APIs influence the publishing of the resource. For more information on how the Azure resources use these APIs, see Use existing Azure resources.

See also