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
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 --update| Path | Mode | Notes |
|---|---|---|
| Current directory | read/write | Mounted at the same absolute path |
~/.claude/ |
read/write | Claude state and auth |
~/.claude.json |
read/write | Claude settings |
~/.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| 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.
To 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"