claude code + docker complete guide
Table of content
by Ray Svitla
Claude Code runs shell commands on your machine. with your permissions. with access to your files. your SSH keys. your environment variables. your entire home directory.
are you nervous yet? you should be a little nervous.
Docker solves this. run Claude Code inside a container, give it access to only what it needs, and let it break things without breaking your machine. it’s not paranoia — it’s basic hygiene for running agentic AI.
the basic setup
Anthropic provides an official Docker image. the minimal run:
docker run -it \
-e ANTHROPIC_API_KEY=$ANTHROPIC_API_KEY \
-v $(pwd):/workspace \
-w /workspace \
ghcr.io/anthropics/claude-code:latest
this mounts your current directory into the container and starts Claude Code with access to those files. everything outside /workspace is invisible to Claude. your SSH keys, your dotfiles, your other projects — all unreachable.
why bother
three reasons, in order of how often they’ll save you.
sandboxing. Claude occasionally runs commands that have side effects you didn’t expect. npm install that adds 400 packages to your global cache. pip install that conflicts with your system Python. a sed command that edits the wrong file. inside a container, the blast radius is contained. delete the container, start fresh.
reproducibility. “works on my machine” extends to Claude. if Claude sets up a project inside a container with a specific Node version, Python version, and system dependencies, that setup is reproducible. ship the Dockerfile, anyone can recreate the environment.
multi-project isolation. running Claude on a Python 3.12 project while another project needs 3.9? containers make this trivial. no pyenv juggling, no version conflicts, no “which pip is this.”
a real Dockerfile
the official image is fine for quick work. for serious use, build your own:
FROM ghcr.io/anthropics/claude-code:latest
# add your project's dependencies
RUN apt-get update && apt-get install -y \
python3 python3-pip \
postgresql-client \
&& rm -rf /var/lib/apt/lists/*
# pre-install project tools
COPY package.json /workspace/
RUN cd /workspace && npm install
# set git config so Claude can commit
RUN git config --global user.email "claude@example.com" \
&& git config --global user.name "Claude Code"
build it: docker build -t claude-dev .
run it with your project mounted:
docker run -it \
-e ANTHROPIC_API_KEY=$ANTHROPIC_API_KEY \
-v $(pwd):/workspace \
-v $HOME/.gitconfig:/root/.gitconfig:ro \
-p 3000:3000 \
-w /workspace \
claude-dev
note the port mapping — if Claude starts a dev server, you can access it from your host browser. the .gitconfig mount is read-only so Claude can use git but not modify your config.
MCP servers inside containers
MCP servers running inside Docker need their own consideration. you can run them in the same container as Claude Code or in separate containers using docker compose.
same container (simpler):
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": ["-y", "@anthropic/mcp-filesystem", "/workspace"]
}
}
}
separate containers (more isolated but more complex): use docker compose with a shared network. Claude Code in one container, MCP servers in others, communicating over the internal network.
for most people, same container is fine. separate containers matter when your MCP servers need different permissions or you’re running something security-sensitive.
the sharp edges
performance. Docker on macOS uses a Linux VM under the hood, and file system access through the mount is slower than native. on large projects, npm install or cargo build inside a container can be noticeably slower. Linux hosts don’t have this problem.
GPU access. if you’re doing anything that needs GPU (local models, ML work), Docker GPU passthrough is its own adventure. nvidia-docker on Linux works. macOS has no GPU passthrough. plan accordingly.
state persistence. containers are ephemeral by default. when you stop the container, everything Claude installed or configured inside it disappears. use volumes for data you want to keep:
docker run -it \
-v claude-cache:/root/.cache \
-v claude-npm:/root/.npm \
...
networking. Claude inside a container can’t access localhost services on your host without --network host (Linux) or host.docker.internal (macOS/Windows). if Claude needs to talk to your local database or dev server, configure this explicitly.
the paranoia spectrum
minimal trust (recommended for unfamiliar codebases):
docker run --read-only --tmpfs /tmp -v $(pwd):/workspace:ro ...
read-only filesystem, project mounted read-only. Claude can read but not modify anything. useful for code review and analysis.
standard trust (daily development):
docker run -v $(pwd):/workspace ...
Claude can modify your project files but nothing else on your system.
full trust (you know what you’re doing):
docker run -v $HOME:/home/user -v /var/run/docker.sock:/var/run/docker.sock ...
Claude has access to your home directory and can control Docker itself. this is powerful and dangerous. don’t do this unless you genuinely need it and understand the implications.
where do you fall on the paranoia spectrum — and where should you?
→ agent guardrails — safety beyond containers → claude code setup — basic installation → apple containers — macOS native alternative
Ray Svitla stay evolving