Codeman logocodeman GitHub →

Run Claude Code in a Docker sandbox, one container per project

Updated 2026-09-25

On this pageWhat a container protects, and what it does notBuild an imageRun one container per projectLimiting the networkTrapsHow Codeman handles it

Build an image that contains the CLI, then run one long-lived container per project as your own non-root user, with the project bind-mounted at the same path, capabilities dropped, and a per-project home directory that keeps the CLI's login. The non-root user is not optional: Claude Code refuses --dangerously-skip-permissions when it runs as root.

What a container protects, and what it does not

Commands the agent runs execute inside the container, so a stray rm -rf or a hostile install script hits the container's filesystem instead of your laptop's. Your project is still bind-mounted, so every edit lands directly in your real repository.

Anthropic's dev container docs are blunt about the rest: with --dangerously-skip-permissions, a container does not stop a malicious project from exfiltrating anything reachable inside it, including the Claude Code credentials in ~/.claude. Use it with repositories you trust, and do not mount ~/.ssh or cloud credential files into it.

Build an image

FROM node:22-bookworm-slim
RUN apt-get update \
 && apt-get install -y --no-install-recommends git tmux ca-certificates \
 && rm -rf /var/lib/apt/lists/*
RUN npm install -g @anthropic-ai/claude-code
docker build -t claude-agent .

Add whatever your project needs to build and test, since the agent will run those commands inside this image. Other agent CLIs install the same way; add them to the same image or build one per CLI.

Run one container per project

mkdir -p ~/.agent-homes/myapp
docker run -d --name claude-myapp \
  --user "$(id -u):$(id -g)" \
  --cap-drop ALL --security-opt no-new-privileges \
  --pids-limit 512 --memory 4g --memory-swap 4g --init \
  -e HOME=/home/agent \
  -v ~/.agent-homes/myapp:/home/agent \
  -v "$PWD":"$PWD" -w "$PWD" \
  claude-agent sleep infinity

docker exec -it claude-myapp tmux new -A -s main claude

What each part is for:

Never add --privileged or mount /var/run/docker.sock. Either one hands the agent the host.

Limiting the network

--network none looks tempting, but it also cuts the agent off from its model API, so the CLI cannot work. The practical option is an egress allowlist. Anthropic's reference dev container ships an init-firewall.sh that limits outbound traffic to the destinations it allows; running a firewall inside the container needs the NET_ADMIN and NET_RAW capabilities, which you would add back after --cap-drop ALL. You can also leave the container alone and enforce egress rules on the host or network instead.

Traps

How Codeman handles it

Codeman builds this pattern in as Docker cases. On Add Case → Create New, tick 🐳 Run in an isolated Docker container; expanding the container settings offers Small, Medium, Large and GPU templates for memory and CPU.

What it does for you:

A container you already run can be attached instead, with Attach to an existing container; Codeman then only execs into it and never starts, stops or removes it.

One setting to know: in-container Claude hooks call back to the Codeman server, and with Codeman's default loopback bind they cannot reach it. Set CODEMAN_DOCKER_BRIDGE_HOOKS=1 to open a hooks-only listener on the Docker bridge. The full reference is Docker Cases, and where containers fit in the threat model is covered in Security.