Containerized drop-in replacement for Claude Code - run worry-free in --dangerously-skip-permissions mode with complete isolation. Pre-configured MCP servers, host GPU access, conda environment mounting, and modular plugin system for effortless customization.
Required:
- ✅ Claude Code authentication
- ✅ Docker installation
Everything else is optional - the container runs fine without any additional setup.
# 1. Clone and enter directory
git clone https://github.com/LORDofDOOM/claude-docker.git
cd claude-docker
# 2. Setup environment (completely optional - skip if you don't need custom config)
cp .env.example .env
nano .env # Add any optional configs
# 3. Install (use sudo for GPU support)
sudo ./src/install.sh # Or just ./src/install.sh without GPU
# 4. Run from any project
cd ~/your-project
claude-dockerInstaller notes:
- Alias is added to your detected shell RC file (
.bashrc,.bash_profile,.zshrc, or.profile) - Persistent data defaults to
~/.claude-docker; override withCLAUDE_DOCKER_HOME=/path/to/writable/dir
# 1. Clone and enter directory
git clone https://github.com/LORDofDOOM/claude-docker.git
cd claude-docker
# 2. Setup environment (completely optional)
copy .env.example .env
notepad .env # Add any optional configs
# 3. Install as global .NET tool (one-time)
install-windows.bat
# 4. Open a NEW terminal, then run from any project
cd C:\your-project
claude-dockerRequirements: .NET SDK 10+ and Docker Desktop with WSL2 backend.
Installer notes:
- Installs
claude-dockeras a .NET global tool (~/.dotnet/tools, already in PATH) - Sets
CLAUDE_DOCKER_PROJECTenvironment variable to locate the repo - Persistent data defaults to
%USERPROFILE%\.claude-docker; override withCLAUDE_DOCKER_HOME - Conda host-mount is not supported on Windows (Windows paths are incompatible with Linux container paths)
That's it! Claude runs in an isolated Docker container with access to your project directory.
claude-docker # Start Claude in current directory
claude-docker --podman # Use podman instead of docker
claude-docker --continue # Resume previous conversation
claude-docker --rebuild # Force rebuild Docker image
claude-docker --rebuild --no-cache # Rebuild without using cache
claude-docker --memory 8g # Set container memory limit
claude-docker --gpus all # Enable GPU access (requires nvidia-docker)
claude-docker --cc-version 2.0.64 # Install specific Claude Code version| Flag | Description | Example |
|---|---|---|
--podman |
Use podman instead of docker | claude-docker --podman |
--continue |
Resume previous conversation in current directory | claude-docker --continue |
--rebuild |
Force rebuild of the Docker image | claude-docker --rebuild |
--no-cache |
When rebuilding, don't use Docker cache | claude-docker --rebuild --no-cache |
--memory |
Set container memory limit | claude-docker --memory 8g |
--gpus |
Enable GPU access | claude-docker --gpus all |
--cc-version |
Install specific Claude Code version (requires rebuild) | claude-docker --rebuild --cc-version 2.0.64 |
--tool |
Pick the runtime: claude (default), opencode, or codex |
claude-docker --tool codex |
The Docker image is automatically rebuilt when a new git commit is detected in the claude-docker repository. This means changes to Dockerfile, mcp-servers.txt, .env, or any other build file take effect after you commit and run claude-docker — no --rebuild flag needed.
The current commit hash is stored in ~/.claude-docker/.build-hash and compared on each launch.
Set defaults in your .env file:
DOCKER_MEMORY_LIMIT=8g # Default memory limit
DOCKER_GPU_ACCESS=all # Default GPU accessSet this in your shell if you want to override the default persistent directory:
# Linux / macOS
export CLAUDE_DOCKER_HOME=/path/to/writable/dir
# Windows (PowerShell)
setx CLAUDE_DOCKER_HOME "C:\path\to\writable\dir"# Resume work with 16GB memory limit
claude-docker --continue --memory 16g
# Rebuild after updating .env file
claude-docker --rebuild
# Use GPU for ML tasks
claude-docker --gpus all
# Rollback to specific Claude Code version
claude-docker --rebuild --cc-version 2.0.64
# Install latest version after rollback
claude-docker --rebuildAll configuration below is optional. The container works out-of-the-box without any of these settings.
All environment variables are completely optional. Only configure what you need:
Get Telegram notifications when Claude completes tasks, hits errors, or needs your input — powered by the mcp-communicator-telegram MCP server.
TELEGRAM_BOT_TOKEN=your_telegram_bot_token
TELEGRAM_CHAT_ID=your_telegram_chat_idSetup:
- Message @BotFather on Telegram, send
/newbot, follow prompts - Send
/startto your new bot - Get your chat ID from
https://api.telegram.org/bot<YOUR_TOKEN>/getUpdates
How it works:
- Telegram is an MCP tool (
notify_user/ask_user), not a background service. Claude decides when to use it based on theCLAUDE.mdnotification protocol. notify_user— sends one-way status updates (task complete, milestone reached)ask_user— sends a question and waits for your reply via Telegram- Claude will not send messages at the idle prompt. It sends notifications when it finishes a task, encounters an error, or needs your input.
Important: The notification protocol is defined in the CLAUDE.md template. If you customized your persistent CLAUDE.md before the Telegram protocol was added, you need to reset it:
# Linux / macOS
rm "${CLAUDE_DOCKER_HOME:-$HOME/.claude-docker}/claude-home/CLAUDE.md"
# Windows
del "%USERPROFILE%\.claude-docker\claude-home\CLAUDE.md"Then rebuild with claude-docker --rebuild to copy the fresh template.
By default, claude-docker uses its own isolated configuration at ~/.claude-docker/claude-home/. If you also use Claude Code natively (just running claude on the host), you can share configuration so both environments have the same memory, sessions, settings, and conversation history.
SHARE_NATIVE_CLAUDE=truefalse (default) |
true |
|
|---|---|---|
| Config location | ~/.claude-docker/claude-home/ |
Host's ~/.claude/ |
| Memory | Isolated | Shared |
| Session history | Isolated (per project) | Shared (with path linking) |
| CLAUDE.md / settings | Isolated | Shared |
| Commands / Agents | Isolated | Shared |
--continue |
Only resumes docker sessions | Resumes across native and docker |
| MCP servers | Always separate (stored in .claude.json) |
Always separate |
When enabled, --continue works across both — start a task with claude-docker, then resume it with native claude (or vice versa) in the same project folder.
Note: Each project directory gets its own session history regardless of this setting. Running claude-docker in D:\ProjectA and D:\ProjectB will never share sessions with each other.
The container can also run OpenCode instead of Claude Code, with the same launch ergonomics: same Docker image, same MCP servers, same auto-rebuild. Two equivalent ways to start it:
opencode-docker # sister command (preferred)
claude-docker --tool opencode # equivalentENABLE_OPENCODE=true # default: true — image always includes the opencode binary
SHARE_NATIVE_OPENCODE=false # mirror the host's ~/.config/opencode and ~/.local/share/opencode into the containerDefaults baked into the image:
permission: "allow"is set in the seeded~/.config/opencode/opencode.json— this is the OpenCode TUI equivalent of Claude's--dangerously-skip-permissions(the flag itself only exists onopencode run, not the TUI).- The same five MCP servers ship pre-configured (Serena, Context7, Telegram, grep.app, Playwright) — translated from
mcp-servers.txtinto OpenCode's declarative"mcp": { … }schema with{env:VAR}substitution.
Credentials are persisted exactly like Claude's:
false(default):~/.claude-docker/opencode-config/and~/.claude-docker/opencode-data/(host-isolated). On first launch, host's~/.local/share/opencode/auth.json(if present) is copied in so existing API keys carry over.true: bind-mounts the host's~/.config/opencodeand~/.local/share/opencodedirectly, so native and docker share auth + sessions.
Note on Anthropic auth: OpenCode does not support Claude Pro/Max subscription auth — Anthropic explicitly prohibits it. Use an ANTHROPIC_API_KEY (or any of the 75+ other providers) via opencode auth login. If you want to use a Claude subscription, run claude-docker (default).
The container can also run Codex CLI (openai/codex), with the same launch ergonomics: same Docker image, same MCP servers, same auto-rebuild. Two equivalent ways to start it:
codex-docker # sister command (preferred)
claude-docker --tool codex # equivalentENABLE_CODEX=true # default: true — image always includes the codex binary
SHARE_NATIVE_CODEX=false # mirror the host's ~/.codex into the containerDefaults baked into the image:
approval_policy = "never"andsandbox_mode = "danger-full-access"are set in the seeded~/.codex/config.toml, and Codex is launched with--yolo— together the equivalent of Claude's--dangerously-skip-permissions. Safe because the container is isolated.- The same MCP servers ship pre-configured (Serena, Context7, grep.app, Playwright, plus Telegram when creds are set) — translated into Codex's
[mcp_servers.*]TOML schema. Context7's API key is sourced from theCONTEXT7_API_KEYenv var viaenv_http_headers, and the Telegram block (with real credentials) is appended bystartup.shonly whenTELEGRAM_BOT_TOKEN/TELEGRAM_CHAT_IDare present. --continuemaps tocodex resume --last(Codex has no--continueflag).
Credentials are persisted exactly like Claude's — Codex keeps both config.toml and auth.json under a single ~/.codex directory:
false(default):~/.claude-docker/codex-home/(host-isolated). On first launch, host's~/.codex/auth.json(if present) is copied in so an existing ChatGPT/API login carries over.true: bind-mounts the host's~/.codexdirectly, so native and docker share auth + sessions.
Both installers turn Claude's commit/PR attribution off by default, so you don't have to edit config on every machine:
| Surface | Key | Trailer it produces |
|---|---|---|
| Legacy co-author | includeCoAuthoredBy: false |
Co-Authored-By: Claude <[email protected]> |
| Commit footer | attribution.commit: "" |
Generated with Claude Code |
| PR body footer | attribution.pr: "" |
same, in the PR description |
| Session link | attribution.sessionUrl: false |
Claude-Session: https://claude.ai/code/session_... |
sessionUrl is the one that bites people. It is a separate gate — with an empty attribution.commit, Claude Code emits the session URL as the only trailer rather than none, because it builds the text as commit ? commit + "\nClaude-Session: " + url : "Claude-Session: " + url. It appears in cloud / Remote Control sessions. See anthropics/claude-code#77830, closed as working-as-designed with sessionUrl as the documented switch.
- Docker: applied by
src/startup.shon every container start. - Native Windows: applied by
src/apply-attribution.ps1, invoked frominstall-windows.bat. It merges into your existingsettings.json(all other keys preserved), writes asettings.json.bakfirst, and refuses to touch the file if it isn't valid JSON. Re-running is safe.
Set DISABLE_AI_ATTRIBUTION=false in .env (Docker) or in the environment (Windows) to keep Claude's default attribution. The env var CLAUDE_CODE_SUPPRESS_SESSION_ATTRIBUTION=1 hits the same gate as sessionUrl if you prefer not to persist a setting.
Both installers turn Claude's commit/PR attribution off by default, so you don't have to edit config on every machine:
| Surface | Key | Trailer it produces |
|---|---|---|
| Legacy co-author | includeCoAuthoredBy: false |
Co-Authored-By: Claude <[email protected]> |
| Commit footer | attribution.commit: "" |
Generated with Claude Code |
| PR body footer | attribution.pr: "" |
same, in the PR description |
| Session link | attribution.sessionUrl: false |
Claude-Session: https://claude.ai/code/session_... |
sessionUrl is the one that bites people. It is a separate gate — with an empty attribution.commit, Claude Code emits the session URL as the only trailer rather than none, because it builds the text as commit ? commit + "\nClaude-Session: " + url : "Claude-Session: " + url. It shows up in cloud / Remote Control sessions. See anthropics/claude-code#77830, closed as working-as-designed with sessionUrl as the documented switch.
- Docker: applied by
src/startup.shon every container start. - Native Windows: applied by
src/apply-attribution.ps1, invoked frominstall-windows.bat. It merges into your existingsettings.json(all other keys preserved), writes asettings.json.bakfirst, and refuses to touch the file if it isn't valid JSON. Re-running is safe.
Set DISABLE_AI_ATTRIBUTION=false in .env (Docker) or in the environment (Windows) to keep Claude's default attribution. The env var CLAUDE_CODE_SUPPRESS_SESSION_ATTRIBUTION=1 hits the same gate as sessionUrl if you prefer not to persist a setting.
The same enhanced statusline used inside the Docker container (model, cost, context bar, git branch, 5h/7d plan usage) can be enabled for native Windows Claude Code via a PowerShell port at src/statusline.ps1.
# Enable
install-windows-statusline.bat
# Disable
uninstall-windows-statusline.batThe installer merges into %USERPROFILE%\.claude\settings.json — your existing keys are preserved. Restart Claude Code after running it.
Caveats:
- Cold-start adds ~150–300 ms per render (Windows PowerShell startup). Each render after the first stays in cache; the OAuth usage check is itself cached for 60 seconds in
%TEMP%\.claude-usage-cache.json. - If you also use
SHARE_NATIVE_CLAUDE=true, the docker container's startup writes its ownstatusLine(pointing at/app/statusline.sh) into the same settings file, which would clobber the Windows command. SetENABLE_STATUSLINE=falsein.envto keep the docker side from overwriting it.
Mount additional host directories into the container for Claude to access. Useful for shared libraries, reference projects, or source code that lives outside the current project folder.
# Semicolon-separated list of directories
# Append :ro for read-only access
EXTRA_MOUNT_DIRS=D:\Projects\SharedLib:ro;D:\OtherProjectDirectories are mounted under /mnt/ using the folder name:
D:\Projects\SharedLib→/mnt/SharedLib(read-only)D:\OtherProject→/mnt/OtherProject(read-write)
Linux example:
EXTRA_MOUNT_DIRS=/home/user/shared-code:ro;/home/user/other-projectBy default, the container can reach services running on your host machine via host.docker.internal. For example, if you have a dev server on port 9000, use http://host.docker.internal:9000 from inside the container (e.g. with Playwright).
If you need full host network access (so localhost inside the container maps directly to the host), enable host network mode:
DOCKER_NETWORK_MODE=host| Mode | How to reach host port 9000 | Use case |
|---|---|---|
| Default (bridge) | http://host.docker.internal:9000 |
Works out of the box, no config needed |
DOCKER_NETWORK_MODE=host |
http://localhost:9000 |
Full host network, useful for Playwright testing local apps |
Note: --network host disables Docker's network isolation. The container shares the host's network stack entirely. Only use this when you need direct localhost access.
Mount your host conda environments and packages into the container:
# Primary conda installation path
CONDA_PREFIX=/path/to/your/conda
# Additional conda directories (space-separated list)
# Directories are mounted to the same path inside the container
# Automatic detection:
# - Paths with "*env*" are added to CONDA_ENVS_DIRS (for environments)
# - Paths with "*pkg*" are added to CONDA_PKGS_DIRS (for package cache)
CONDA_EXTRA_DIRS="/path/to/envs /path/to/pkgs"All your conda environments work exactly as they do on your host system - no Dockerfile modifications needed.
Additional apt packages beyond the Dockerfile defaults:
SYSTEM_PACKAGES="libopenslide0 libgdal-dev"Note: Adding system packages requires rebuilding the image with claude-docker --rebuild.
Git configuration (global username and email) is automatically loaded from your host system during Docker build. Commits appear as you.
Note: Whether Claude uses git at all is controlled by your CLAUDE.md prompt engineering. The default configuration does NOT include git behaviors - customize after installation if needed.
Claude Docker uses dedicated SSH keys (separate from your personal keys for security):
Linux / macOS:
# 1. Create directory and generate key
CLAUDE_DOCKER_DIR="${CLAUDE_DOCKER_HOME:-$HOME/.claude-docker}"
mkdir -p "$CLAUDE_DOCKER_DIR/ssh"
ssh-keygen -t rsa -b 4096 -f "$CLAUDE_DOCKER_DIR/ssh/id_rsa" -N ''
# 2. Add public key to GitHub
cat "$CLAUDE_DOCKER_DIR/ssh/id_rsa.pub"
# Copy output and add to: GitHub → Settings → SSH and GPG keys → New SSH key
# 3. Test connection
ssh -T [email protected] -i "$CLAUDE_DOCKER_DIR/ssh/id_rsa"Windows (PowerShell):
# 1. Create directory and generate key
$sshDir = "$env:USERPROFILE\.claude-docker\ssh"
mkdir $sshDir -Force
ssh-keygen -t rsa -b 4096 -f "$sshDir\id_rsa" -N '""'
# 2. Add public key to GitHub
type "$sshDir\id_rsa.pub"
# Copy output and add to: GitHub → Settings → SSH and GPG keys → New SSH key
# 3. Test connection
ssh -T git@github.com -i "$sshDir\id_rsa"After installation, customize Claude's behavior by editing files in ${CLAUDE_DOCKER_HOME:-~/.claude-docker}/claude-home/:
# Linux / macOS
nano "${CLAUDE_DOCKER_HOME:-$HOME/.claude-docker}/claude-home/CLAUDE.md"
# Windows
notepad "%USERPROFILE%\.claude-docker\claude-home\CLAUDE.md"Important: The default CLAUDE.md includes the author's opinionated workflow preferences:
- Automatic codebase indexing on startup
- Task clarification protocols
- Conda environment execution standards
- Context.md maintenance requirements
- Telegram notification behaviors
These are NOT requirements of the Docker container - they're customizable prompt engineering. Change CLAUDE.md to match your workflow preferences.
# Linux / macOS
nano "${CLAUDE_DOCKER_HOME:-$HOME/.claude-docker}/claude-home/settings.json"
# Windows
notepad "%USERPROFILE%\.claude-docker\claude-home\settings.json"Configure Claude Code settings including:
- Timeouts: Bash command execution timeouts (default: 24 hours)
- MCP Timeout: MCP server response timeout (default: 60 seconds)
- Permissions: Auto-approved commands (ls, grep, find, etc.)
Recommended: Disable auto compact to gain 30% extra usable context window:
/config auto compact set to falseRun this command inside Claude Code to disable automatic context compaction.
This directory is mounted as ~/.claude inside the container, so you can also customize:
- Slash commands (
.claude/commands/) - Agent personas (
.claude/agents/) - All standard Claude Code customizations
MCP (Model Context Protocol) servers extend Claude's capabilities. Installation is simple - just add commands to mcp-servers.txt that you'd normally run in terminal.
Semantic code navigation and symbol manipulation with automatic project indexing.
Value: Better efficiency in retrieving and editing code means greater token efficiency and more usable context window.
Official, version-specific documentation straight from the source.
Value: Unhobble Claude Code by giving it up-to-date docs. Stale documentation is an artificial performance bottleneck.
Setup: Create a free API key at context7.com/dashboard and add it to your .env file as CONTEXT7_API_KEY.
Search real code examples on GitHub.
Value: When documentation is missing, rapidly search across GitHub for working implementations to understand different APIs and syntaxes for unfamiliar tasks.
Browser automation for web testing, scraping, and interaction.
Value: Claude can navigate websites, fill forms, take screenshots, and run end-to-end tests directly from the container. The Playwright MCP runs as a separate Docker container (mcr.microsoft.com/playwright/mcp) that is automatically started when you launch claude-docker.
Telegram bot notifications and two-way communication via mcp-communicator-telegram.
Value: Step away from your terminal. Claude sends you Telegram messages when tasks finish or when it needs input. You can reply via Telegram using ask_user, and Claude continues working. Notifications are driven by CLAUDE.md instructions — Claude uses the tools automatically when it completes work, hits errors, or reaches milestones.
These servers are pre-configured but commented out in mcp-servers.txt to keep the default setup lean. Uncomment to enable.
Multi-model code review and debugging using Gemini and other LLMs via OpenRouter.
Value: Different LLMs debating each other normally outperforms any single LLM. Zen supports conversation threading for collaborative AI discussions, second opinions, and model debates.
Why disabled by default: Each Zen tool adds significant tokens to context. For focused agentic coding, this overhead isn't worth it. Enable for "vibe coding" sessions where you want AI model collaboration.
To enable:
- Uncomment the Zen MCP line in
mcp-servers.txt - Add
OPENROUTER_API_KEYto your.envfile (get one at openrouter.ai) - Rebuild:
claude-docker --rebuild
Important: Only enable the tools you need - each tool is expensive in terms of context tokens. See the Zen MCP tools documentation for available tools and the .env.example for all supported environment variables.
Example mcp-servers.txt:
# Serena - Coding agent toolkit
claude mcp add-json "serena" '{"command":"bash","args":[...]}'
# Context7 - Documentation lookup (requires API key in .env)
claude mcp add -s user --transport http context7 https://mcp.context7.com/mcp --header "CONTEXT7_API_KEY: ${CONTEXT7_API_KEY}"
# Grep - GitHub code search (no API key needed)
claude mcp add -s user --transport http grep https://mcp.grep.app
# Telegram - Notifications and two-way chat (requires Telegram credentials in .env)
claude mcp add-json telegram -s user '{"command":"npx","args":["-y","mcp-communicator-telegram"],"env":{...}}'Each line is exactly what you'd type in your terminal to run that MCP server. The installation script handles the rest.
📋 See MCP_SERVERS.md for more examples and detailed setup instructions
- Complete AI coding agent setup - Claude Code in isolated Docker container
- Pre-configured MCP servers - Advanced coding tools, documentation lookup, Telegram notifications and easy set up to add more.
- Persistent conversation history - Resumes from where you left off with
--continue, even after crashes - Host machines conda envs - No need to waste time re setting up conda environments, host machines conda dirs are mounted and ready to use by claude docker.
- Simple one-command setup - Zero friction plug-and-play integration
- Fully customizable - Modify files at
${CLAUDE_DOCKER_HOME:-~/.claude-docker}for custom behavior
Want to start simple? Skip the pre-configured MCP servers and extra packages:
# Empty the file or delete unwanted entries
> mcp-servers.txt
claude-docker --rebuild --no-cache # For changes to take effect.Edit Dockerfile to remove packages you don't need:
# Line 8: Python
# Line 10: Git
# Other lines: Various system packages
nano Dockerfile
claude-docker --rebuild --no-cache # For changes to take effect.After installation, customize Claude's behavior:
nano "${CLAUDE_DOCKER_HOME:-$HOME/.claude-docker}/claude-home/CLAUDE.md"- Repository: https://github.com/LORDofDOOM/claude-docker
- Author: Vishal J (@VishalJ99)
This project is open source. See the LICENSE file for details.