Run claude code in somewhat safe and isolated yolo mode
- 🚀 Standalone Script: Single file contains everything - Dockerfile, MCP servers, configuration
- 🤖 Pre-configured MCP Servers: Unsplash, Context7, and Playwright ready to use
- 🔧 Auto-build: Automatically builds Docker image if it doesn't exist
- 🔒 Secure: Host system protected by Docker boundaries with read-only mounts
- ⚡ Fast Setup: No manual Docker builds or MCP configuration needed
- 🔄 Persistent Container: Reuses existing container for faster startup
- Quick Start
- Script Options
- What's Included
- Testing the Setup
- Advanced Usage
- Security Notes
- Troubleshooting
- How It Works
- Contributing
- Visual Workflow
- Docker installed and running
- Claude authentication configured (
claude auth
) - Environment variables for MCP servers (e.g.,
UNSPLASH_ACCESS_KEY
)
# Download the script (single file needed)
curl -O https://raw.githubusercontent.com/icanhasjonas/run-claude-docker/main/run-claude.sh
chmod +x run-claude.sh
# Interactive shell (auto-pulls from Docker Hub on first run, reuses existing container)
./run-claude.sh
# Run specific command
./run-claude.sh claude --dangerously-skip-permissions "analyze this codebase"
# Custom workspace
./run-claude.sh -w /path/to/project
# If run-claude.sh fails due to architecture issues, build locally
./run-claude.sh --build
# Build Docker image and exit
./run-claude.sh --build
# Force rebuild image and continue
./run-claude.sh --rebuild
# Custom workspace
./run-claude.sh -w /path/to/project
# Custom Claude config path
./run-claude.sh -c /path/to/.claude
# Custom container name (default: claude-code)
./run-claude.sh -n my-claude-container
# Custom image name
./run-claude.sh -i my-claude:v1.0
# One-shot with cleanup (removes container after exit)
./run-claude.sh --rm --no-interactive claude auth status
# Safe mode (no dangerous permissions)
./run-claude.sh --safe
# Non-interactive mode
./run-claude.sh --no-interactive
# Recreate container (remove existing and create new)
./run-claude.sh --recreate
# Help
./run-claude.sh --help
By default, the script creates a persistent container named claude-code
that is reused across runs:
- First run: Creates and starts the container
- Subsequent runs: Reuses the existing container for faster startup
- Container running: Executes commands in the running container
- Container stopped: Restarts the existing container preserving all changes
This behavior significantly reduces startup time and preserves any modifications made inside the container (installed packages, configuration changes, etc.).
The script contains a complete Dockerfile that includes:
- Ubuntu 22.04 base image
- Claude Code installation
- Go, Node.js, Python, and build tools
- Pre-built Unsplash MCP server
- All MCP servers pre-configured
Automatically configured and ready to use:
- Unsplash: Photo search and download (
unsplash-mcp-server
) - Context7: AI context service (
https://mcp.context7.com/mcp
) - Playwright: Browser automation (
@playwright/mcp@latest
)
CLAUDE_CODE_IMAGE_NAME
- Override default Docker Hub image (default:icanhasjonas/claude-code
)
UNSPLASH_ACCESS_KEY
OPENAI_API_KEY
NUGET_API_KEY
CLAUDE_DANGEROUS_MODE=1
NODE_OPTIONS=--max-old-space-size=8192
TERM
- Terminal settings for proper color support
The script automatically forwards Claude authentication and OAuth credentials from your host system:
- OAuth Account: Preserves your Claude login session
- User Settings: Maintains preferences and onboarding state
- Permissions: Automatically enables bypass permissions mode in container
- Subscription: Forwards subscription and access cache information
This ensures seamless authentication without needing to re-login inside the container.
Automatically mounted:
- Workspace:
$(pwd)
→/home/$(whoami)/workspace
- Claude config:
~/.claude
→/home/$(whoami)/.claude
- SSH keys:
~/.ssh
→/home/$(whoami)/.ssh
(read-only) - Git config:
~/.gitconfig
→/home/$(whoami)/.gitconfig
(read-only)
The script makes a best effort to forward your Claude authentication into the container session:
- Seamless Login: Your existing Claude authentication is automatically available (after initial setup)
- OAuth Preservation: Maintains your logged-in state and subscription access
- Config Merging: Intelligently merges host Claude configuration with container settings
- Permission Bypass: Automatically enables bypass permissions mode for streamlined operation
Important: On your first run, you may need to run claude /login
inside the container. After that initial authentication, your login state is preserved and forwarded automatically for future runs.
# First run will automatically pull the pre-built image from Docker Hub
./run-claude.sh claude auth status
# If not authenticated, you'll need to login (usually only required once)
./run-claude.sh claude /login
# Test MCP servers are working
./run-claude.sh claude "search for sunset photos on unsplash"
# Build image only (useful for CI/CD)
./run-claude.sh --build
# Force rebuild (get latest updates)
./run-claude.sh --rebuild
# Create test project
mkdir test-project
cd test-project
echo "console.log('hello');" > test.js
# Test Claude with file modification
./run-claude.sh claude --dangerously-skip-permissions "add error handling to test.js"
# Check if file persists on host
cat test.js
# Test Unsplash MCP
./run-claude.sh claude "find a photo of mountains using unsplash"
# Test Playwright MCP
./run-claude.sh claude "take a screenshot of google.com"
# Use custom image name
./run-claude.sh -i my-claude:v1.0
# Build with custom name
./run-claude.sh -i my-claude:v1.0 --build
# Show docker command being executed
RUN_CLAUDE_VERBOSE=1 ./run-claude.sh
# Example output:
# Running Claude Code container...
# Command: docker run --rm -it --privileged --name claude-code-1234567890 ...
# Set required environment variables
export UNSPLASH_ACCESS_KEY="your-key-here"
export OPENAI_API_KEY="your-openai-key"
# Use custom Docker image
export CLAUDE_CODE_IMAGE_NAME="myregistry/my-claude-code"
./run-claude.sh
# Or use .env file approach
echo "UNSPLASH_ACCESS_KEY=your-key" >> ~/.bashrc
source ~/.bashrc
- ✅ Host isolation: Host system protected by Docker boundaries
- ✅ Read-only mounts: SSH keys and system configs mounted read-only
- ✅ User isolation: Runs as non-root user inside container
⚠️ Privileged mode: Required for dangerous permissions functionality
- Container has
--privileged
flag for full system access within container - Claude runs with
--dangerously-skip-permissions
by default - Only use with trusted code and repositories
- All file modifications are contained within mounted volumes
- Only mount directories you want Claude to access
- Use read-only mounts for sensitive configs
- Regularly rebuild image for security updates
- Monitor container resource usage
- Use temporary containers (
--rm
) for one-shot commands
# Fix workspace permissions
docker run --rm -v $(pwd):/workspace claude-code:latest sudo chown -R claude:claude /workspace
First Time Setup:
On your very first run, you may need to authenticate Claude inside the container:
# Check Claude authentication status
./run-claude.sh claude auth status
# If not authenticated, login (this is usually only needed once)
./run-claude.sh claude /login
After the initial /login
, the script automatically forwards your authentication between the host and container, so you shouldn't need to re-authenticate in future runs.
Note: The authentication forwarding works most of the time but isn't bulletproof. If you encounter auth issues, try running /login
again inside the container.
Troubleshooting Authentication:
# Check Claude config
./run-claude.sh claude auth status
# Re-authenticate if needed
./run-claude.sh claude auth
# Or use the web login method
./run-claude.sh claude /login
# Force rebuild image
./run-claude.sh --rebuild
# Or build only
./run-claude.sh --build
# Check existing images
docker images | grep claude
# List containers
docker ps -a | grep claude
# Stop persistent container
docker stop claude-code
# Remove persistent container
docker rm claude-code
# Or use the script to recreate
./run-claude.sh --recreate
┌─────────────────────────────────────────────────────────────────────────────────┐
│ HOST SYSTEM │
├─────────────────────────────────────────────────────────────────────────────────┤
│ ./run-claude.sh │
│ │ │
│ ├─ 1. Check if Docker image exists │
│ │ ├─ NO → Build embedded Dockerfile │
│ │ └─ YES → Continue │
│ │ │
│ ├─ 2. Check if container exists │
│ │ ├─ RUNNING → Execute in existing container │
│ │ ├─ STOPPED → Start existing container (preserves state) │
│ │ └─ MISSING → Create new container │
│ │ │
│ └─ 3. Mount volumes & forward env vars │
│ │ │
│ ▼ │
├─────────────────────────────────────────────────────────────────────────────────┤
│ DOCKER CONTAINER (Ubuntu 25.04 + Claude + MCP) │
│ │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
│ │ Unsplash MCP │ │ Context7 MCP │ │ Playwright MCP │ │
│ │ (Pre-built) │ │ (HTTP/Web) │ │ (npm global) │ │
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
│ │ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ CLAUDE CODE │ │
│ │ (--dangerously-skip-permissions) │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ ZSH + Oh-My-Zsh + LazyVim + Dev Tools │ │
│ │ • Node.js (via fnm) • Go • Python • Git • Build tools │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ MOUNTED VOLUMES (Read/Write): │
│ • ~/.claude → Container config │
│ • $(pwd) → Working directory │
│ │
│ MOUNTED VOLUMES (Read-Only): │
│ • ~/.ssh → SSH keys │
│ • ~/.gitconfig → Git configuration │
│ │
│ ENV FORWARDED: │
│ • API Keys (Unsplash, OpenAI, etc.) │
│ • CLAUDE_DANGEROUS_MODE=1 │
│ • Claude Authentication & OAuth │
│ • Terminal settings (TERM) │
└─────────────────────────────────────────────────────────────────────────────────┘
🔒 ISOLATION BENEFITS:
✅ Host system protected by Docker boundaries
✅ All dangerous operations contained in container
✅ Persistent containers with preserved state
✅ Pre-configured MCP servers ready to use
⚠️ YOLO MODE:
• Container runs with --privileged flag
• Claude runs with --dangerously-skip-permissions
• Use only with trusted projects!
The run-claude.sh
script is completely self-contained:
- Embedded Dockerfile: Contains a complete Ubuntu 22.04 setup with Claude Code
- Auto-detection: Checks if Docker image exists, pulls from Docker Hub if missing, builds only with
--build
or--rebuild
- Container Persistence: Reuses existing
claude-code
container for faster startup - MCP Setup: Automatically configures Unsplash, Context7, and Playwright servers
- Environment Forwarding: Passes through API keys, configurations, and Claude authentication
- Volume Management: Mounts workspace and config directories automatically
- User Matching: Creates container user matching your host user
No separate files needed - just the single run-claude.sh
script!
Pull requests are welcome! Feel free to contribute improvements, bug fixes, or new features.
The Dockerfile is embedded directly in the run-claude.sh
script to maintain the self-contained nature of the tool. When making changes to the container configuration:
-
Edit the embedded Dockerfile in the
generate_dockerfile_content()
function -
Test your changes by rebuilding the container:
# Build new image and test (doesn't run container) ./run-claude.sh --build # Or rebuild and run container immediately ./run-claude.sh --rebuild
-
Export for standalone use (optional):
# Export current Dockerfile for inspection or external use ./run-claude.sh --export-dockerfile Dockerfile
- Single source of truth: The
generate_dockerfile_content()
function contains the authoritative Dockerfile - No separate Dockerfile: Everything is embedded to maintain the self-contained design
- Always test rebuilds: After changing container configuration, use
--rebuild
to test - Both build options available:
--build
: Just builds the image (useful for testing build process)--rebuild
: Builds image and runs container (full testing)
# After editing the embedded Dockerfile:
# Option 1: Build only (test build process)
./run-claude.sh --build
# Option 2: Rebuild and test (full workflow)
./run-claude.sh --rebuild
# Option 3: Export and inspect
./run-claude.sh --export-dockerfile debug.dockerfile
less debug.dockerfile
This workflow ensures that the container changes are properly tested while maintaining the tool's self-contained design.
flowchart TD
Start([🚀 ./run-claude.sh]) --> CheckImage{🐳 Docker Image<br/>Exists?}
CheckImage -->|No| PullImage[📥 Try Pull from<br/>Docker Hub]
PullImage --> PullSuccess{Pull Success?}
PullSuccess -->|Yes| TagImage[🏷️ Tag as<br/>claude-code:latest]
PullSuccess -->|No| BuildImage[🔨 Build from<br/>Embedded Dockerfile]
TagImage --> CheckContainer
BuildImage --> CheckContainer
CheckImage -->|Yes| CheckContainer{📦 Container<br/>Exists?}
CheckContainer -->|Missing| CreateContainer[⚡ Create New Container<br/>with Volumes & Env]
CheckContainer -->|Stopped| StartContainer[♻️ Start Existing<br/>Container<br/><small>🎯 Preserves State</small>]
CheckContainer -->|Running| ExecContainer[🔄 Execute in<br/>Running Container]
CreateContainer --> RunCommand{💻 Command<br/>Provided?}
StartContainer --> RunCommand
ExecContainer --> RunCommand
RunCommand -->|Yes| ExecuteCmd[⚡ Execute:<br/>claude --dangerously-skip-permissions]
RunCommand -->|No| InteractiveShell[🐚 Interactive Shell<br/>zsh + oh-my-zsh]
ExecuteCmd --> MCPServers[🤖 MCP Servers Available]
InteractiveShell --> MCPServers
MCPServers --> Unsplash[📸 Unsplash<br/>Photo Search]
MCPServers --> Context7[🧠 Context7<br/>AI Context]
MCPServers --> Playwright[🎭 Playwright<br/>Browser Automation]
Unsplash --> WorkInContainer[🛠️ Work in Isolated<br/>Container Environment]
Context7 --> WorkInContainer
Playwright --> WorkInContainer
WorkInContainer --> PersistChanges[💾 Changes Persist<br/>in Container]
PersistChanges --> End([✅ Complete])
%% Styling
classDef startEnd fill:#e1f5fe,stroke:#01579b,stroke-width:3px,color:#000
classDef decision fill:#fff3e0,stroke:#e65100,stroke-width:2px,color:#000
classDef process fill:#f3e5f5,stroke:#4a148c,stroke-width:2px,color:#000
classDef container fill:#e8f5e8,stroke:#2e7d32,stroke-width:2px,color:#000
classDef mcp fill:#fff8e1,stroke:#f57f17,stroke-width:2px,color:#000
class Start,End startEnd
class CheckImage,PullSuccess,CheckContainer,RunCommand decision
class PullImage,TagImage,BuildImage,CreateContainer,StartContainer,ExecContainer,ExecuteCmd,InteractiveShell,WorkInContainer,PersistChanges process
class MCPServers,Unsplash,Context7,Playwright mcp