Docker Container for Claude Code, including project setup/updates
Find a file
docker-claude 6410f22f1d feat(policy): allow bash and file modification tools without prompting
Add permissions.allow to managed-settings.json for Bash(*), Edit(*),
and Write(*). Claude Code will not prompt for confirmation on shell
commands or file writes inside the container.
2026-04-14 22:57:29 +02:00
claude feat(policy): allow bash and file modification tools without prompting 2026-04-14 22:57:29 +02:00
proxy refactor(docker): migrate both images to Alpine 2026-04-14 22:40:57 +02:00
.dockerignore initial 2026-04-14 20:11:24 +02:00
.env.example feat(auth): support subscription login alongside API key 2026-04-14 22:47:04 +02:00
.gitignore initial 2026-04-14 20:11:24 +02:00
CLAUDE.md refactor(claude): use built-in node user instead of custom claude user 2026-04-14 22:50:59 +02:00
claude.sh feat(auth): support subscription login alongside API key 2026-04-14 22:47:04 +02:00
docker-compose.yml refactor(claude): use built-in node user instead of custom claude user 2026-04-14 22:50:59 +02:00
README.md refactor(claude): use built-in node user instead of custom claude user 2026-04-14 22:50:59 +02:00

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     │──┐  claude-internal             │   │
│  │  │  (UID 1000) │  │  (internal: true)            │   │
│  │  └─────────────┘  ├──────────────► ┌──────────┐ │   │
│  │  ┌─────────────┐  │                │  proxy   │ │   │
│  │  │  webui      │──┘                │ (UID 13) │ │   │
│  │  │  (UID 1000) │                   └────┬─────┘ │   │
│  │  │  port 7681  │              proxy-external     │   │
│  │  └─────────────┘                        │        │   │
│  └──────────────────────────────────────────────────┘   │
│                                            │             │
│                                            ▼             │
│                                 internet (allowlisted)   │
└──────────────────────────────────────────────────────────┘
  • claude — Claude Code CLI (node:20-alpine), runs as the built-in node user (UID 1000), on claude-internal only
  • webui — Claude Code in a browser terminal via ttyd (node:20-alpine), node user (UID 1000), on claude-internal only, port 7681
  • proxy — Squid forward proxy (alpine:3.21), bridges claude-internal ↔ internet with egress allowlist
  • claude-internalinternal: true; no default gateway, containers cannot reach the internet directly
  • proxy-external — Standard bridge; proxy sidecar only

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 credentials (see Authentication below)
cp .env.example .env
$EDITOR .env

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

Authentication

Three options — pick one and set it in .env:

Option 1 — API key

ANTHROPIC_API_KEY=sk-ant-...

Option 2 — OAuth token (subscription, headless-friendly)

Run this on your host (not inside the container) to generate a 1-year token:

claude setup-token

Then set the printed token in .env:

CLAUDE_CODE_OAUTH_TOKEN=...

Option 3 — Browser OAuth (interactive)

Leave both keys unset. On first run, Claude Code will print a login URL. Port 54545 must be reachable from your browser for the OAuth callback:

sbx ports <sandbox-name> --publish 54545:54545/tcp

Then run ./claude.sh run and follow the prompt. Credentials are stored in the claude-config Docker volume and reused on every subsequent run.

Usage

CLI mode

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

# Start proxy if needed, launch Claude Code (faster on subsequent runs)
./claude.sh run

# Mount a host directory as the workspace
WORKSPACE_DIR=$HOME/myproject ./claude.sh run

Web interface

Serves Claude Code as a browser terminal via ttyd, protected by HTTP basic auth.

# Add to .env first:
# WEBUI_PASSWORD=your-strong-password
# WEBUI_USER=claude   # optional, defaults to "claude"

./claude.sh web
# → Web interface running at http://0.0.0.0:7681

# To reach it from outside the sandbox host:
sbx ports <sandbox-name> --publish 7681:7681/tcp

# Stop web interface (keeps proxy running)
./claude.sh web-stop

Other commands

./claude.sh stop          # Stop and remove all containers
./claude.sh update        # Rebuild images without cache
./claude.sh logs          # Tail proxy logs
./claude.sh logs webui    # Tail web interface logs
./claude.sh status        # Show container status
./claude.sh shell         # Debug bash shell in the Claude container

Workspace

Mode Default Override
CLI (run/start) Named Docker volume (isolated) WORKSPACE_DIR=/path ./claude.sh run
Web (web) Named Docker volume (claude-web-workspace) Edit docker-compose.yml volumes

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
# acl allowed_sites dstdomain registry.npmjs.org

Rebuild after changes:

./claude.sh update
./claude.sh stop && ./claude.sh start

Security controls

Control claude / webui proxy
Non-root user UID 1000 (node, built into base image) squid user
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
Web auth basic auth (ttyd --credential) n/a