-
Notifications
You must be signed in to change notification settings - Fork 0
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/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
}
}
]
}
{ "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. } ]