Claude Code Advanced
35 minutes
linux
macos
windows

Claude Code in Docker

Set up isolated Docker environments for running Claude Code safely and consistently.

What you'll accomplish

Run Claude Code in Docker containers with proper configuration and volume mounting.

Prerequisites

  • Docker installed and working
  • Understanding of permissions (lesson 3)
  • Basic Docker knowledge

Why Use Docker with Claude Code?

Running Claude Code in Docker provides:

BenefitDescription
IsolationChanges can't affect your system
ReproducibilitySame environment everywhere
SafetyUse --dangerously-skip-permissions safely
Clean slateFresh environment each time
Team consistencyEveryone runs the same setup

Basic Setup

Simple Dockerfile

Start with a minimal configuration:

dockerfile
# Dockerfile
FROM node:20-slim

# Install git (needed for most projects)
RUN apt-get update && apt-get install -y git && rm -rf /var/lib/apt/lists/*

# Install Claude Code
RUN npm install -g @anthropic-ai/claude-code

# Set up workspace
WORKDIR /workspace

# Run as non-root for security
RUN useradd -m -s /bin/bash claude && chown -R claude:claude /workspace
USER claude

# Default command
CMD ["claude"]

Build and Run

bash
# Build the image
docker build -t claude-code-dev .

# Run interactively
docker run -it \
  -v $(pwd):/workspace \
  -e ANTHROPIC_API_KEY=$ANTHROPIC_API_KEY \
  claude-code-dev

Full-Featured Setup

A production-ready Dockerfile with more tools:

dockerfile
# Dockerfile
FROM node:20-bookworm

# System dependencies
RUN apt-get update && apt-get install -y \
    git \
    curl \
    jq \
    ripgrep \
    fd-find \
    && rm -rf /var/lib/apt/lists/*

# Install Claude Code
RUN npm install -g @anthropic-ai/claude-code

# Install common global tools
RUN npm install -g \
    typescript \
    prettier \
    eslint

# Create non-root user
RUN useradd -m -s /bin/bash claude
RUN mkdir -p /home/claude/.claude && chown -R claude:claude /home/claude

# Set up workspace
WORKDIR /workspace
RUN chown claude:claude /workspace

# Switch to non-root user
USER claude

# Default environment
ENV NODE_ENV=development

# Default to interactive claude
CMD ["claude", "--dangerously-skip-permissions"]
Note

Notice --dangerously-skip-permissions in the CMD. This is safe because we're in an isolated container.

Docker Compose

For complex setups, use Docker Compose:

yaml
# docker-compose.yml
version: '3.8'

services:
  claude:
    build: .
    volumes:
      - ./project:/workspace
      - claude-cache:/home/claude/.claude
    environment:
      - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}
    stdin_open: true
    tty: true

volumes:
  claude-cache:

Running

bash
# Start Claude Code
docker-compose run claude

# With a specific prompt
docker-compose run claude claude -p "Fix the linting errors"

# Build fresh
docker-compose build --no-cache

Volume Mounting Strategies

Project Only (Recommended)

Mount only your project directory:

bash
docker run -it \
  -v ./my-project:/workspace \
  -e ANTHROPIC_API_KEY=$ANTHROPIC_API_KEY \
  claude-code-dev

Pros: Minimal access, maximum safety Cons: Can't access other projects

Named Volumes for Cache

Persist Claude's cache between runs:

bash
docker run -it \
  -v ./my-project:/workspace \
  -v claude-cache:/home/claude/.claude \
  -e ANTHROPIC_API_KEY=$ANTHROPIC_API_KEY \
  claude-code-dev

Pros: Faster subsequent runs, settings persist Cons: Slightly more complex

Read-Only Mounts

Prevent Claude from modifying certain paths:

bash
docker run -it \
  -v ./my-project:/workspace \
  -v ./reference:/reference:ro \
  -e ANTHROPIC_API_KEY=$ANTHROPIC_API_KEY \
  claude-code-dev

The :ro makes /reference read-only.

Including Configuration Files

Method 1: Copy in Dockerfile

dockerfile
# Copy global settings
COPY --chown=claude:claude .claude/ /home/claude/.claude/

# Copy project settings
COPY --chown=claude:claude AGENTS.md /workspace/

Method 2: Mount at Runtime

bash
docker run -it \
  -v ./project:/workspace \
  -v ~/.claude:/home/claude/.claude \
  -e ANTHROPIC_API_KEY=$ANTHROPIC_API_KEY \
  claude-code-dev

Method 3: Environment Variables

bash
docker run -it \
  -v ./project:/workspace \
  -e ANTHROPIC_API_KEY=$ANTHROPIC_API_KEY \
  -e CLAUDE_CODE_MODEL=claude-opus-4-5-20251101 \
  claude-code-dev

Multi-Stage Build for Smaller Images

Reduce image size with multi-stage builds:

dockerfile
# Build stage
FROM node:20-bookworm AS builder

RUN npm install -g @anthropic-ai/claude-code

# Runtime stage
FROM node:20-slim

# Copy only what we need
COPY --from=builder /usr/local/lib/node_modules /usr/local/lib/node_modules
COPY --from=builder /usr/local/bin/claude /usr/local/bin/claude

# Minimal runtime dependencies
RUN apt-get update && apt-get install -y git && rm -rf /var/lib/apt/lists/*

WORKDIR /workspace
RUN useradd -m claude && chown claude:claude /workspace
USER claude

CMD ["claude", "--dangerously-skip-permissions"]

Development Workflow

1. Create Project-Specific Docker Setup

In your project:

my-project/
├── docker/
│   ├── Dockerfile
│   └── docker-compose.yml
├── src/
├── package.json
└── AGENTS.md

2. Add Helper Scripts

bash
#!/bin/bash
# scripts/claude-docker.sh

docker run -it \
  -v "$(pwd)":/workspace \
  -v claude-cache:/home/claude/.claude \
  -e ANTHROPIC_API_KEY="$ANTHROPIC_API_KEY" \
  claude-code-dev "$@"

Make it executable:

bash
chmod +x scripts/claude-docker.sh

Use it:

bash
./scripts/claude-docker.sh  # Interactive
./scripts/claude-docker.sh -p "Run tests"  # With prompt

3. Add npm Script

json
{
  "scripts": {
    "claude": "docker-compose run claude",
    "claude:build": "docker-compose build"
  }
}

Now use npm run claude.

CI/CD Integration

GitHub Actions

yaml
# .github/workflows/claude-fix.yml
name: Auto-fix with Claude

on:
  workflow_dispatch:
    inputs:
      prompt:
        description: 'What should Claude do?'
        required: true
        default: 'Fix the linting errors'

jobs:
  claude-fix:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Run Claude Code
        run: |
          docker run --rm \
            -v ${{ github.workspace }}:/workspace \
            -e ANTHROPIC_API_KEY=${{ secrets.ANTHROPIC_API_KEY }} \
            claude-code-dev \
            claude --dangerously-skip-permissions -p "${{ github.event.inputs.prompt }}"

      - name: Commit changes
        run: |
          git config user.name "Claude Code"
          git config user.email "[email protected]"
          git add -A
          git diff --staged --quiet || git commit -m "Auto-fix by Claude Code"
          git push

GitLab CI

yaml
# .gitlab-ci.yml
claude-review:
  image: claude-code-dev
  script:
    - claude --dangerously-skip-permissions -p "Review the code and suggest improvements"
  variables:
    ANTHROPIC_API_KEY: $ANTHROPIC_API_KEY

Security Considerations

1. Never Mount Sensitive Directories

bash
# DANGEROUS - don't do this
docker run -it -v ~/:/workspace claude-code-dev

# DANGEROUS - gives access to credentials
docker run -it -v ~/.ssh:/home/claude/.ssh claude-code-dev

2. Use Secrets Management

yaml
# docker-compose.yml with Docker secrets
services:
  claude:
    build: .
    secrets:
      - anthropic_key
    environment:
      - ANTHROPIC_API_KEY_FILE=/run/secrets/anthropic_key

secrets:
  anthropic_key:
    file: ./secrets/anthropic_key.txt

3. Network Isolation

Limit network access if Claude doesn't need it:

bash
docker run -it \
  --network=none \  # No network access
  -v ./project:/workspace \
  -e ANTHROPIC_API_KEY=$ANTHROPIC_API_KEY \
  claude-code-dev

Or restrict to specific hosts:

yaml
services:
  claude:
    networks:
      - claude-net

networks:
  claude-net:
    driver: bridge

4. Resource Limits

Prevent runaway processes:

bash
docker run -it \
  --memory=4g \
  --cpus=2 \
  -v ./project:/workspace \
  -e ANTHROPIC_API_KEY=$ANTHROPIC_API_KEY \
  claude-code-dev

Common Issues

Frequently Asked Questions

On Linux, pass your user/group IDs:

bash
docker run -it --user $(id -u):$(id -g) ...

Or in Docker Compose:

yaml
services:
  claude:
    user: "${UID}:${GID}"

Make sure git is installed in the container and the .git folder is mounted:

bash
docker run -it -v ./project:/workspace claude-code-dev
# The .git folder should be inside ./project

Changes in the container are lost unless you:

  1. Mount a volume for that directory
  2. Commit the container to a new image
  3. Use named volumes for persistence

Always mount your project directory as a volume.

Use multi-stage builds and slim base images:

dockerfile
FROM node:20-slim  # Instead of node:20

Clean up after apt-get:

dockerfile
RUN apt-get update && apt-get install -y git \
    && rm -rf /var/lib/apt/lists/*

Check:

  1. Environment variable is passed: -e ANTHROPIC_API_KEY=$ANTHROPIC_API_KEY
  2. Variable is set in your shell: echo $ANTHROPIC_API_KEY
  3. No quotes issues: use "$ANTHROPIC_API_KEY" not '$ANTHROPIC_API_KEY'

Verify Your Understanding

You've completed this lesson if you can:

0/5

Next: Advanced MCP integration for connecting Claude to external tools.