docker-claude/README.md
2026-04-14 20:11:24 +02:00

4.5 KiB

docker-claude

Runs Claude Code inside an isolated Docker environment with a proxy sidecar for controlled egress. Claude cannot reach the host filesystem or network directly.

Architecture

┌─────────────────────────────────────────────────────┐
│  Host machine                                       │
│                                                     │
│  claude.sh (control script)                         │
│       │                                             │
│       ▼                                             │
│  ┌─────────────────────────────────────────────┐   │
│  │  Docker: claude-secure                      │   │
│  │                                             │   │
│  │  ┌─────────────┐    claude-internal         │   │
│  │  │  claude     │◄─────(internal only)───►  │   │
│  │  │  (UID 1000) │           │               │   │
│  │  └─────────────┘    ┌──────┴──────┐        │   │
│  │                     │   proxy     │         │   │
│  │                     │  (UID 13)   │         │   │
│  │                     └──────┬──────┘         │   │
│  │                     proxy-external           │   │
│  └─────────────────────────────────────────────┘   │
│                              │                      │
│                              ▼                      │
│                   internet (allowlisted)             │
└─────────────────────────────────────────────────────┘
  • claude container — Claude Code, runs as UID 1000, on claude-internal only (no internet route)
  • proxy container — Squid forward proxy, runs as UID 13, bridges claude-internal ↔ internet, enforces egress allowlist
  • claude-internal — Docker bridge with internal: true; Docker adds no default gateway, so containers on this network cannot reach the internet directly
  • proxy-external — Standard bridge; the proxy sidecar uses this for controlled outbound access

Prerequisites

  • Docker Engine 24+
  • Docker Compose v2 plugin (docker compose version)

Setup

# 1. Clone / copy this repo
git clone <repo> docker-claude && cd docker-claude

# 2. Configure your API key
cp .env.example .env
$EDITOR .env   # set ANTHROPIC_API_KEY

# 3. Make the control script executable
chmod +x claude.sh

Usage

# Build images, start proxy, launch Claude Code interactively
./claude.sh start

# Same as start but skips image rebuild (faster on subsequent runs)
./claude.sh run

# Stop and remove all containers (proxy + any running sessions)
./claude.sh stop

# Rebuild images without cache (e.g. after Claude Code updates)
./claude.sh update

# Tail proxy access logs
./claude.sh logs

# Show container status
./claude.sh status

# Open a debug bash shell inside the Claude container
./claude.sh shell

Working with host files

By default, Claude's workspace is a named Docker volume (claude-secure-workspace) — fully isolated from the host.

To mount a specific host directory:

WORKSPACE_DIR=$HOME/myproject ./claude.sh run

The directory is mounted at /workspace inside the container.

Egress allowlist

Edit proxy/squid.conf and add domains to the allowed_sites ACL:

acl allowed_sites dstdomain api.anthropic.com
acl allowed_sites dstdomain statsig.anthropic.com
# acl allowed_sites dstdomain api.github.com   # uncomment if needed
# acl allowed_sites dstdomain registry.npmjs.org

Rebuild the proxy after changes:

docker compose -p claude-secure build proxy
./claude.sh stop && ./claude.sh start

Security controls

Control Claude container Proxy container
Non-root user UID 1000 (claude) UID 13 (proxy)
no-new-privileges yes yes
All capabilities dropped yes yes
Direct internet access no (internal network only) allowlisted only
Host filesystem no mounts by default none
Docker socket not mounted not mounted