Description
Problem
Currently, the containers and devcontainers API endpoints are split and the UI only renders "containers" filtered on certain labels. This is not ideal from a managing of devcontainers standpoint as the devcontainer would disappear if the underlying container is removed.
Proposal (new combined format):
To fix this, we can update the containers endpoint to list devcontainers as well making the data available in the UI.
GET /api/v0/containers
:
With this unified endpoint, we can still support old container access methods as well as opt to render .devcontainers
in the UI rather than .containers
filtered on labels. We can also remove the devcontainer_dirty
and devcontainer_status
fields from .containers
as those were added as a stop-gap to show devcontainer state.
Why we want this?
We need a way to represent devcontainers even when there is no container running, or they are in an error state. For example, during recreation of a devcontainer, the devcontainer may be temporarily missing from the UI, or in the event of an error, completely disappear and require manual intervetion.
UI impact
Consider the existing UI:

Notice the dev container being represented as dev container: inspiring_curran
.
This has two problems when taking into account sub agents in dev containers (#621).
- The agent name keeps changing on container recreation, breaking existing URLs and connection attempts (e.g.
coder ssh workspace.<random_words>
) - The docker container name is not a valid agent name (underscores
_
are not permitted)
As can be observed from the /api/v0/devcontainers
endpoint below, dev containers already have a name (ideally) either defined via devcontainer.json
.customize
field (TODO create issue) or via Terraform resource if used.
Ideally this dev container should be rendered as dev container: coder (inspiring_curran)
or similar. Likewise its status
should be properly represented (running, starting, stopped, error).
Definition of done
- Merge
GET /api/v0/containers
andGET /api/v0/containers/devcontainers
underGET /api/v0/containers
- Remove
.devcontainer_dirty
and.devcontainer_status
fields from.container
entries - Render dev containers from the newly added
.devcontainers
field instead of.containers
- Show
.name
and.container.name
in the format proposed above - Keep logic for "dirty", but use
.dirty
from devcontainer (instead ofcontainer.devcontainer_dirty
) - Represent devcontainer
.status
in the UI - The devcontainers are always rendered, even if the container is stopped
For reference, the existing endpoints render the following content:
GET /api/v0/containers
:
{
"containers": [
{
"created_at": "2025-06-03T12:12:04.213068588Z",
"id": "42b1be73cdcc818c0d9f6afefae5569fb82cb56fa9b9df8efab7a43bb352d1f0",
"name": "gifted-bouman",
"image": "vsc-coder-16501171b99410a3c45670c2393882c63fca0ca319d89a5c3f48672fec889697-features-uid",
"labels": {
"devcontainer.config_file": "/home/coder/coder/.devcontainer/devcontainer.json",
"devcontainer.local_folder": "/home/coder/coder",
"devcontainer.metadata": "[ {\"id\":\"ghcr.io/devcontainers/features/docker-in-docker:2\",\"privileged\":true,\"entrypoint\":\"/usr/local/share/docker-init.sh\",\"mounts\":[{\"source\":\"dind-var-lib-docker-${devcontainerId}\",\"target\":\"/var/lib/docker\",\"type\":\"volume\"}],\"customizations\":{\"vscode\":{\"extensions\":[\"ms-azuretools.vscode-docker\"],\"settings\":{\"github.copilot.chat.codeGeneration.instructions\":[{\"text\":\"This dev container includes the Docker CLI (`docker`) pre-installed and available on the `PATH` for running and managing containers using a dedicated Docker daemon running inside the dev container.\"}]}}}}, {\"customizations\":{\"vscode\":{\"extensions\":[\"biomejs.biome\"]}}} ]",
"org.opencontainers.image.ref.name": "ubuntu",
"org.opencontainers.image.version": "22.04"
},
"running": true,
"ports": [],
"status": "running",
"volumes": {
"/home/coder/coder": "/workspaces/coder", "/var/lib/docker/volumes/dind-var-lib-docker-188djrll4q61ascc3gh2s0phshvifcn964vkc68dp1l7v7eqqjtn/_data": "/var/lib/docker" },
"devcontainer_status": "running",
"devcontainer_dirty": false
}
]
}
GET /api/v0/containers/devcontainers
:
{
"devcontainers": [
{
"id": "3c8cfbac-aff2-400b-86d5-0e51036fda80",
"name": "coder",
"workspace_folder": "/home/coder/coder",
"config_path": "/home/coder/coder/.devcontainer/devcontainer.json",
"status": "running",
"dirty": false,
"container": {
"created_at": "2025-06-03T12:12:04.213068588Z",
"id": "42b1be73cdcc818c0d9f6afefae5569fb82cb56fa9b9df8efab7a43bb352d1f0",
"name": "gifted_bouman",
"image": "vsc-coder-16501171b99410a3c45670c2393882c63fca0ca319d89a5c3f48672fec889697-features-uid",
"labels": {
"devcontainer.config_file": "/home/coder/coder/.devcontainer/devcontainer.json",
"devcontainer.local_folder": "/home/coder/coder",
"devcontainer.metadata": "[ {\"id\":\"ghcr.io/devcontainers/features/docker-in-docker:2\",\"privileged\":true,\"entrypoint\":\"/usr/local/share/docker-init.sh\",\"mounts\":[{\"source\":\"dind-var-lib-docker-${devcontainerId}\",\"target\":\"/var/lib/docker\",\"type\":\"volume\"}],\"customizations\":{\"vscode\":{\"extensions\":[\"ms-azuretools.vscode-docker\"],\"settings\":{\"github.copilot.chat.codeGeneration.instructions\":[{\"text\":\"This dev container includes the Docker CLI (`docker`) pre-installed and available on the `PATH` for running and managing containers using a dedicated Docker daemon running inside the dev container.\"}]}}}}, {\"customizations\":{\"vscode\":{\"extensions\":[\"biomejs.biome\"]}}} ]",
"org.opencontainers.image.ref.name": "ubuntu",
"org.opencontainers.image.version": "22.04"
},
"running": true,
"ports": [],
"status": "running",
"volumes": {
"/home/coder/coder": "/workspaces/coder",
"/var/lib/docker/volumes/dind-var-lib-docker-188djrll4q61ascc3gh2s0phshvifcn964vkc68dp1l7v7eqqjtn/_data": "/var/lib/docker"
},
"devcontainer_status": "running",
"devcontainer_dirty": false
}
}
]
}