microsoft / microsoft/aspire

Docker Compose destroy requires `ComposeFilePath` in deployment state even though teardown uses only project name

Open
#17,081 3 comments 0 reactions 0 assignees View on GitHub
area-deployment triage:bot-seen
Dominant language
C#
Stars
6.3k
Forks
991
Avg merge
2d 15h
Merged PRs (30d)
196

Description

## Summary

The Docker Compose destroy path currently refuses to run if `ComposeFilePath` is missing from deployment state, even though the actual teardown path uses project-name-only mode and does not pass the compose file to `docker compose down`.

This makes `aspire destroy` more fragile than necessary for Docker Compose environments and can lead to a no-op destroy with:

> No Docker Compose deployment state found for 'docker-compose'. Nothing to destroy.

## Why this looks wrong from the code

In `src/Aspire.Hosting.Docker/DockerComposeEnvironmentResource.cs`, destroy does this:

- acquires `DockerCompose:{Name}` from deployment state
- reads `savedComposeFilePath = stateSection.Data["ComposeFilePath"]?.ToString()`
- returns early with "Nothing to destroy" if that value is null or empty

But when destroy does proceed, it builds the compose context like this:

- `ProjectName = savedProjectName`
- `WorkingDirectory = savedOutputPath`

It does **not** set `ComposeFilePath` for teardown.

That matches `ContainerRuntimeBase.ComposeDownAsync(...)`, which can already run in project-name-only mode through `BuildComposeArguments(...)`.

## Expected behavior

If Aspire has enough saved state to identify the compose project, destroy should attempt teardown instead of requiring `ComposeFilePath` specifically.

At minimum, if `ProjectName` is present, I would expect destroy to try `docker compose --project-name down`.

## Related diagnostic gap

The same local deployment state file can exist for other reasons, such as saved parameter values, even when the Docker Compose section is absent or incomplete.

That makes the current message a bit misleading, because there may be local deployment state, just not the specific `ComposeFilePath` field that this path requires.

Also, the top-level destroy step clears the whole deployment state file afterward, which makes postmortem diagnosis harder.

## Source evidence

Docker Compose state is persisted only after a successful `ComposeUpAsync(...)`:

- `src/Aspire.Hosting.Docker/DockerComposeEnvironmentResource.cs`
- `stateSection.Data["OutputPath"] = outputPath;`
- `stateSection.Data["ProjectName"] = composeContext.ProjectName;`
- `stateSection.Data["ComposeFilePath"] = composeContext.ComposeFilePath;`

Destroy currently gates on `ComposeFilePath`:

- `src/Aspire.Hosting.Docker/DockerComposeEnvironmentResource.cs`
- `var savedComposeFilePath = stateSection.Data["ComposeFilePath"]?.ToString();`
- early return when it is null or empty

Destroy then tears down using project-name-only mode:

- `var composeContext = new ComposeOperationContext { ProjectName = savedProjectName, WorkingDirectory = savedOutputPath };`

`ContainerRuntimeBase.BuildComposeArguments(...)` already supports no compose file:

- `compose --project-name "{context.ProjectName}"` when `ComposeFilePath` is null

## Scenario that led me here

I ran into this while debugging a failed Docker Compose deployment and later got `No Docker Compose deployment state found ... Nothing to destroy`, even though compose-managed resources from the same project name still existed.

I cannot prove from the outside exactly which state transition dropped the field, but the current gate on `ComposeFilePath` looks stricter than the actual teardown path requires.

From my general understanding, then the flow that makes this an issue is:
`aspire deploy` -> *one or more* services does not come up -> deploy state is not written -> user tries to remove the failed deploy -> deploy cant be found as the deploy state is missing

Contributor guide

Open the contributing guide

Research direction

Start in src/Aspire.Hosting.Docker/DockerComposeEnvironmentResource.cs by tracing the destroy state lookup and its ComposeFilePath guard, then read ContainerRuntimeBase.BuildComposeArguments(...) for project-name-only behavior. Done means destroy attempts teardown when ProjectName is available without requiring ComposeFilePath, while preserving the existing deployment-state handling.

Written by the indexing model from the issue text.

Assessment

Tech stack
csharp, docker-compose
Domain
devops, infrastructure
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
52/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.