A minimalist solution to run Claude Code in a sandboxed throwaway container, safely isolated from your host system while still having access to your project and necessary credentials.
- Supports multiple simultaneous sessions on the same project.
- Telemetry is disabled (via
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC). - Clipboard image paste works inside the container (macOS only) — use Ctrl+V to paste screenshots, images copied from a browser, or image files copied from Finder.
- Single image — one image is built once and reused for every project, keeping disk usage low.
- Same-path mounts — the project directory and home paths are mounted at their exact host paths inside the container. This keeps file references, git context, and symlinks valid without any translation.
- Throwaway containers — each invocation starts a fresh container (
--rm). No state is left behind between sessions beyond what is explicitly mounted. - Selective mounts — only the minimum set of host paths needed for Claude Code to function are exposed. Optional paths (git config, SSH keys, etc.) are mounted read-only and only if they exist.
- No Node.js on the host — Claude Code and its dependencies live entirely inside the image.
- Docker or Podman
jq(recommended) — needed for proper profile isolation (strips account keys when creating profile settings). Without it, profiles get a full copy of the base settings including account info.
For clipboard image paste:
-
macOS (the clipboard bridge relies on macOS-specific APIs)
-
Python 3 (pre-installed on macOS; used to run the clipboard bridge server)
-
pngpaste(recommended) — improves clipboard compatibility:brew install pngpaste
Without it, the bridge falls back to AppleScript, which works but may be slower.
1. Build the image (once):
./build.shTo pin a specific Claude Code version or tag the image:
CLAUDE_VERSION=1.2.3 ./build.sh # pin Claude Code version
./build.sh staging # tag image as claude-code:staging2. Install the launcher (once):
./install.shThis symlinks run.sh to ~/.local/bin/claude.
To use a different command name (e.g. to keep a local claude install):
INSTALL_AS=claude-docker ./install.sh# From any project directory:
claude
# Pass arguments directly:
claude --version
claude "explain this codebase"
# Start a named session (useful when returning back to it later via --resume):
claude --name "Add necessary tests"
# Update Claude Code to the latest version (doesn't interrupt ongoing sessions):
claude --updateUse --profile <name> to maintain separate login credentials (e.g., for different Anthropic accounts):
# Work account
claude --profile work
# Personal account
claude --profile personal "explain this code"
# Default (no profile) — uses ~/.claude/.credentials.json normally
claudeEach profile stores its own credentials (~/.claude/.credentials.<name>.json) and settings (~/.claude.<name>.json), overlay-mounted on top of the originals inside the container. On first use, settings are copied from ~/.claude.json (with account-specific keys stripped; requires jq); credentials start empty so Claude Code will prompt you to log in.
# List profiles
ls ~/.claude/.credentials.*.json
# Remove a profile
rm ~/.claude/.credentials.<name>.json ~/.claude.<name>.json| Path | Mode | Notes |
|---|---|---|
| Current directory | read/write | Mounted at the same absolute path |
~/.claude/ |
read/write | Claude state and auth |
~/.claude/.credentials.<profile>.json |
read/write | Only with --profile; overlays .credentials.json |
~/.claude.json |
read/write | Claude settings |
~/.claude.<profile>.json |
read/write | Only with --profile; overlays ~/.claude.json |
~/.cache/ |
read/write | Shared cache across sessions |
~/.gitconfig |
read-only | If it exists |
~/.gitignore.global |
read-only | If it exists |
~/.ssh/ |
read-only | If it exists |
~/.npmrc |
read-only | If it exists |
~/.config/gh |
read-only | If it exists |
User-specific settings live in ~/.config/claude-code-container/config.ini (respects $XDG_CONFIG_HOME). The file uses simple INI-style sections.
[mounts] — extra paths to mount into the container, one per line in src:dst[:options] format:
[mounts]
# Shared notes directory (read/write)
/home/myuser/notes:/home/myuser/notes
# Read-only reference data
/home/myuser/datasets:/data:ro[env] — extra environment variables to pass to the container, one KEY=value per line:
[env]
SOME_ENV_VAR=value[profile.<name>.env] — profile-specific environment variables, only applied when --profile <name> is used. Useful for routing different profiles to different API endpoints:
[profile.personal.env]
ANTHROPIC_BASE_URL=https://api.example.com
ANTHROPIC_AUTH_TOKEN=xxx| Environment variable | Default | Description |
|---|---|---|
CLAUDE_DOCKER_IMAGE |
claude-code:latest |
Image name to use |
CLAUDE_CONFIG_DIR |
~/.claude |
Path to Claude config/state dir on host |
INSTALL_DIR |
~/.local/bin |
Where install.sh places the symlink |
INSTALL_AS |
claude |
Command name created by install.sh |
CLIPBOARD_HOST |
host.docker.internal |
Hostname used by the container to reach the clipboard bridge server |
CLIPBOARD_PORT |
18256 |
TCP port used by the clipboard bridge server |
CLIPBOARD_DEBUG |
(unset) | Set to any value to enable clipboard debug logging |
Claude Code's Ctrl+V image paste is bridged to the macOS clipboard automatically. A lightweight Python server starts in the background on the host and exposes clipboard image data over TCP; shim scripts inside the container intercept xclip/wl-paste calls and forward them to it.
Supported sources:
- Screenshots (Cmd+Shift+3 / Cmd+Shift+4 / etc.)
- Images copied from a browser or any app
- Image files copied from Finder (PNG, JPG, GIF, WebP, TIFF)
Not supported: non-image files — pasting a .txt, etc. does nothing. For PDFs, use Claude Code's @-mention syntax instead: type @/path/to/file.pdf to bring a PDF into context.
See Requirements for host-side dependencies.
You can route a profile to a third-party Anthropic-compatible API by setting ANTHROPIC_BASE_URL and related env vars in the profile's config section in ~/.config/claude-code-container/config.ini.
[profile.glm.env]
ANTHROPIC_BASE_URL=https://api.z.ai/api/anthropic
ANTHROPIC_AUTH_TOKEN=xxx
API_TIMEOUT_MS=300000
ANTHROPIC_DEFAULT_OPUS_MODEL=glm-5.1
ANTHROPIC_DEFAULT_SONNET_MODEL=glm-5
ANTHROPIC_DEFAULT_HAIKU_MODEL=glm-4.7claude --profile glmTo enable verbose logging for the clipboard bridge, set CLIPBOARD_DEBUG:
CLIPBOARD_DEBUG=1 claudeLogs are written to:
~/.claude/clipboard-server.log— host-side server~/.claude/clipboard-client.log— container-side shim
When modifying clipboard-server.py, the already-running server process won't pick up your changes automatically. Kill it so the next claude invocation starts a fresh instance:
pkill -f clipboard-server.py 2>/dev/null && echo "killed" || echo "not running"