Skip to content

Commit 0cc9fde

Browse files
committed
Add rootless Docker (docker-in-docker) to fx image
Install rootless extras (docker-ce-rootless-extras, uidmap, dbus-user-session, fuse-overlayfs, slirp4netns) on top of the Docker CE already in the base, preset DOCKER_HOST/XDG_RUNTIME_DIR, and ship a dockerd-rootless-start helper to bring up the daemon as the coder user.
1 parent 45d7b9d commit 0cc9fde

3 files changed

Lines changed: 66 additions & 0 deletions

File tree

README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,18 @@ So you’re replacing the plain enterprise base with the same foundation plus ou
4747
- **Claude** and **OpenCode** installers (best-effort copy to `/usr/local/bin` when present).
4848
- **npm globals:** `@openai/codex`, `@github/copilot`.
4949
- **Chrome libraries** plus **agent-browser** (installs its Chromium).
50+
- **Rootless Docker** (docker-in-docker): Docker CE from the base plus rootless extras (`uidmap`, `fuse-overlayfs`, `slirp4netns`). Run `dockerd-rootless-start` to bring up the daemon as the `coder` user.
51+
52+
### Rootless Docker
53+
54+
The image can run Docker-in-Docker without privileged root. Start the daemon inside the workspace:
55+
56+
```bash
57+
dockerd-rootless-start
58+
docker run --rm hello-world
59+
```
60+
61+
`DOCKER_HOST` and `XDG_RUNTIME_DIR` are preset for login shells, so the CLI talks to the rootless daemon automatically. The host must permit nested user namespaces — in a Coder Terraform template, run the workspace container with `privileged = true` (or the equivalent `--userns` setup), and call `dockerd-rootless-start` from `startup_script` to have Docker ready on boot.
5062

5163
## Development
5264

fx/Dockerfile

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,15 @@ ENV GOROOT=/usr/local/go
88
ENV GOPATH=/home/coder/go
99
ENV PATH=$GOROOT/bin:$GOPATH/bin:$PATH
1010

11+
# Rootless Docker (docker-in-docker without privileged root)
12+
ENV XDG_RUNTIME_DIR=/run/user/1000
13+
ENV DOCKER_HOST=unix:///run/user/1000/docker.sock
14+
1115
USER root
1216

17+
# Helper to bring up the rootless Docker daemon at workspace start
18+
COPY --chmod=0755 dockerd-rootless-start.sh /usr/local/bin/dockerd-rootless-start
19+
1320
RUN \
1421
# REPOS ####################################################################
1522
# Add all package repos first, then do a single apt-get update
@@ -39,6 +46,8 @@ RUN \
3946
php8.5-zip php8.5-bcmath php8.5-apcu php8.5-imagick \
4047
# Python
4148
python3 python3-pip python3-venv \
49+
# Rootless Docker (docker-ce is already in the base image)
50+
docker-ce-rootless-extras uidmap dbus-user-session fuse-overlayfs slirp4netns \
4251
# GitHub CLI
4352
gh \
4453
# Chrome libs (agent-browser)
@@ -90,6 +99,10 @@ RUN \
9099
echo 'export BUN_INSTALL="${BUN_INSTALL:-/usr/local}"' >> /etc/profile && \
91100
echo '[ -d "$DENO_INSTALL/bin" ] && export PATH="$DENO_INSTALL/bin:$PATH"' >> /etc/profile && \
92101
echo '[ -d "$BUN_INSTALL/bin" ] && export PATH="$BUN_INSTALL/bin:$PATH"' >> /etc/profile && \
102+
echo '' >> /etc/profile && \
103+
echo '# Rootless Docker' >> /etc/profile && \
104+
echo 'export XDG_RUNTIME_DIR="${XDG_RUNTIME_DIR:-/run/user/$(id -u)}"' >> /etc/profile && \
105+
echo 'export DOCKER_HOST="unix://${XDG_RUNTIME_DIR}/docker.sock"' >> /etc/profile && \
93106
# OWNERSHIP (coder can upgrade tools) ######################################
94107
chown -R coder:coder /home/coder/.local /home/coder/.opencode /home/coder/go 2>/dev/null || true && \
95108
# CLEANUP ###################################################################

fx/dockerd-rootless-start.sh

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
#!/usr/bin/env bash
2+
# Start the rootless Docker daemon for the current (non-root) user.
3+
#
4+
# Docker CE is already present in the image; this brings up dockerd in
5+
# rootless mode so workspaces can run Docker-in-Docker without privileged
6+
# root. Run it once after the container/workspace starts:
7+
#
8+
# dockerd-rootless-start
9+
# docker run --rm hello-world
10+
#
11+
# Note: the host must allow user namespaces. In Coder, run the container
12+
# with --userns-host or the equivalent template option.
13+
set -euo pipefail
14+
15+
uid="$(id -u)"
16+
export XDG_RUNTIME_DIR="${XDG_RUNTIME_DIR:-/run/user/${uid}}"
17+
export DOCKER_HOST="unix://${XDG_RUNTIME_DIR}/docker.sock"
18+
19+
# /run/user/<uid> lives on tmpfs and is recreated on every container start.
20+
if [ ! -d "$XDG_RUNTIME_DIR" ]; then
21+
sudo install -d -m 0700 -o "$uid" -g "$(id -g)" "$XDG_RUNTIME_DIR"
22+
fi
23+
24+
if docker info >/dev/null 2>&1; then
25+
echo "rootless dockerd already running at ${DOCKER_HOST}"
26+
exit 0
27+
fi
28+
29+
echo "starting rootless dockerd ..."
30+
nohup dockerd-rootless.sh >/tmp/dockerd-rootless.log 2>&1 &
31+
32+
for _ in $(seq 1 30); do
33+
if docker info >/dev/null 2>&1; then
34+
echo "rootless dockerd ready at ${DOCKER_HOST}"
35+
exit 0
36+
fi
37+
sleep 1
38+
done
39+
40+
echo "dockerd did not become ready in time; see /tmp/dockerd-rootless.log" >&2
41+
exit 1

0 commit comments

Comments
 (0)