Amakuru.net

A reproducible Claude Code dev container

A Debian-based Docker image with every tool I need pinned to a specific version, the host .claude config mounted read-only and bootstrapped at startup, and UID/GID matching for clean volume mounts.

Running Claude Code directly on the host means it accumulates state, drifts from the toolchain that’s actually deployed in production, and occasionally touches things outside the working directory. Running it in a fresh container every time gives reproducibility but trades it for redoing apt install and pinning every CLI by hand at the start of each session. claude-docker is the middle ground: a pinned, prebuilt image that boots into a known state.

Pin everything

Python 3.12.9 via uv. Node 24. Claude Code 2.1.81. Terraform 1.14.7 via tfenv. kubectl, Helm, glab, gh, ruff, ripgrep — all at specific versions. The reason isn’t dogma; it’s that “latest” doesn’t reproduce in six months. If something works today, the only way to make it work the same way next March is to pin it now.

Read-only host mount, then bootstrap

The host’s ~/.claude is mounted read-only into the container, then bootstrapped at startup into a writable location inside. The read-only mount prevents a session inside the container from corrupting the host config — writing through a mounted symlink is a quick way to lose CLAUDE.md.

Match the host UID on macOS

Docker on macOS does volume mounts via the VM’s filesystem layer. If the container user’s UID doesn’t match the host user’s UID, every file written via a mount comes out owned by the wrong user. The build supports USER_UID=$(id -u) USER_GID=$(id -g) docker compose build so the container user matches the host user.

It’s a five-line addition that saves the recurring “why are my files owned by 1000?” debugging session.

Was it worth it?

The same image covers two use cases I have. On a locked-down client machine, it gives a consistent dev environment that behaves identically every session. On a personal machine, it’s a sandbox that doesn’t reach into the host. Either way, the toolchain is the one I picked, at the version I picked, until I rebuild.

dmorel69/claude-docker — Debian Trixie, Docker