Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions src/frontend/src/content/docs/app-host/resource-lifetimes.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ description: Learn how session, persistent, resource-scoped, and parent-process

import { Tabs, TabItem } from '@astrojs/starlight/components';
import { Image } from 'astro:assets';
import LearnMore from '@components/LearnMore.astro';
import persistentContainer from '@assets/whats-new/aspire-9/persistent-container.png';
import persistentContainerDocker from '@assets/whats-new/aspire-9/persistent-container-docker-desktop.png';

Expand Down Expand Up @@ -40,7 +41,11 @@ Persistent resources are automatically recreated when the AppHost detects meanin

Persistent containers use proxied endpoints by default, just like session containers. The proxy runs only while the AppHost is running, so the proxy address isn't reachable after the AppHost stops. Persistent executables and projects default to proxyless endpoints so their direct addresses stay stable and reachable even after the AppHost stops.

You can still configure endpoint proxy behavior explicitly on any persistent resource. Set `isProxied: false` on an individual endpoint, or call `WithEndpointProxySupport(false)` to make every endpoint on a resource proxyless. Persistent executable endpoints must have a concrete `port` or `targetPort`; automatically persisted random executable ports aren't supported. For proxyless container endpoints with only a `targetPort`, Aspire immediately uses the target port as the allocated host port.
You can still configure endpoint proxy behavior explicitly on any persistent resource. Set `isProxied: false` on an individual endpoint, or call `WithEndpointProxySupport(false)` to make every endpoint on a resource proxyless. When a proxyless endpoint doesn't specify a public `port`, Aspire allocates one before the resource is created. For persistent resources, Aspire stores the allocated port in user secrets when user secrets are available and reuses it on later AppHost runs.

<LearnMore>
For more information, see [Allocate ports for dynamic proxyless endpoints](/fundamentals/networking-overview/#allocate-ports-for-dynamic-proxyless-endpoints).
</LearnMore>

:::danger[Persistent container &ne; persistent data]
Persistent container lifetime doesn't guarantee data durability. For details, see [Container lifetime vs. data durability](#container-lifetime-vs-data-durability).
Expand Down Expand Up @@ -157,7 +162,7 @@ await builder.build().run();
</TabItem>
</Tabs>

Configure a concrete `port` or `targetPort` for persistent executable endpoints; automatically persisted random executable ports aren't supported.
The preceding example configures a concrete `port` so the endpoint address is explicit. If you omit the public `port`, Aspire allocates one before the executable starts and reuses it from user secrets on later AppHost runs when user secrets are available.

## Configure a persistent project

Expand Down
50 changes: 46 additions & 4 deletions src/frontend/src/content/docs/fundamentals/networking-overview.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ The inner loop is the process of developing and testing your app locally before

- **Endpoints/Endpoint configurations**: Endpoints are the connections between your app and the services it depends on, such as databases, message queues, or APIs. Endpoints provide information such as the service name, host port, scheme, and environment variable. Aspire can create endpoints automatically from resource configuration, and you can also add them explicitly by calling `WithEndpoint`.
- **Proxies**: Aspire automatically launches a proxy for each proxied service binding you add to your app, and assigns a port for the proxy to listen on. The proxy then forwards the requests to the port that your app listens on, which might be different from the proxy port. This way, you can avoid port conflicts and access your app and services using consistent and predictable URLs.
- **Proxyless endpoints**: Endpoints with `IsProxied` set to `false` connect directly to the resource instead of routing through an Aspire-managed proxy. Starting in Aspire 13.4, endpoints on persistent executables and projects are proxyless by default. Persistent containers use proxied endpoints by default, the same as session containers. Proxyless endpoints must specify a target port so Aspire knows which port the resource listens on.
- **Proxyless endpoints**: Endpoints with `IsProxied` set to `false` connect directly to the resource instead of routing through an Aspire-managed proxy. Starting in Aspire 13.4, endpoints on persistent executables and projects are proxyless by default. Persistent containers use proxied endpoints by default, the same as session containers.
- **Container networks**: Aspire creates and manages dedicated networks for container resources so containers can discover and communicate with each other during local development.

## How endpoints work
Expand Down Expand Up @@ -232,9 +232,7 @@ Most endpoints are proxied by default. A proxyless endpoint skips the Aspire-man

Starting in Aspire 13.4, endpoints on persistent executables and projects are proxyless by default. This lets the persistent resource keep using its direct endpoint across AppHost runs instead of depending on a new proxy instance each time. This also ensures that the ports your service is reachable on stay stable whether the AppHost is running or not. Persistent containers use proxied endpoints by default, the same as session containers, so integrations that depend on endpoint allocation before startup continue to work.

For proxyless container endpoints, Aspire resolves the host port immediately when only a `targetPort` is specified — there is no deferred allocation. For example, `.WithEndpoint(targetPort: 6379, isProxied: false)` allocates host port `6379` right away.

Because there's no proxy to listen on one port and forward to another, every proxyless endpoint must include a target port. The target port tells Aspire which port the resource listens on. You can also set `port` when you need a specific host port, but `targetPort` is still required for proxyless endpoints.
The following example configures a proxyless HTTP endpoint for an nginx container:

<Tabs syncKey="aspire-lang">
<TabItem id="csharp" label="C#">
Expand Down Expand Up @@ -262,6 +260,50 @@ await web.withHttpEndpoint({ targetPort: 80, isProxied: false });
</TabItem>
</Tabs>

### Allocate ports for dynamic proxyless endpoints

Proxyless endpoints can omit the public host `port`. Starting in Aspire 13.5, Aspire allocates a public host port for each dynamic proxyless endpoint before workload resources are created. The allocated port is then available to configuration that resolves endpoint properties, such as `GetEndpoint(...).Property(EndpointProperty.Port)`.

If you set `port`, Aspire uses that value as the public host port. If you omit `port`, Aspire allocates one from the proxyless endpoint port range. The default range is `10000-32767`, and you can override it with the `ASPIRE_PROXYLESS_ENDPOINT_PORT_RANGE` environment variable in `start-end` format. Container endpoints still require `targetPort` so Aspire knows which port inside the container receives traffic.

This lets you write patterns like the following, where a container exposes its allocated public port in an environment variable:

<Tabs syncKey="aspire-lang">
<TabItem id="csharp" label="C#">

```csharp title="AppHost.cs"
var database = builder.AddContainer("database", "image")
.WithEndpoint(name: "tcp", targetPort: 5432, isProxied: false);

database.WithEnvironment("PUBLIC_PORT", database.GetEndpoint("tcp").Property(EndpointProperty.Port));
```

</TabItem>
<TabItem id="typescript" label="TypeScript">

```typescript title="apphost.mts" twoslash
import { EndpointProperty, createBuilder } from './.aspire/modules/aspire.mjs';

const builder = await createBuilder();

const database = await builder.addContainer('database', {
image: 'image',
tag: 'latest',
});
await database.withEndpoint({ name: 'tcp', targetPort: 5432, isProxied: false });

await database.withEnvironment('PUBLIC_PORT', database.getEndpoint('tcp').property(EndpointProperty.Port));
```

</TabItem>
</Tabs>

In this example, `PUBLIC_PORT` is set to the endpoint's allocated public host port before the container is created. Because the endpoint is proxyless and doesn't specify `port`, Aspire allocates a port from the proxyless endpoint port range.

:::note
For persistent resources, Aspire stores allocated proxyless endpoint ports in user secrets when user secrets are available and reuses them on later AppHost runs. If Aspire can't persist the allocated port, configure a fixed public `port` or use a proxied endpoint to avoid recreating the persistent resource on each run.
:::

## Omit the host port

For proxied endpoints, when you omit the host port, Aspire generates a random port for both host and service port. This is useful when you want to avoid port conflicts and don't care about the host or service port. Consider the following code:
Expand Down
3 changes: 3 additions & 0 deletions src/frontend/src/content/docs/whats-new/aspire-13-5.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ This release introduces:
- **VS Code extension renamed to "Aspire"** and rebranded for clarity on the Marketplace.
- **AppHost discovery efficiency** in VS Code respects exclusion settings and debounces file changes to reduce background scanning.
- **Foundry Local integration CLI update** now uses the foundry CLI for lifecycle management, requiring foundry 1.1.0+.
- **Proxyless endpoint port allocation** assigns dynamic public host ports before resources are created, so endpoint property references resolve consistently.
- …and much more.

## 🆙 Upgrade to Aspire 13.5
Expand Down Expand Up @@ -200,6 +201,8 @@ The following breaking changes are included in Aspire 13.5:

4. **GitHub Models integration deprecated**: The GitHub Models service is no longer available to new customers, so the `Aspire.Hosting.GitHub.Models` integration is sunset as of Aspire 13.5. All public APIs are marked `[Obsolete]`, and the package no longer appears in `aspire add` output. One final obsolete release will ship on NuGet, and the package will be removed entirely in a future version. Migrate to the [Azure AI Foundry integration](/integrations/cloud/azure/azure-ai-foundry/azure-ai-foundry-get-started/) instead. See [microsoft/aspire#18402](https://github.com/microsoft/aspire/issues/18402) for details.

5. **Proxyless endpoint port allocation timing changed**: Proxyless endpoints without an explicit public `port` now receive one during service preparation, before workload resources are created. Executable proxyless endpoints that previously failed without a public port, and container proxyless endpoints that expected the public port to be assigned later during container startup, should expect Aspire to assign the port earlier. The default allocation range is `10000-32767` and can be overridden with `ASPIRE_PROXYLESS_ENDPOINT_PORT_RANGE=start-end`. Persistent resources reuse allocated ports from user secrets when available.

<Aside type="caution" title="Review deprecated APIs">
Review your code for uses of `ServiceProvider` on hosting context types and `PublishAsConnectionString` extension methods. These will generate compiler warnings in Aspire 13.5 and may become harder errors in future releases. If you use the `Aspire.Hosting.GitHub.Models` integration, plan to migrate off it before the package is removed.
</Aside>
Expand Down
Loading