Skip to content

Commit 57bb2b6

Browse files
committed
Update README.md to include Coder workspace template details and architecture
1 parent b470408 commit 57bb2b6

1 file changed

Lines changed: 182 additions & 0 deletions

File tree

README.md

Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@ for Claude Code and friends, plus a lightweight `nvm` feature.
55

66
Published to GHCR under the `sourecode/devcontainer-features` namespace.
77

8+
This repo also contains the Coder workspace template
9+
([`Dockerfile.workspace`](Dockerfile.workspace) and [`main.tf`](main.tf))
10+
that hosts the devcontainers these features get installed into — see
11+
[Coder workspace template](#coder-workspace-template).
12+
813
## Features
914

1015
| Feature | OCI reference | Summary |
@@ -74,6 +79,181 @@ Claude login (`~/.claude/.credentials.json`) and chat history (`projects/`,
7479
`$HOME` as a named volume — see [`docs/persistence.md`](docs/persistence.md)
7580
for the shared-home pattern we use across every devcontainer.
7681

82+
## Coder workspace template
83+
84+
`Dockerfile.workspace` builds a Coder workspace image and `main.tf` is the
85+
Coder template that launches it. The workspace container runs its own
86+
`dockerd` under the [sysbox](https://github.com/nestybox/sysbox) runtime
87+
(`runtime = "sysbox-runc"` in `main.tf`), so `@devcontainers/cli` inside the
88+
workspace talks to a local daemon and bind-mount paths resolve against the
89+
same filesystem the daemon sees.
90+
91+
### Architecture
92+
93+
```
94+
host docker daemon
95+
└── workspace container (sourecode/coder-workspace:latest, runtime=sysbox-runc)
96+
├── systemd (PID 1)
97+
├── dockerd
98+
├── coder-agent.service (main agent)
99+
└── @devcontainers/cli up
100+
└── devcontainer container(s) (compose, features, lifecycle all work)
101+
└── coder sub-agent (runs code-server / jetbrains here)
102+
```
103+
104+
Editors run **inside the devcontainer** via Coder's Dev Containers integration
105+
(`coder_devcontainer.subagent_id`). When you click "Open in code-server" in the
106+
Coder UI, you land in the devcontainer's filesystem with its tools, not the
107+
outer workspace.
108+
109+
One extra container over plain Coder workspaces. No DooD path translation.
110+
111+
### Template files
112+
113+
- `Dockerfile.workspace` — builds the workspace image. Ubuntu + systemd +
114+
docker-ce + Node LTS + `@devcontainers/cli` + a `coder` user at UID 1000.
115+
- `main.tf` — Coder template. Launches the workspace container under
116+
`runtime = "sysbox-runc"`, injects `CODER_AGENT_TOKEN` via env, and uploads
117+
the agent init script to `/etc/coder/agent-init.sh`. The
118+
`coder-agent.service` systemd unit (baked into the image, see
119+
`Dockerfile.workspace`) runs that script on boot.
120+
121+
### Prerequisites (on the Docker host)
122+
123+
1. Linux kernel >= 5.12 (>= 6.3 ideal, avoids shiftfs entirely)
124+
2. Native Docker (not the snap) at `/usr/bin/docker`
125+
3. Sysbox installed (see below)
126+
4. Your existing Coder server (this template was developed against a
127+
docker-compose-deployed Coder)
128+
129+
### Install sysbox
130+
131+
Zero-container-deletion install, tolerates a single `dockerd` restart.
132+
133+
```bash
134+
# 1. pre-populate /etc/docker/daemon.json so sysbox's post-install step
135+
# doesn't need to touch the network config itself
136+
sudo tee /etc/docker/daemon.json >/dev/null <<'JSON'
137+
{
138+
"bip": "172.24.0.1/16",
139+
"default-address-pools": [
140+
{ "base": "172.31.0.0/16", "size": 24 }
141+
]
142+
}
143+
JSON
144+
145+
# Pick CIDRs free of your existing networks:
146+
# docker network inspect $(docker network ls -q) | grep -i subnet
147+
148+
# 2. one controlled restart so dockerd loads the keys
149+
sudo systemctl restart docker
150+
151+
# 3. install sysbox (Ubuntu/Debian amd64)
152+
wget https://downloads.nestybox.com/sysbox/releases/v0.7.0/sysbox-ce_0.7.0-0.linux_amd64.deb
153+
sudo apt-get install -y jq fuse3 ./sysbox-ce_0.7.0-0.linux_amd64.deb
154+
155+
# 4. verify
156+
docker info | grep -i runtime # should list sysbox-runc
157+
systemctl status sysbox --no-pager
158+
```
159+
160+
Smoke test that nested Docker works under sysbox:
161+
162+
```bash
163+
CID=$(docker run -d --rm --runtime=sysbox-runc nestybox/ubuntu-noble-systemd-docker)
164+
sleep 15
165+
docker exec "$CID" docker run --rm hello-world # should print the hello-world greeting
166+
docker stop "$CID"
167+
```
168+
169+
### Build the workspace image
170+
171+
```bash
172+
docker build -f Dockerfile.workspace -t sourecode/coder-workspace:latest .
173+
```
174+
175+
The image must exist in the host's local image store (or in a registry the
176+
host can pull from). It is referenced by `var.workspace_image` in `main.tf`.
177+
178+
### Push the template to Coder
179+
180+
If your Coder runs inside a docker-compose stack and you prefer not to install
181+
`coder` on the host, use the CLI that's baked into the Coder server image:
182+
183+
```bash
184+
# copy the template files into the Coder container
185+
docker exec coder-coder-1 mkdir -p /tmp/tpl
186+
docker cp ./main.tf coder-coder-1:/tmp/tpl/main.tf
187+
docker cp ./Dockerfile.workspace coder-coder-1:/tmp/tpl/Dockerfile.workspace
188+
189+
# login once
190+
docker exec -it coder-coder-1 /opt/coder login http://localhost:7080
191+
192+
# push
193+
docker exec -it coder-coder-1 /opt/coder templates push coder-template -d /tmp/tpl --yes
194+
```
195+
196+
Or install the `coder` CLI locally and push from the repo dir directly.
197+
198+
#### Important: template variables
199+
200+
If you push a newer version of the template but workspaces still launch from
201+
an old image, check **Templates → Settings → Variables** in the Coder UI.
202+
A persisted override for `workspace_image` wins over the `default` in `main.tf`
203+
across version bumps. Clear it or set it to `sourecode/coder-workspace:latest`.
204+
205+
### Create / update workspaces
206+
207+
A workspace pinned to an older template version does not auto-upgrade. After
208+
pushing a new version, either:
209+
210+
- Click **Update** on the workspace in the UI, or
211+
- `coder update <workspace-name>` (from wherever you have the CLI)
212+
213+
### Troubleshooting
214+
215+
- **"Agent is taking longer than expected to connect"** — the workspace
216+
container exited instead of running systemd. Check:
217+
```bash
218+
CID=$(docker ps -a --filter "name=coder-" -q | head -1)
219+
docker inspect "$CID" --format '{{.HostConfig.Runtime}} {{.Config.Image}} {{.State.Status}}'
220+
docker logs "$CID" | tail -50
221+
```
222+
Runtime must be `sysbox-runc` (hardcoded in `main.tf`); image should match
223+
whatever `var.workspace_image` resolves to (default
224+
`sourecode/coder-workspace:latest`). If either is wrong, fix the template /
225+
variable override and recreate.
226+
227+
- **Agent up but nothing connects** — inspect systemd and the agent unit:
228+
```bash
229+
docker exec "$CID" systemctl is-system-running
230+
docker exec "$CID" systemctl status docker coder-agent --no-pager
231+
docker exec "$CID" journalctl -u coder-agent --no-pager -n 100
232+
docker exec "$CID" ls -la /etc/coder/ # expect agent-init.sh present and executable
233+
docker exec "$CID" bash -lc "tr '\0' '\n' < /proc/1/environ | grep CODER_AGENT_TOKEN"
234+
```
235+
236+
- **Inner `@devcontainers/cli up` hangs / errors** — run it by hand inside the
237+
workspace as the `coder` user to see the real output:
238+
```bash
239+
docker exec -u coder -it "$CID" bash -lc "cd ~/<repo> && devcontainer up --workspace-folder ."
240+
```
241+
242+
### Why this shape
243+
244+
`@devcontainers/cli` assumes the CLI and the Docker daemon share a filesystem.
245+
When Coder launches a workspace container and mounts the host socket in, the
246+
CLI (inside the workspace) and the daemon (on the host) see different
247+
filesystems — every bind-mount path the CLI emits is wrong from the daemon's
248+
point of view. That's the root cause of the
249+
`bind source path does not exist` failures.
250+
251+
The principled fixes are: path alignment (fragile), DinD (insecure),
252+
sysbox (safe DinD), envbuilder (collapse into one container), or CI pre-build
253+
(move work out of runtime). This template uses sysbox so the workspace's own
254+
`dockerd` runs safely inside it, paths resolve naturally, and compose-based
255+
devcontainers work unchanged.
256+
77257
## Developing on this repo
78258

79259
### Repository layout
@@ -97,6 +277,8 @@ src/
97277
docs/
98278
migration-guide.md
99279
persistence.md
280+
Dockerfile.workspace # Coder workspace image (Ubuntu + systemd + dockerd + @devcontainers/cli)
281+
main.tf # Coder template that launches the workspace under sysbox-runc
100282
```
101283

102284
Each feature directory follows the [Dev Container Features spec](https://containers.dev/implementors/features/):

0 commit comments

Comments
 (0)