microsoft / microsoft/aspire

Volume sharing semantics are inconsistent across run mode and compute environments

Open
#19,697 1 comment 0 reactions 0 assignees View on GitHub
area-app-model area-deployment triage:bot-seen
Dominant language
C#
Stars
6.3k
Forks
991
Avg merge
2d 15h
Merged PRs (30d)
196

Description

### Is there an existing issue for this?

- [X] I have searched the existing issues

### Is your feature request related to a problem? Please describe the problem.

A named volume does not mean the same thing across targets. Given two resources that each declare the same volume name:

```csharp
builder.AddContainer("a", "img").WithVolume("data", "/data");
builder.AddProject("b").WithVolume("data", "/data", env: "DATA_PATH");
```

the sharing behavior differs by target:

| Target | Result | Where |
|---|---|---|
| Run mode — containers | **shared** | literal name flows into `ContainerMountAnnotation.Source` and becomes the Docker named volume |
| Run mode — projects/executables | isolated | `VolumeMountPathResolver.GetLocalPath` scopes by `hash(resource.Name)` |
| Docker Compose | **shared** | `DockerComposeEnvironmentContext` uses `mount.Source` verbatim as the Compose volume name |
| Azure Container Apps | isolated | volume named `$"{prefix}_{normalizedResourceName}_{volumeIndex}"` |
| Kubernetes | isolated | default storage type is `emptyDir`, which is per-pod |

So the same AppHost code shares storage on Compose and in run-mode containers, and isolates it on ACA, Kubernetes, and for run-mode projects and executables.

Note this is not new — run-mode containers have shared by literal name for a long time. `VolumeNameGenerator` exists precisely because raw names are not auto-scoped, so integrations (`WithDataVolume()`) generate `{appname}-{apphostHash}-{resourceName}-{suffix}` to avoid collisions. It only became visible because #19404 added a project/executable path that deliberately isolates.

Discovered during review of #19404 (thanks @JamesNK).

### Describe the solution you'd like

No immediate change. #19404 keeps projects and executables isolated, which is the safe default for the portable volume path convention: it guarantees that resolving a volume path never silently hands two workloads the same directory.

What needs deciding is the model, not the implementation:

**Docker Compose is arguably the odd one out rather than the reference.** Compose almost always schedules on a single node, so "named volume = shared mount" is nearly free. On ACA and Kubernetes, shared volume semantics get considerably more complicated — you are into access modes (`ReadWriteOnce` vs `ReadWriteMany`), storage classes, node affinity, and whether the backing store can even support concurrent writers. Treating a bare name as an implicit sharing contract does not survive that translation.

This likely points at a missing primitive: sharing should be something an AppHost author **declares explicitly**, rather than something inferred from two resources coincidentally passing the same string. A first-class volume resource, referenced by multiple workloads, would let each compute environment translate a stated sharing requirement into whatever its platform actually supports — and fail loudly when it cannot.

The hard part, and the reason this is not solvable today: **there is no story for sharing a volume between an executable/project and a container within the same AppHost.** The container sees a mount inside its own filesystem namespace; the host process sees a local directory. Making those the same storage requires bind-mount plumbing and path translation that does not exist yet, and the semantics would still not survive being published to ACA or Kubernetes.

Rough shape of the work, for discussion:

- Decide whether a bare volume name should imply sharing at all, or whether sharing must be explicit.
- Consider a first-class volume resource that workloads reference, so sharing is declared rather than inferred.
- Define what each compute environment does when asked for sharing it cannot provide — most likely a publish-time error rather than silently degrading to isolated storage.
- Separately work out host-process ↔ container sharing in run mode, which is the blocker for making any of the above consistent in the inner loop.

### Additional context

Relevant code:

- `src/Shared/VolumeMountPathResolver.cs` — `GetLocalPath` scopes by resource name; the comment there already acknowledges that "generic target publishers do not share volume identity consistently".
- `src/Aspire.Hosting.Docker/DockerComposeEnvironmentContext.cs` — `var name = mount.Source;`
- `src/Aspire.Hosting.Azure.AppContainers/AzureContainerAppEnvironmentResource.cs` — `$"{prefix}_{normalizedResourceName}_{volumeIndex}"`
- `src/Aspire.Hosting.Kubernetes/KubernetesEnvironmentResource.cs` — `DefaultStorageType = "emptyDir"`
- `src/Aspire.Hosting/VolumeNameGenerator.cs` — the existing per-resource scoping helper used by integrations

Contributor guide

Open the contributing guide

Research direction

Read the volume behavior in src/Shared/VolumeMountPathResolver.cs, src/Aspire.Hosting.Docker/DockerComposeEnvironmentContext.cs, src/Aspire.Hosting.Azure.AppContainers/AzureContainerAppEnvironmentResource.cs, src/Aspire.Hosting.Kubernetes/KubernetesEnvironmentResource.cs, and src/Aspire.Hosting/VolumeNameGenerator.cs. Compare how each environment handles shared names and review the discussion around #19404. Done means an agreed sharing model and explicit behavior for unsupported environments, not an implementation yet.

Written by the indexing model from the issue text.

Assessment

Tech stack
azure, csharp, docker-compose, kubernetes
Domain
cloud, infrastructure
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Needs clarification
Newbie friendliness
30/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.