Add Aspire to an existing app
Este conteúdo não está disponível em sua língua ainda.
Add Aspire to the app you already have instead of rebuilding your solution around a new template. Start by choosing the AppHost style that fits your repo, then register the services, containers, shared infrastructure, and, when needed, custom executables you already run today, regardless of whether those workloads are written in C#, Node.js, Python, Go, Rust, Java, or something else.
Why add Aspire to an existing app?
Section titled “Why add Aspire to an existing app?”As distributed applications grow, local development often turns into a collection of fragile scripts, copied connection strings, and startup-order tribal knowledge. Aspire gives you a single orchestration layer for the resources you already own. Define the relationships once in code, and Aspire handles service discovery, configuration injection, startup ordering, and dashboard visibility.
You can also adopt Aspire incrementally. Start by modeling the parts that are hardest to keep aligned by hand, such as containers, databases, caches, queues, background workers, and local dev commands. Add telemetry when you’re ready, then deepen the model as your app grows.
Start with your scenario
Section titled “Start with your scenario”This guide is organized around the kinds of resources you already manage rather than around a preferred service language. Aspire is multi-language by design, so one AppHost can orchestrate a system that spans C#, Node.js, Python, Go, Rust, Java, and other supported workloads.
- Existing services with hosting integrations: You already have C# services, Node.js apps, Vite frontends, Python apps, or ASGI apps such as FastAPI and want to use Aspire’s dedicated hosting APIs.
- Existing containers and shared infrastructure: You already have images, Docker Compose files, databases, caches, queues, or reverse proxies that you want Aspire to coordinate.
- Existing C# services: You already have C# projects or file-based C# apps and want the C# AppHost to orchestrate them directly.
In every scenario, you can choose either AppHost style and mix resource types in the same application model.
When a dedicated hosting API exists for a workload, prefer it over AddExecutable or addExecutable. Executable resources are the fallback for custom tools, one-off commands, or workloads that do not yet have a dedicated hosting integration.
Choose your AppHost
Section titled “Choose your AppHost”The AppHost is the orchestration layer. Your choice here changes how you express orchestration, not what Aspire can orchestrate.
Use a C# AppHost when your repo already centers on C# or when you want a single-file orchestrator that still fits naturally into .NET SDK and IDE workflows.
- Lives in a single
apphost.csfile that uses#:sdkand#:packagedirectives - Uses methods such as
AddUvicornApp,AddPythonApp,AddNodeApp,AddViteApp,AddContainer, andAddCSharpApp - Fits naturally into existing .NET SDK, IDE, and repo workflows
Use a TypeScript AppHost when your repo already centers on a Node.js workspace or when you prefer path-based orchestration in TypeScript.
- Lives in
apphost.ts - Uses methods such as
addUvicornApp,addPythonApp,addNodeApp,addViteApp,addContainer, andaddProject - Fits naturally into existing package-manager and monorepo workflows
Prerequisites
Section titled “Prerequisites”Before you begin, make sure you have:
- Aspire CLI installed
- An existing application or workspace to add Aspire to
- The runtimes and tools your existing services already need
AppHost-specific requirements
Section titled “AppHost-specific requirements”- .NET SDK 10.0 or later
- Visual Studio 2022 17.13 or later, Visual Studio Code, or JetBrains Rider (optional)
- Node.js 22 or later
- npm, yarn, or pnpm
Scenario-specific requirements
Section titled “Scenario-specific requirements”For workloads with hosting integrations:
- Service directories and standard project metadata for the workloads you plan to model, such as
package.json,pyproject.toml, orrequirements.txt - The runtimes and package managers those services already need
For custom executables as a fallback:
- Working start commands for each service you plan to model
- Any repo-local config files or working directories those commands rely on
For containers and shared infrastructure:
- Existing image names, Dockerfiles, or Compose knowledge for the services you want to model
- The databases, caches, queues, or reverse proxies you want Aspire to own or connect to
For C# services:
- One or more C# projects or file-based C# apps if you plan to use
AddCSharpApp - A solution file is optional and not required for a file-based C# AppHost
Overview of the process
Section titled “Overview of the process”Adding Aspire to an existing app usually follows these steps:
- Choose an AppHost that fits your repo and workflow.
- Initialize Aspire support with
aspire init. - Register your existing processes, containers, and shared resources in the AppHost.
- Add telemetry and integrations where they add value.
- Run and verify the full system with Aspire orchestration.
Initialize Aspire support
Section titled “Initialize Aspire support”The aspire init command creates the orchestration layer and helps wire the first set of resources into it.
-
Navigate to the root of your existing repo:
Navigate to your repo cd /path/to/your-repo -
Run
aspire init:Initialize Aspire with a C# AppHost aspire initThe command runs in interactive mode by default. It can detect existing C# projects, create a single-file AppHost, and add the initial package directives and configuration it needs.
For more details on the aspire init command and its options, see the CLI reference: aspire init.
After initialization, a typical C#-centric repo might look like this:
- apphost.cs (new)
- apphost.run.json (new)
Directoryservices/
DirectoryApi/
- ExampleEcommerce.Api.csproj
DirectoryWeb/
- package.json
Directorysrc/
- …
Directoryworkers/
Directoryinventory-sync/
- worker.py
Starter AppHost:
#:sdk Aspire.AppHost.Sdk@13.1.0
var builder = DistributedApplication.CreateBuilder(args);
// TODO: Add resources here
builder.Build().Run();Add #:package directives in apphost.cs for the hosting integrations you use, such as Aspire.Hosting.Redis, Aspire.Hosting.Python, Aspire.Hosting.JavaScript, and Aspire.Hosting.PostgreSQL.
-
Navigate to the root of your existing workspace or repo:
Navigate to your workspace or repo cd /path/to/your-workspace -
Run
aspire initwith the TypeScript AppHost option:Initialize Aspire with a TypeScript AppHost aspire init --language typescriptIf you omit
--language typescript, choose TypeScript when the CLI prompts for the AppHost language.
For more details on the aspire init command and its options, see the CLI reference: aspire init.
After initialization, a typical workspace might look like this:
Directorymy-store/
- apphost.ts (new)
Directory.modules/ (new)
- …
- aspire.config.json (new)
- package.json (new or updated)
Directoryservices/
Directoryapi/
- Dockerfile
Directoryweb/
- package.json
Directorysrc/
- …
Directoryworkers/
Directoryinventory-sync/
- worker.py
Starter AppHost:
import { createBuilder } from './.modules/aspire.js';
const builder = await createBuilder();
// Add your resources here
await builder.build().run();Model your existing apps in the AppHost
Section titled “Model your existing apps in the AppHost”Once you have an AppHost, register the resources you already run today. Each service, container, integration, database, queue, project reference, or, when necessary, executable becomes a resource in the AppHost.
Two patterns stay the same across AppHost styles:
- Use
WithReferenceorwithReferenceto declare resource dependencies and enable service discovery. - Use
WaitFororwaitForwhen one resource should wait for another to be ready.
Scenario: Existing services with hosting integrations
Section titled “Scenario: Existing services with hosting integrations”Use this approach when your app includes supported service types such as Node.js apps, Vite frontends, Python scripts, or Uvicorn-based APIs. In these cases, prefer the dedicated hosting APIs over raw executable resources.
#:sdk Aspire.AppHost.Sdk@13.1.0#:package Aspire.Hosting.Redis@*#:package Aspire.Hosting.Python@*#:package Aspire.Hosting.JavaScript@*
var builder = DistributedApplication.CreateBuilder(args);
var cache = builder.AddRedis("cache");
var api = builder.AddUvicornApp("api", "../services/api", "main:app") .WithUv() .WithReference(cache) .WithExternalHttpEndpoints();
var worker = builder.AddPythonApp("worker", "../workers/inventory-sync", "worker.py") .WithReference(cache);
var web = builder.AddViteApp("web", "../services/web") .WithReference(api) .WaitFor(api);
builder.Build().Run();import { createBuilder } from './.modules/aspire.js';
const builder = await createBuilder();
const cache = await builder.addRedis('cache');
const api = await builder .addUvicornApp('api', './services/api', 'main:app') .withUv() .withReference(cache) .withExternalHttpEndpoints();
await builder .addPythonApp('worker', './workers/inventory-sync', 'worker.py') .withReference(cache);
await builder .addViteApp('web', './services/web') .withReference(api) .waitFor(api);
await builder.build().run();If a workload does not have a dedicated hosting API yet, use AddExecutable or addExecutable as the fallback path for custom CLIs, bespoke build tools, or unsupported runtime commands.
For first-class workload guidance, see JavaScript integration, Python integration, and Multi-language architecture.
Scenario: Existing containers and shared infrastructure
Section titled “Scenario: Existing containers and shared infrastructure”Use this approach when you already have container images, Docker Compose services, or an infrastructure topology that you want to model directly in the AppHost.
Before you model shared infrastructure with Aspire, add the integrations you need:
aspire add postgresaspire add redis#:sdk Aspire.AppHost.Sdk@13.1.0#:package Aspire.Hosting.PostgreSQL@*#:package Aspire.Hosting.Redis@*
var builder = DistributedApplication.CreateBuilder(args);
var db = builder.AddPostgres("postgres") .AddDatabase("orders");
var cache = builder.AddRedis("cache");
var api = builder.AddContainer("api", "ghcr.io/contoso/orders-api:latest") .WithReference(db) .WithReference(cache) .WithHttpEndpoint(port: 8080, targetPort: 8080, name: "http");
var web = builder.AddContainer("web", "ghcr.io/contoso/orders-web:latest") .WithReference(api) .WithHttpEndpoint(port: 3000, targetPort: 3000, name: "http");
builder.Build().Run();import { createBuilder } from './.modules/aspire.js';
const builder = await createBuilder();
const db = (await builder.addPostgres('postgres')).addDatabase('orders');
const cache = await builder.addRedis('cache');
const api = await builder .addContainer('api', 'ghcr.io/contoso/orders-api:latest') .withReference(db) .withReference(cache) .withHttpEndpoint({ port: 8080, targetPort: 8080, name: 'http' });
await builder .addContainer('web', 'ghcr.io/contoso/orders-web:latest') .withReference(api) .withHttpEndpoint({ port: 3000, targetPort: 3000, name: 'http' });
await builder.build().run();Scenario: Existing C# services
Section titled “Scenario: Existing C# services”Use this approach when you want a file-based C# AppHost to orchestrate existing C# services without creating an AppHost .csproj and project-reference graph.
In a file-based C# AppHost, use AddCSharpApp to point at existing C# service paths directly. This works with single-file C# apps, directories, or existing .csproj files.
#:sdk Aspire.AppHost.Sdk@13.1.0#:package Aspire.Hosting.Redis@*
#pragma warning disable ASPIRECSHARPAPPS001
var builder = DistributedApplication.CreateBuilder(args);
var cache = builder.AddRedis("cache");
var api = builder.AddCSharpApp("api", "./src/Api/Store.Api.csproj") .WithReference(cache) .WithHttpHealthCheck("/health");
var worker = builder.AddCSharpApp("worker", "./src/Worker/Store.Worker.csproj") .WithReference(cache);
builder.Build().Run();import { createBuilder } from './.modules/aspire.js';
const builder = await createBuilder();
const cache = await builder.addRedis('cache');
const api = await builder .addProject('api', './src/Api/Store.Api.csproj') .withReference(cache) .withHttpHealthCheck({ path: '/health' });
await builder .addProject('worker', './src/Worker/Store.Worker.csproj') .withReference(cache);
await builder.build().run();Nothing requires a single pattern. It is common to keep some services as C# apps referenced from a file-based AppHost, run others with language-specific hosting APIs, and model shared infrastructure with integrations or containers in the same AppHost. A single application model can span C#, Node.js, Python, Go, Rust, Java, and containerized services side by side.
For more examples, see C# file-based apps, Executable resources, and Migrate from Docker Compose.
Add telemetry configuration (optional)
Section titled “Add telemetry configuration (optional)”Telemetry is configured inside your workloads, not in the AppHost itself. The right setup depends on the service language you are bringing into Aspire.
For C# services
Section titled “For C# services”If your app includes C# services, ServiceDefaults is the standard way to add observability, resilience, and health checks.
-
Add ServiceDefaults if you did not enable it during
aspire init:Add ServiceDefaults dotnet new aspire-servicedefaults -n YourProject.ServiceDefaultsdotnet sln add YourProject.ServiceDefaultsdotnet add YourProject reference YourProject.ServiceDefaults -
Update your service’s
Program.cs:Program.cs — Add ServiceDefaults var builder = WebApplication.CreateBuilder(args);builder.AddServiceDefaults();var app = builder.Build();app.MapDefaultEndpoints();app.Run();
For more information, see Service Defaults.
For Node.js and TypeScript services
Section titled “For Node.js and TypeScript services”If your app includes Node.js services, configure OpenTelemetry inside the service and point it at the Aspire OTLP endpoint.
-
Install the OpenTelemetry packages:
Install OpenTelemetry packages npm install @opentelemetry/api @opentelemetry/sdk-node \@opentelemetry/auto-instrumentations-node \@opentelemetry/exporter-trace-otlp-grpc \@opentelemetry/exporter-metrics-otlp-grpc -
Create a telemetry bootstrap file:
telemetry.ts import { NodeSDK } from '@opentelemetry/sdk-node';import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';const sdk = new NodeSDK({instrumentations: [getNodeAutoInstrumentations()],});sdk.start(); -
Import it first in your app entry point:
src/server.ts import './telemetry';import express from 'express';const app = express();
For other runtimes
Section titled “For other runtimes”Use the OpenTelemetry SDK or instrumentation library that matches the runtime you are already using, then export telemetry to the OTLP endpoint Aspire provides during local orchestration.
Add integrations (optional)
Section titled “Add integrations (optional)”Aspire integrations simplify common infrastructure such as Redis, PostgreSQL, RabbitMQ, and more. They are often the fastest way to make an existing setup easier to run because they replace copied connection strings and hand-managed startup order with explicit resource relationships. When you add an integration to the AppHost and reference it from dependent workloads, Aspire injects the configuration those workloads need.
- Identify the shared resources in your system, such as databases, caches, queues, or storage.
- Add the integration to the AppHost.
- Reference that resource from the apps that depend on it.
- Configure the client library inside each consuming service.
For example, to add Redis:
aspire add redis#:sdk Aspire.AppHost.Sdk@13.1.0#:package Aspire.Hosting.Redis@*#:package Aspire.Hosting.Python@*
var builder = DistributedApplication.CreateBuilder(args);
var cache = builder.AddRedis("cache");
var api = builder.AddContainer("api", "ghcr.io/contoso/api:latest") .WithReference(cache) .WithHttpEndpoint(port: 8080, targetPort: 8080, name: "http");
var worker = builder.AddPythonApp("worker", "../worker", "worker.py") .WithReference(cache);
builder.Build().Run();import { createBuilder } from './.modules/aspire.js';
const builder = await createBuilder();
const cache = await builder.addRedis('cache');
const api = await builder .addContainer('api', 'ghcr.io/contoso/api:latest') .withReference(cache) .withHttpEndpoint({ port: 8080, targetPort: 8080, name: 'http' });
await builder .addPythonApp('worker', './worker', 'worker.py') .withReference(cache);
await builder.build().run();The client configuration stays inside each consuming service, whether that service is a C# project, container image, or executable launched by Aspire.
dotnet add YourApi package Aspire.StackExchange.Redisvar builder = WebApplication.CreateBuilder(args);builder.AddRedisClient("cache");npm install redisimport { createClient } from 'redis';
const client = createClient({ socket: { host: process.env.CACHE_HOST, port: Number(process.env.CACHE_PORT), },});For concrete integration walkthroughs, see Redis integration, PostgreSQL integration, and RabbitMQ integration. Browse the full Integrations gallery for more options.
Run and verify
Section titled “Run and verify”Once your AppHost models the system you want to run, start everything together with the Aspire CLI.
-
From the directory that contains your AppHost, run:
Run your application with Aspire aspire run -
Wait for the CLI to discover the AppHost, launch the resources, and print the dashboard URL.
Example output Finding apphosts...Dashboard: https://localhost:17068/login?t=examplePress CTRL+C to stop the apphost and exit. -
Open the dashboard in your browser and verify:
- All resources start successfully
- Service dependencies appear in the expected order
- Logs, traces, and metrics are visible
- Endpoints and environment variables look correct
-
Exercise the actual app flows you care about, such as frontend-to-API calls, worker jobs, or database access.
-
Stop the system by pressing
⌘+C ⌘+C Control + C CtrlC Control + C CtrlC in your terminal.
Compare with Docker Compose
Section titled “Compare with Docker Compose”If you currently orchestrate the app with Docker Compose, the same relationships translate into an AppHost without forcing every workload to become a project resource.
services: postgres: image: postgres:latest environment: - POSTGRES_PASSWORD=postgres - POSTGRES_DB=mydb ports: - "5432:5432"
api: build: ./api environment: - DATABASE_URL=postgres://postgres:postgres@postgres:5432/mydb depends_on: - postgres
web: build: ./web environment: - API_URL=http://api:8080 depends_on: - api#:sdk Aspire.AppHost.Sdk@13.1.0#:package Aspire.Hosting.PostgreSQL@*
var builder = DistributedApplication.CreateBuilder(args);
var db = builder.AddPostgres("postgres") .AddDatabase("mydb");
var api = builder.AddContainer("api", "contoso/api:latest") .WithReference(db) .WithHttpEndpoint(port: 8080, targetPort: 8080, name: "http");
var web = builder.AddContainer("web", "contoso/web:latest") .WithReference(api) .WithHttpEndpoint(port: 3000, targetPort: 3000, name: "http");
builder.Build().Run();import { createBuilder } from './.modules/aspire.js';
const builder = await createBuilder();
const db = (await builder.addPostgres('postgres')).addDatabase('mydb');
const api = await builder .addContainer('api', 'contoso/api:latest') .withReference(db) .withHttpEndpoint({ port: 8080, targetPort: 8080, name: 'http' });
await builder .addContainer('web', 'contoso/web:latest') .withReference(api) .withHttpEndpoint({ port: 3000, targetPort: 3000, name: 'http' });
await builder.build().run();Next steps
Section titled “Next steps”After you have Aspire orchestrating the existing app, a few natural next steps are:
- Explore Aspire integrations for databases, caches, message queues, and cloud services
- Learn more about Executable resources
- Review Migrate from Docker Compose if your current setup is Compose-heavy
- Learn more about the Aspire dashboard
- Add C# Service Defaults for C# services
- Add standalone dashboard support for Node.js if your system includes Node services
- Follow Deploy your first app when you’re ready to move beyond local orchestration
- Use the Aspire extension for VS Code to run and inspect your app from the editor