A secure Docker environment for running Claude Code on your projects with filesystem isolation and proper file ownership.
- Docker and Docker Compose installed
- A project directory you want to work on
- Anthropic API key OR Claude Pro/Max subscription
- Make the setup script executable:
chmod +x setup-and-run.sh- Run the setup script:
./setup-and-run.shThe script will:
- β Detect your host user ID (UID) and group ID (GID) automatically
- β Prompt you for your project directory
- β Prompt you for a project name (for settings isolation)
- β Validate the directory exists and project name is valid
- β
Create the
.envconfiguration file with your user IDs and project name - β Optionally save your API key
- β Build the Docker image with matching user/group
- β Start the container with isolated Claude settings
- β Drop you into an interactive shell
- Inside the container, start Claude Code:
claude- Create a
.envfile:
PROJECT_DIR=/absolute/path/to/your/project
PROJECT_NAME=myproject
USER_ID=1000
GROUP_ID=1000
USERNAME=node
ANTHROPIC_API_KEY=your-api-key-here
ANTHROPIC_MODEL=claude-sonnet-4-5-20250929Important: Get your actual user IDs and choose a project name:
id -u # Your USER_ID
id -g # Your GROUP_ID
basename /path/to/your/project # Suggested PROJECT_NAME- Build and start the container:
docker compose build
docker compose up -d
docker compose exec claude-code bash- Inside the container, run Claude Code:
claude- Your project is mounted at
/workspaceinside the container - Claude Code cannot access files outside this directory on your host
- Host system files (like
~/.ssh,~/.aws) are not accessible - Perfect for working on untrusted or experimental code
- Shared OAuth credentials: Authenticate once via
claude-credentialsvolume, use across all projects - Per-project settings: Each project gets its own
claude-settings-${PROJECT_NAME}volume - Settings stored in:
/home/node/.claude/(different volume per project) - Credentials symlinked from:
/home/node/.claude-shared/.credentials.json - Different projects can use different Claude accounts or API keys
- Prevents configuration conflicts between projects
- Easy to reset settings for a specific project without affecting others
Example:
# Project A uses OAuth with Claude Pro
PROJECT_NAME=projectA
# Volume: claude-settings-projectA
# Credentials: Shared via claude-credentials volume
# Project B uses API key
PROJECT_NAME=projectB
# Volume: claude-settings-projectB
# Credentials: Shared via claude-credentials volume
# Each project gets its own volume but shares OAuth credentials- Container runs as your host user (same UID/GID)
- Files created by Claude Code maintain your ownership
- No
root:rootownership issues - No need for
sudo chownafter using the container
How it works:
- Setup script detects your UID (e.g., 1000) and GID (e.g., 1000)
- Docker builds container with matching user
- Files created in
/workspaceare owned by your host user - Seamless workflow - edit files from host or container
Example:
# Before entering container
$ id
uid=1000(john) gid=1000(john)
# Inside container, Claude Code creates a file
$ echo "test" > /workspace/newfile.txt
# Back on host
$ ls -la ~/myproject/newfile.txt
-rw-r--r-- 1 john john 5 Oct 25 10:00 newfile.txt β
# NOT owned by root!The container is configured with:
- CPU limit: 2 cores
- Memory limit: 4GB
- Memory reservation: 1GB
Adjust these in docker-compose.yml as needed.
To completely disable network access (maximum security):
# In docker-compose.yml, uncomment:
network_mode: noneNote: This will prevent Claude Code from accessing the Anthropic API and OAuth authentication. Use only for offline testing with a pre-authenticated session.
docker compose start
docker compose exec claude-code bashdocker compose downdocker compose logs -fdocker compose restartdocker compose down
docker compose build --no-cache
docker compose up -dEach project gets its own isolated Claude Code settings. This allows you to:
- Use different Claude accounts for different projects
- Have different permission configurations per project
- Keep project-specific authentication tokens separate
- Run multiple projects simultaneously without port conflicts (when using non-OAuth mode)
Option 1: Run Projects Simultaneously (Recommended)
Since containers use random ports in non-OAuth mode, you can run multiple projects at the same time:
- Keep the current project running
- Navigate to a different project directory
- Clone this repository or copy the setup files
- Run
./setup-and-run.shwith the new project directory - Both containers run simultaneously without conflicts!
Option 2: Switch Projects (One at a Time)
- Stop the current container:
docker compose down- Update the
.envfile with the new project:
PROJECT_DIR=/path/to/different/project
PROJECT_NAME=different-project # Different name = different settings
# USER_ID and GROUP_ID remain the same- Start the container:
docker compose up -d
docker compose exec claude-code bashAlternative: Run the setup script again to interactively choose a new project. The script will automatically suggest a project name based on the directory path.
OAuth credentials are automatically shared across all projects via the claude-credentials volume.
If you want multiple projects to share the same settings (history, todos, permissions):
# In .env for both projects, use the same PROJECT_NAME
PROJECT_NAME=shared-config
# Both will use the same volume: claude-settings-shared-configBoth projects will use the same settings volume and permissions. OAuth credentials are shared regardless of PROJECT_NAME.
- PROJECT_DIR: Absolute path to your project directory (required)
- PROJECT_NAME: Unique name for this project's Claude settings (required)
- Must be a valid directory name (letters, numbers, dots, hyphens, underscores only)
- Default: Last directory name from PROJECT_DIR
- Used to create isolated volume:
claude-settings-${PROJECT_NAME} - Example:
myapp,client-project,experiment_1
- USER_ID: Your host user ID - auto-detected by setup script (required)
- GROUP_ID: Your host group ID - auto-detected by setup script (required)
- USERNAME: Container username, default is
node(required) - ANTHROPIC_API_KEY: Your Anthropic API key (optional if using OAuth)
- ANTHROPIC_MODEL: Claude model to use (default: claude-sonnet-4-5-20250929)
- OAUTH_MODE: (Optional, passed as command-line environment variable, NOT stored in .env)
- Controls port binding for OAuth authentication
- Set to
:8338for OAuth setup:OAUTH_MODE=":8338" docker compose up -d - Leave unset for normal use:
docker compose up -d(uses random port) - Only needed during initial OAuth authentication
- After authentication, restart without OAUTH_MODE to allow multiple projects simultaneously
The setup script automatically detects these, but if setting up manually:
# Find your user ID
id -u
# Output: 1000 (example)
# Find your group ID
id -g
# Output: 1000 (example)
# Find your username
whoami
# Output: john (example)
# Suggested project name (from your project path)
basename /path/to/your/yourproject
# Output: yourproject (example)Project Name Rules:
- Only letters (a-z, A-Z), numbers (0-9), dots (.), hyphens (-), and underscores (_)
- No spaces, slashes, or special characters
- Should be descriptive and unique per project
- Examples:
webapp,client-site,ml_experiment,project-2024
Settings are persisted in per-project Docker volumes (claude-settings-${PROJECT_NAME}). To configure permissions:
- Inside the container, create
~/.claude/settings.json:
# Inside container
mkdir -p ~/.claude
cat > ~/.claude/settings.json << 'EOF'
{
"permissions": {
"deny": [
"Read(./.env)",
"Read(./.env.*)",
"Read(./secrets/**)"
],
"ask": [
"Bash(git push:*)",
"Bash(docker:*)"
]
}
}
EOF- Or use the interactive command:
claude
/permissionsNote: Each project gets its own volume mounted at /home/node/.claude/, so each project can have different permission configurations. OAuth credentials are shared across all projects via symlink to /home/node/.claude-shared/.credentials.json.
Prerequisites:
- Active Claude Pro ($20/month) or Claude Max ($100/month) subscription
- Port 8338 must be exposed (already configured in docker-compose.yml)
Steps:
-
Leave
ANTHROPIC_API_KEYempty in your.envfile -
Start the container and run Claude Code:
docker compose up -d
docker compose exec claude-code bash
claude- Follow the OAuth flow (see OAuth Setup Guide)
Note: OAuth tokens are saved in the shared claude-credentials volume at /home/node/.claude-shared/.credentials.json and symlinked to each project's /home/node/.claude/.credentials.json. All projects share the same OAuth credentials by default.
-
Get your API key from https://console.anthropic.com/
-
Set
ANTHROPIC_API_KEYin your.envfile:
ANTHROPIC_API_KEY=sk-ant-api03-your-key-here- Restart the container:
docker compose down
docker compose up -dIf your OAuth token expires or you need to authenticate with a different account:
Quick method (with setup script):
# Stop current container
docker compose down
# Run setup script again - it handles OAuth mode automatically
./setup-and-run.sh
# After OAuth completes, the script will offer to restart in non-OAuth modeManual method:
- Stop the container:
docker compose down- Start in OAuth mode (required for authentication):
OAUTH_MODE=":8338" docker compose up -d
docker compose exec claude-code bash- Inside container, remove old authentication and re-authenticate:
# Remove shared credentials (affects all projects)
rm -f ~/.claude-shared/.credentials.json
# Or remove this project's entire settings (keeps credentials)
rm -rf ~/.claude/*
# Start Claude Code and authenticate
claude
# Choose option 2 (OAuth)
# Follow the authentication steps- After authentication completes, exit and restart in normal mode:
exit
docker compose down && docker compose up -dWhy restart in non-OAuth mode?
- Uses a random host port instead of fixed port 8338
- Allows multiple projects to run simultaneously without port conflicts
- OAuth authentication continues to work (tokens are already saved)
- Port 8338 is only needed during the initial authentication flow
The container includes:
- Node.js 20 (Alpine Linux)
- npm (latest version)
- Claude Code CLI (latest version)
- Python 3 with pip (from Alpine repos)
- Git
- Common development tools (bash, curl, vim, nano)
- SSH client
- Build tools (gcc, make, etc.)
- sudo (configured for passwordless access)
Ensure you're using the absolute path to your project:
# Good
PROJECT_DIR=/home/username/projects/myapp
# Bad (relative paths don't work)
PROJECT_DIR=./myapp
PROJECT_DIR=~/projects/myapp # Tilde may not expand in .envThis should not happen with the current setup. If it does:
- Check your
.envfile has correct USER_ID and GROUP_ID:
cat .env | grep -E "USER_ID|GROUP_ID"- Verify they match your host user:
id -u # Should match USER_ID in .env
id -g # Should match GROUP_ID in .env- Rebuild the container with correct IDs:
docker compose down
docker compose build --no-cache
docker compose up -dThe container runs as your user, so you should have the same permissions as on the host.
If you need root access inside the container:
# The container user has passwordless sudo
docker compose exec claude-code sudo <command># Check logs
docker compose logs
# Rebuild from scratch (WARNING: removes volumes)
docker compose down -v
docker compose build --no-cache
docker compose up -dNote: Using -v flag removes ALL volumes including:
- The current project's settings volume:
claude-settings-${PROJECT_NAME} - The shared credentials volume:
claude-credentials(if no other containers use it)
To remove only the current project's settings volume:
# From host machine:
docker volume rm claude-settings-${PROJECT_NAME}
# Or to clear settings without removing volume (from inside container):
rm -rf ~/.claude/*- For OAuth: Ensure port 8338 is not blocked and you complete the browser flow
- For API key: Verify your key is correct at https://console.anthropic.com/
- Check internet connectivity:
docker compose exec claude-code curl https://api.anthropic.com
This is normal and handled by the Dockerfile. The build should continue successfully despite these messages. If the build actually fails:
- Check the full error message:
docker compose build --no-cache 2>&1 | tee build.log- See the Docker Build Troubleshooting guide
- Verify port 8338 is exposed in docker-compose.yml
- Make sure
network_mode: noneis commented out - Try manually opening the URL in your browser
- Check firewall isn't blocking localhost:8338
- Testing Claude Code on unfamiliar projects
- Working on open-source code
- Experimenting with AI-generated code
- Learning Claude Code features
- Keeping your host system clean
- Preventing accidental file ownership issues
- Projects requiring access to host SSH keys (mount them explicitly if needed)
- Projects needing host environment variables (pass them via docker-compose.yml)
- Workflows requiring host Docker access (requires Docker-in-Docker setup)
- Projects with database connections on host (use host networking or bridge network)
- Always use Git in your project directory for easy rollback
- Use descriptive project names - makes it clear which settings belong to which project
- Review changes before committing Claude Code's modifications
- Start with plan mode to understand what Claude wants to do
- Set permission denials for sensitive files in settings.json (per-project)
- Limit resources appropriately in docker-compose.yml
- Regular backups of your project directory
- Use the setup script to ensure correct UID/GID mapping and project name
- Test file ownership after first use to verify setup is correct
- Separate projects = separate settings - use different PROJECT_NAME values
API Key (Pay-per-use):
- Claude Sonnet 4.5: $3/million input tokens, $15/million output tokens
- Typical session: $0.01 - $0.50 depending on complexity
- Monthly cost varies with usage
Claude Pro (OAuth):
- $20/month fixed cost
- Includes Claude Code usage
- Better for regular daily use
Claude Max (OAuth):
- $100/month fixed cost
- Higher rate limits
- Best for heavy usage
Recommendation: For daily development work, Claude Pro with OAuth is typically more economical than API pay-per-use.
- Claude Code Documentation
- Claude Code Settings
- Anthropic Console
- Anthropic Pricing
- OAuth Setup Guide (detailed authentication instructions)
This project is licensed under the MIT License - see the LICENSE file for details.
This software is provided "as-is" without any warranty. The author is not liable for any damages or issues arising from the use of this Docker setup. Use at your own risk.
Important Security Notice:
This Docker setup provides filesystem isolation but should not be considered a complete security solution.
Users are responsible for:
- Reviewing and understanding the code before use
- Configuring appropriate permissions for their use case
- Backing up their projects before using Claude Code
- Monitoring API costs and usage
- Complying with Anthropic's Terms of Service
- Verifying file ownership is preserved correctly
No liability is assumed for:
- Data loss or corruption
- Unexpected API costs
- Security vulnerabilities
- File ownership or permission issues
- Any damages resulting from use of this software
Recommendation: Always use version control (Git) and test in isolated environments first.
- Claude Code by Anthropic
- Docker and Docker Compose
- The open-source community
Questions or issues? Check the troubleshooting guides or review the documentation links above.