- Notifications
You must be signed in to change notification settings - Fork0
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:
{"devcontainers": [ {// Same fields as before. The container data will be duplicated here as there will also be an entry in "containers". } ],"containers": [ {// Same fields as before, but remove `devcontainer_*` fields. } ]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 thedevcontainer_dirty anddevcontainer_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 asdev 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 viadevcontainer.json.customize field (TODO create issue) or via Terraform resource if used.
Ideally this dev container should be rendered asdev container: coder (inspiring_curran) or similar. Likewise itsstatus should be properly represented (running, starting, stopped, error).
Definition of done
- Merge
GET /api/v0/containersandGET /api/v0/containers/devcontainersunderGET /api/v0/containers - Remove
.devcontainer_dirtyand.devcontainer_statusfields from.containerentries - Render dev containers from the newly added
.devcontainersfield instead of.containers - Show
.nameand.container.namein the format proposed above - Keep logic for "dirty", but use
.dirtyfrom devcontainer (instead ofcontainer.devcontainer_dirty) - Represent devcontainer
.statusin 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 } } ]}