No description
  • Shell 59%
  • Dockerfile 41%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Robert Butzhammer 01e627a57a ci: verify kaniko works on all runners
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-08-08 02:17:58 +02:00
.gitea/workflows ci: switch to kaniko — builds without Docker daemon 2026-08-08 01:55:56 +02:00
.env.dev.example Init 2026-07-26 12:26:06 +02:00
.gitignore Init 2026-07-25 07:15:26 +02:00
docker-compose.yml Switch run.sh from docker compose to a direct docker run 2026-08-03 16:53:34 +02:00
docker-entrypoint.sh fix: replace sudo with gosu for rootless Docker compatibility 2026-08-07 21:42:22 +02:00
Dockerfile fix: use chmod 755 so dev user can read entrypoint script after gosu re-exec 2026-08-08 00:26:27 +02:00
README.md Switch run.sh from docker compose to a direct docker run 2026-08-03 16:53:34 +02:00
run-repo.sh Init 2026-07-26 12:26:06 +02:00
run.sh Switch run.sh from docker compose to a direct docker run 2026-08-03 16:53:34 +02:00
setup-claude.sh Init 2026-07-26 12:26:06 +02:00

claude-devcontainer

A Docker image for running Claude Code in an isolated, disposable container. It ships Node 22, the .NET 10 SDK, dotnet-ef, the GitHub CLI, postgresql-client, and Playwright with Chromium/Firefox pre-installed — a non-root dev user runs everything so claude --dangerously-skip-permissions is safe to use.

The image is built and pushed to git.butzei.de/robert/claude-devcontainer on every push to master (see .gitea/workflows/build.yml).

Prerequisites

  • Docker (rootful or rootless)
  • ~/.claude and ~/.claude.json — your existing Claude Code login/config on the host (bind-mounted in, so auth carries over)
  • ~/.git-credentials — for git push access from inside the container (git config --global credential.helper store on the host populates this)
  • ~/.config/gh — for gh CLI auth, if you use it

Setup

Copy the example env file and adjust it for your setup:

cp .env.dev.example .env.dev

Key variables (see comments in .env.dev.example for details):

  • DOCKER_SOCKET — path to the Docker socket, for Testcontainers / Docker-in-Docker. Defaults to /var/run/docker.sock; rootless Docker users should point this at /run/user/<uid>/docker.sock.
  • DOCKER_GID — GID of the host docker group, only needed on standard (non-rootless) Docker so the container's dev user can reach the socket.
  • DEV_UID / DEV_GID — override if your host user isn't UID/GID 1000.
  • ANTHROPIC_API_KEY — optional; leave unset to use the OAuth credentials copied in from ~/.claude.
  • GIT_TOKEN — a Gitea personal access token, written to ~/.git-credentials inside the container at startup.

.env.dev is gitignored — never commit real credentials.

Usage

There are two ways to run the container: a plain docker run for local projects, and a docker-compose.yml profile for headless repo mode.

Mode 1: Mount a local project (run.sh)

Runs docker run directly, mounting a project directory from the host into the container at its real path (so Claude's per-project session history matches between host and container) and starting Claude immediately with --dangerously-skip-permissions. The container is started with --rm, so it's removed automatically when Claude exits.

./run.sh                    # use the current directory as the project
./run.sh /path/to/project   # use a specific directory

Mode 2: Clone a repo and run headless (run-repo.sh)

Clones a git repository inside the container and starts Claude with --dangerously-skip-permissions right away — intended for driving the container remotely via claude.ai/code.

./run-repo.sh <repo-url> [branch]

Watch the clone and Claude startup:

docker compose -f docker-compose.yml --profile repo logs -f

First-time setup only — link the container to claude.ai/code:

docker compose -f docker-compose.yml --profile repo exec claude-repo bash
# inside the container:
claude
/web-setup

The link is saved to ~/.claude and persists across restarts. Requires gh auth login on the host beforehand, and git credentials in ~/.git-credentials for private repos.

Stop it with:

docker compose -f docker-compose.yml --profile repo down

How it works

  • docker-entrypoint.sh runs as the dev user on container start. It calls setup-claude.sh via a scoped, passwordless sudo rule to fix permissions on the bind-mounted ~/.claude, ~/.claude.json, ~/.git-credentials, ~/.config/gh, and the working directory — this is needed because rootless Docker bind mounts can appear as root:root inside the container. The dev user has no other root access.
  • In repo mode, the entrypoint then clones REPO_URL (optionally at BRANCH) into ~/<repo-name> and execs claude --dangerously-skip-permissions.
  • In local mode (run.sh), the entrypoint execs whatever command docker run was given — claude --dangerously-skip-permissions.