Skip to content

Commit 9c0b581

Browse files
CopilotIEvangelistadegeo
authored
Add comprehensive documentation for ExecutableResource and AddExecutable API (#4188)
* Initial plan * Add new ExecutableResource documentation article Co-authored-by: IEvangelist <[email protected]> * Address review feedback: update date, fix list punctuation, replace Azure SWA CLI with Vercel CLI Co-authored-by: IEvangelist <[email protected]> * Fix punctuation for all complete sentences in lists throughout the document Co-authored-by: IEvangelist <[email protected]> * Address review feedback: fix bullet punctuation, simplify method parameters section, correct AddExecutable parameter order, and update examples Co-authored-by: IEvangelist <[email protected]> * Apply suggestions from code review * Apply suggestions from code review * Fix WithArgs usage throughout documentation - remove non-existent WithArgs method and pass all arguments directly to AddExecutable Co-authored-by: IEvangelist <[email protected]> * Apply suggestions from code review * Update docs/app-host/executable-resources.md * Update executable-resources.md * Combine resource dependency sections and fix production deployment text Co-authored-by: IEvangelist <[email protected]> * Update executable-resources.md * Fix numbered list formatting to use all "1." as per Microsoft Writing Style Guide Co-authored-by: IEvangelist <[email protected]> * Add ai-generated metadata to executable-resources documentation Co-authored-by: adegeo <[email protected]> * Change ai-usage metadata to ai-assisted Co-authored-by: adegeo <[email protected]> --------- Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: IEvangelist <[email protected]> Co-authored-by: David Pine <[email protected]> Co-authored-by: adegeo <[email protected]>
1 parent 0e346ae commit 9c0b581

File tree

2 files changed

+206
-0
lines changed

2 files changed

+206
-0
lines changed

docs/app-host/executable-resources.md

Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
---
2+
title: Host external executables in .NET Aspire
3+
description: Learn how to use ExecutableResource and AddExecutable to host external executable applications in your .NET Aspire app host.
4+
ms.date: 08/11/2025
5+
ai-usage: ai-assisted
6+
---
7+
8+
# Host external executables in .NET Aspire
9+
10+
In .NET Aspire, you can host external executable applications alongside your projects using the <xref:Aspire.Hosting.ExecutableResourceBuilderExtensions.AddExecutable%2A> method. This capability is useful when you need to integrate executable applications or tools into your distributed application, such as Node.js applications, Python scripts, or specialized CLI tools.
11+
12+
## When to use executable resources
13+
14+
Use executable resources when you need to:
15+
16+
- Host non-.NET applications that don't have containerized equivalents.
17+
- Integrate command-line tools or utilities into your application.
18+
- Run external processes that other resources depend on.
19+
- Develop with tools that provide local development servers.
20+
21+
Common examples include:
22+
23+
- **Frontend development servers**: Tools like [Vercel CLI](https://vercel.com/docs/cli), Vite, or webpack dev server.
24+
- **Language-specific applications**: Node.js apps, Python scripts, or Go applications.
25+
- **Database tools**: Migration utilities or database seeders.
26+
- **Build tools**: Asset processors or code generators.
27+
28+
## Basic usage
29+
30+
The <xref:Aspire.Hosting.ExecutableResourceBuilderExtensions.AddExecutable%2A> method requires a resource name, the executable path, and optionally command-line arguments and a working directory:
31+
32+
```csharp
33+
var builder = DistributedApplication.CreateBuilder(args);
34+
35+
// Basic executable without arguments
36+
var nodeApp = builder.AddExecutable("frontend", "node", ".", "server.js");
37+
38+
// Executable with command-line arguments
39+
var pythonApp = builder.AddExecutable(
40+
"api", "python", ".", "-m", "uvicorn", "main:app", "--reload", "--host", "0.0.0.0", "--port", "8000");
41+
42+
builder.Build().Run();
43+
```
44+
45+
This code demonstrates setting up a basic executable resource. The first example runs a Node.js server script, while the second starts a Python application using Uvicorn with specific configuration options passed as arguments directly to the AddExecutable method.
46+
47+
## Resource dependencies and environment configuration
48+
49+
You can provide command-line arguments directly in the AddExecutable call and configure environment variables for resource dependencies. Executable resources can reference other resources and access their connection information.
50+
51+
### Arguments in the AddExecutable call
52+
53+
```csharp
54+
var builder = DistributedApplication.CreateBuilder(args);
55+
56+
// Arguments provided directly in AddExecutable
57+
var app = builder.AddExecutable("vercel-dev", "vercel", ".", "dev", "--listen", "3000");
58+
```
59+
60+
### Resource dependencies with environment variables
61+
62+
For arguments that depend on other resources, use environment variables:
63+
64+
```csharp
65+
var builder = DistributedApplication.CreateBuilder(args);
66+
67+
var redis = builder.AddRedis("cache");
68+
var postgres = builder.AddPostgres("postgres").AddDatabase("appdb");
69+
70+
var app = builder.AddExecutable("worker", "python", ".", "worker.py")
71+
.WithReference(redis) // Provides ConnectionStrings__cache
72+
.WithReference(postgres); // Provides ConnectionStrings__appdb
73+
```
74+
75+
When one resource depends on another, `WithReference` passes along environment variables containing the dependent resource's connection details. For example, the `worker` executable's reference to `redis` and `postgres` provides it with the `ConnectionStrings__cache` and `ConnectionStrings__appdb` environment variables, which contain connection strings to these resources.
76+
77+
### Access specific endpoint information
78+
79+
For more control over how connection information is passed to your executable:
80+
81+
```csharp
82+
var builder = DistributedApplication.CreateBuilder(args);
83+
84+
var redis = builder.AddRedis("cache");
85+
86+
var app = builder.AddExecutable("app", "node", ".", "app.js")
87+
.WithReference(redis)
88+
.WithEnvironment(context =>
89+
{
90+
// Provide individual connection details
91+
context.EnvironmentVariables["REDIS_HOST"] = redis.Resource.PrimaryEndpoint.Property(EndpointProperty.Host);
92+
context.EnvironmentVariables["REDIS_PORT"] = redis.Resource.PrimaryEndpoint.Property(EndpointProperty.Port);
93+
});
94+
```
95+
96+
## Practical example: Vercel CLI
97+
98+
Here's a complete example using the [Vercel CLI](https://vercel.com/docs/cli) to host a frontend application with a backend API:
99+
100+
```csharp
101+
var builder = DistributedApplication.CreateBuilder(args);
102+
103+
// Backend API
104+
var api = builder.AddProject<Projects.Api>("api")
105+
.WithExternalHttpEndpoints();
106+
107+
// Frontend with Vercel CLI
108+
var frontend = builder.AddExecutable(
109+
"vercel-dev", "vercel", ".", "dev", "--listen", "3000")
110+
.WithEnvironment("API_URL", api.GetEndpoint("http"))
111+
.WithHttpEndpoint(port: 3000, name: "http");
112+
113+
builder.Build().Run();
114+
```
115+
116+
## Configure endpoints
117+
118+
Executable resources can expose HTTP endpoints that other resources can reference:
119+
120+
```csharp
121+
var builder = DistributedApplication.CreateBuilder(args);
122+
123+
var frontend = builder.AddExecutable(
124+
"vite-dev", "npm", ".", "run", "dev", "--", "--port", "5173", "--host", "0.0.0.0")
125+
.WithHttpEndpoint(port: 5173, name: "http");
126+
127+
// Another service can reference the frontend
128+
var e2eTests = builder.AddExecutable("playwright", "npx", ".", "playwright", "test")
129+
.WithEnvironment("BASE_URL", frontend.GetEndpoint("http"));
130+
```
131+
132+
## Environment configuration
133+
134+
Configure environment variables for your executable:
135+
136+
```csharp
137+
var builder = DistributedApplication.CreateBuilder(args);
138+
139+
var app = builder.AddExecutable(
140+
"api", "uvicorn", ".", "main:app", "--reload", "--host", "0.0.0.0")
141+
.WithEnvironment("DEBUG", "true")
142+
.WithEnvironment("LOG_LEVEL", "info")
143+
.WithEnvironment(context =>
144+
{
145+
// Dynamic environment variables
146+
context.EnvironmentVariables["START_TIME"] = DateTimeOffset.UtcNow.ToString();
147+
});
148+
```
149+
150+
## Publishing with PublishAsDockerfile
151+
152+
For production deployment, executable resources need to be containerized. Use the <xref:Aspire.Hosting.ExecutableResourceBuilderExtensions.PublishAsDockerFile*> method to specify how the executable should be packaged:
153+
154+
```csharp
155+
var builder = DistributedApplication.CreateBuilder(args);
156+
157+
var app = builder.AddExecutable(
158+
"frontend", "npm", ".", "start", "--port", "3000")
159+
.PublishAsDockerfile();
160+
```
161+
162+
When you call `PublishAsDockerfile()`, .NET Aspire generates a Dockerfile during the publish process. You can customize this by providing your own Dockerfile:
163+
164+
### Custom Dockerfile for publishing
165+
166+
Create a `Dockerfile` in your executable's working directory:
167+
168+
```dockerfile
169+
FROM node:22-alpine
170+
WORKDIR /app
171+
COPY package*.json ./
172+
RUN npm ci --only=production
173+
COPY . .
174+
EXPOSE 3000
175+
CMD ["npm", "start"]
176+
```
177+
178+
Then reference it in your app host:
179+
180+
```csharp
181+
var builder = DistributedApplication.CreateBuilder(args);
182+
183+
var app = builder.AddExecutable("frontend", "npm", ".", "start")
184+
.PublishAsDockerfile([new DockerfileBuildArg("NODE_ENV", "production")]);
185+
```
186+
187+
## Best practices
188+
189+
When working with executable resources:
190+
191+
1. **Use explicit paths**: For better reliability, use full paths to executables when possible.
192+
1. **Handle dependencies**: Use `WithReference` to establish proper dependency relationships.
193+
1. **Configure explicit start**: Use `WithExplicitStart()` for executables that shouldn't start automatically.
194+
1. **Prepare for deployment**: Always use `PublishAsDockerfile()` for production scenarios.
195+
1. **Environment isolation**: Use environment variables rather than command-line arguments for sensitive configuration.
196+
1. **Resource naming**: Use descriptive names that clearly identify the executable's purpose.
197+
198+
## See also
199+
200+
- [App host overview](../fundamentals/app-host-overview.md)
201+
- [Add Dockerfiles to the app model](withdockerfile.md)
202+
- [Node.js apps in .NET Aspire](../get-started/build-aspire-apps-with-nodejs.md)
203+
- [Python apps in .NET Aspire](../get-started/build-aspire-apps-with-python.md)

docs/toc.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,9 @@ items:
7171
- name: Custom resource URLs
7272
displayName: withurl,withurlforendpoint,withurls
7373
href: fundamentals/custom-resource-urls.md
74+
- name: Host external executables
75+
href: app-host/executable-resources.md
76+
displayName: executable,addexecutable,external apps,cli tools
7477
- name: Add Dockerfiles to the app model
7578
href: app-host/withdockerfile.md
7679
displayName: dockerfile,docker

0 commit comments

Comments
 (0)