Skip to content

Repository files navigation

Claude Code Docker

Run Claude Code in a Docker container: a pinned, non-root image with persistent login, plus an opt-in egress firewall for running --dangerously-skip-permissions without handing the agent your whole machine.

Anthropic ships a reference devcontainer for VS Code and calls it "a working example rather than a maintained base image". This repo is the plain-Docker version of that idea: no editor required, no wrapper CLI to install, just a readable Dockerfile and a compose file you can copy into any project.

Container behavior below (build, non-root flag handling, firewall allow/block, login persistence, version pinning) was verified by building and running this exact image (Claude Code 2.1.235, August 2026); CLI flag and output semantics are per the official docs, linked throughout.

Why put Claude Code in a container

  • Autonomous mode needs a boundary. Anthropic's own guidance is to run --dangerously-skip-permissions sessions inside a container, VM, or sandbox runtime, so file tools, subprocesses, MCP servers, and hooks are all inside the boundary (sandbox environments). Inside the container, a misstep trashes /workspace, not your home directory, SSH keys, or other projects.
  • The CLI enforces part of this itself: it refuses --dangerously-skip-permissions when running as root. This image runs Claude Code as an unprivileged claude user, so the flag works.
  • Reproducibility. The image pins a Claude Code version and disables the auto-updater. Every teammate and every CI run gets the same agent.
  • Parallelism. Containers are cheap: run several isolated sessions against different checkouts at once.

Quick Start

git clone https://github.com/agent37-platform/claude-code-docker.git
cd claude-code-docker
docker compose run --rm claude

That drops you into an interactive Claude Code session with the current directory mounted at /workspace. Log in once (subscription OAuth or API key); credentials land in a named volume (claude-config), so every later run and rebuild stays logged in.

Work on a real project instead of this repo:

WORKSPACE=~/code/myapp docker compose run --rm claude

Prefer raw docker?

docker build -t claude-code-docker .
docker run --rm -it \
  -v claude-config:/home/claude/.claude \
  -v "$PWD:/workspace" \
  claude-code-docker

Pin a different release with --build-arg CLAUDE_CODE_VERSION=2.1.227, and match your host user with --build-arg UID=$(id -u) --build-arg GID=$(id -g) if files created in /workspace come out with the wrong owner (Linux hosts; Docker Desktop on macOS/Windows maps ownership for you). On SELinux-enforcing hosts (Fedora/RHEL, podman), add :z to the workspace mount ("$PWD:/workspace:z") or the container cannot read your project.

Authentication inside a container

All the documented options work here (auth docs):

Method How Best for
Interactive OAuth Run claude, follow the login flow; if the browser callback cannot reach the container, paste the code at the prompt Pro/Max subscribers, daily use
ANTHROPIC_API_KEY export it before docker compose run, or put it in .env API billing, headless and CI
CLAUDE_CODE_OAUTH_TOKEN Generate once with claude setup-token (any logged-in machine), then set it like an API key Subscription auth in CI and scripts
Gateway / cloud ANTHROPIC_AUTH_TOKEN, or CLAUDE_CODE_USE_BEDROCK / CLAUDE_CODE_USE_VERTEX with that provider's credentials Enterprise setups

Two container-specific gotchas the docs bury:

  1. Persisting login across rebuilds. OAuth account state lives in ~/.claude.json, which is next to, not inside, ~/.claude. This image sets CLAUDE_CONFIG_DIR=/home/claude/.claude so ALL state lives in one directory, and one volume mount survives everything. If you change the volume target, change CLAUDE_CONFIG_DIR with it (reference).
  2. --bare mode never reads OAuth credentials. In scripts using --bare (recommended for CI: skips hooks, plugin sync, MCP autodiscovery, and CLAUDE.md auto-discovery), authenticate with ANTHROPIC_API_KEY; a CLAUDE_CODE_OAUTH_TOKEN will be ignored there.

Never mount host secrets (~/.ssh, ~/.aws, your real ~/.gitconfig with credential helpers) into a container that runs with permissions off. Anything inside the boundary is readable by whatever the agent executes, including its own credentials volume. Give the container copies of exactly what the task needs.

Autonomous mode, contained

docker compose run --rm yolo

This starts claude --dangerously-skip-permissions behind a default-deny egress firewall (init-firewall.sh, same approach as Anthropic's reference devcontainer): only the domains Claude Code needs per the network docs (Anthropic's API, claude.ai and platform.claude.com for sign-in and OAuth token refresh, registry.npmjs.org), GitHub's published IP ranges, and the telemetry hosts from Anthropic's reference firewall are reachable. Everything else is dropped before it leaves the container. Verified behavior of this exact image: github.com connects, google.com times out.

Your project needs more endpoints? Allow them per run, no file edits:

FIREWALL_ALLOW_DOMAINS="pypi.org files.pythonhosted.org" docker compose run --rm yolo

Honest limits, so you can decide what to trust it with: DNS stays open (the allowlist has to resolve, so DNS tunneling remains an exfil channel), allowlisted hosts like GitHub are themselves reachable, the allowlist IPs are a startup snapshot (a long session can lose an endpoint if a CDN rotates IPs; restart the container to re-resolve), and a malicious repo can still read anything inside the container. The firewall shrinks the blast radius; it does not make untrusted code safe. For hostile-code review, use a throwaway checkout, no credentials volume, and a short-lived token.

Headless and CI

The image runs Claude Code's print mode (headless docs) exactly as documented:

docker run --rm -i \
  -e ANTHROPIC_API_KEY \
  -v "$PWD:/workspace" \
  claude-code-docker \
  claude -p "Explain the failing test in tests/api.test.ts" --bare --output-format json

--output-format json returns the result plus session_id and total_cost_usd; stream-json emits NDJSON events for pipelines; add --max-turns N and --allowedTools "Read,Grep" to bound what an unattended run may do. A ready-to-copy GitHub Actions workflow is in examples/github-actions.yml.

Which isolation option should you use?

By August 2026 there are several ways to isolate Claude Code. They solve different problems:

Option What it is Reach for it when
This repo Plain pinned Dockerfile + compose, firewall included You want docker run/CI/server usage with no editor or extra CLI dependency
Anthropic devcontainer Reference .devcontainer + official Feature You live in VS Code or Codespaces and want the editor-integrated version of the same idea
Docker Sandboxes Docker's free standalone microVM product (sbx run claude), no Docker Desktop needed You want managed per-session microVMs rather than your own image
Native sandboxing Built-in OS-level Bash sandbox (Seatbelt/bubblewrap) + @anthropic-ai/sandbox-runtime You want OS-level guardrails without any container, or layered inside one
claudebox, claude-pod and friends Community wrapper CLIs around the same container idea You want their extras (language profiles, per-project images) and accept a wrapper layer

These compose: the native Bash sandbox can run inside this container for defense in depth (unprivileged containers need the nested-sandbox setting, see the troubleshooting note).

Run it always-on in the cloud

A container on your laptop dies with your laptop. The same image this repo builds can run 24/7 on Agent37 Cloud, which hosts any public Docker image as a persistent instance: from $1.99/mo shared, or $4.94/mo dedicated for an always-on 2 vCPU / 4 GB box with 6 GB disk (the default 4 GB disk works out to $4.76/mo), metered per minute, delete anytime. New workspaces get a $1 starter credit, so the first instance is free to try, no card required.

# 1. Build for amd64 (Agent37 runs linux/amd64) and push anywhere public
docker build --platform linux/amd64 -t ghcr.io/you/claude-code-docker:v1 .
docker push ghcr.io/you/claude-code-docker:v1

# 2. Register it as a template (API key: dashboard -> Cloud -> API keys)
curl -X POST https://api.agent37.com/v1/templates \
  -H "Authorization: Bearer $AGENT37_API_KEY" -H "Content-Type: application/json" \
  -d '{ "name": "claude-code", "image_ref": "ghcr.io/you/claude-code-docker:v1" }'

# 3. Provision an always-on instance from it
curl -X POST https://api.agent37.com/v1/instances \
  -H "Authorization: Bearer $AGENT37_API_KEY" -H "Content-Type: application/json" \
  -d '{ "template": "claude-code", "env": { "ANTHROPIC_API_KEY": "sk-ant-..." } }'

# 4. Drive it: run headless turns over the exec API, from your app or your own cron
curl -X POST https://api.agent37.com/v1/instances/<id>/exec \
  -H "Authorization: Bearer $AGENT37_API_KEY" -H "Content-Type: application/json" \
  -d '{ "command": "claude -p \"Triage new GitHub issues in /workspace\" --output-format json --max-turns 20" }'

Exec calls are capped at 280 seconds: for longer agent runs, start the turn in the background (nohup claude -p ... > /tmp/out.json &) and collect the file with a second exec.

One note for that flow: an instance needs a long-running main process, and this image's default claude exits without a terminal. Register with a keep-alive command instead; a two-line child image does it:

FROM ghcr.io/you/claude-code-docker:v1
CMD ["sleep", "infinity"]

Full walkthrough (cloud builds without a registry, budgets): Agent37 custom image docs. We run Agent37; a one-click Claude Code template is coming, and this repo is the exact image path that page describes today.

What's in the box

File Purpose
Dockerfile debian-slim + pinned official installer, non-root user, CLAUDE_CONFIG_DIR set, auto-updater off
docker-compose.yml claude (interactive) and yolo (firewalled autonomous) services, persistent auth volume
init-firewall.sh Default-deny egress allowlist (Anthropic + GitHub + npm), extensible via env var
entrypoint.sh Raises the firewall when FIREWALL=1, then execs your command
examples/github-actions.yml Headless Claude Code in CI
.env.example Every knob, documented

No Node.js in the image: the official native installer ships a self-contained binary with no Node.js runtime dependency, so the base stays small (debian:bookworm-slim + git, ripgrep, curl, and the firewall tools). Add your project's toolchain in a child image (FROM claude-code-docker + your apt-get install).

License

MIT for the files in this repo. Not affiliated with Anthropic; Claude Code is Anthropic's product, installed here via the official installer, and any image you build and publish contains their binary under their terms.

About

Run Claude Code in Docker: pinned non-root image, persistent login, egress-firewalled --dangerously-skip-permissions mode, compose + CI examples

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages