Sign inSign up

spiriyu/claude-code-sandbox

By spiriyu

•Updated 6 months ago

Ready-to-use Docker sandbox for Claude Code with Node.js (nvm) and Python (pyenv) pre-installed

Image
Machine learning & AI
Developer tools
0

9.2K

spiriyu/claude-code-sandbox repository overview

⁠Docker Image Overview

Changelog

Tags are prefixed with the release version at publish time (e.g. 0.7.0_node24_python3.14). See Build Matrix & Tagging⁠ for the full scheme.

⁠What This Image Does

A ready-to-use sandbox for running Claude Code⁠ inside Docker. It provides a non-root dev user with Node.js (via nvm), Python (via pyenv), and the Claude Code CLI pre-installed.

⁠Base Image

Debian 12 (Bookworm) slim — chosen over Alpine for glibc compatibility with pyenv-built Python and native npm packages.

⁠Build Arguments

ArgumentDefaultDescription
USER_UID1000UID for the dev user
USER_GID1000GID for the dev user
NODE_VERSION24Node.js version installed via nvm
PYTHON_VERSION3.14Python version installed via pyenv

⁠Authentication

The entrypoint requires at least one credential to be set:

Environment VariableSource
ANTHROPIC_API_KEYconsole.anthropic.com/settings/keys⁠
CLAUDE_CODE_OAUTH_TOKENClaude Pro/Max subscription (claude login)

If neither is set, the container exits with an error message.

⁠Image Layers (build order)

1. System packages    — apt-get (curl, git, vim, jq, build-essential, etc.)
2. Locale             — en_US.UTF-8
3. Non-root user      — dev (UID/GID from build args, handles GID collisions)
4. Workspace          — /workspace owned by dev
5. Shell profiles     — /etc/profile.d/nvm.sh, /etc/profile.d/pyenv.sh
6. Entrypoint         — /entrypoint.sh (auth validation + exec)
7. nvm + Node.js      — /home/dev/.nvm
8. pyenv + Python     — /home/dev/.pyenv
9. Claude Code CLI    — globally installed via npm
10. Bootstrap config  — /home/dev/.claude.json (skips onboarding)

⁠Key Paths Inside the Container

PathPurpose
/workspaceDefault working directory, mount your code here
/home/dev/.nvmnvm installation + Node.js versions
/home/dev/.pyenvpyenv installation + Python versions
/home/dev/.claude.jsonBootstrap config (skips onboarding prompts)
/entrypoint.shAuth gate — validates credentials then execs CMD
/etc/profile.d/nvm.shLoads nvm in login shells
/etc/profile.d/pyenv.shLoads pyenv in login shells

⁠Entrypoint Behavior

  1. Checks that ANTHROPIC_API_KEY or CLAUDE_CODE_OAUTH_TOKEN is set (non-empty)
  2. If neither is present, prints an error with setup instructions and exits 1
  3. If valid, execs the CMD — signals (SIGTERM, SIGINT) pass directly to the process

Default CMD: bash -lc "claude --dangerously-skip-permissions"

⁠Shell Profiles

Login shells (bash -l, docker exec -it ... bash -l) automatically source:

  • nvm.sh — exports NVM_DIR, loads nvm and bash completion
  • pyenv.sh — exports PYENV_ROOT, adds pyenv to PATH, runs pyenv init

Both scripts are safe to source when nvm/pyenv binaries are absent (no errors).

⁠Build Matrix & Tagging

Defined in versions.json:

{
    "node": ["24.15.0", "22.22.2", "20.20.2"],
    "python": ["3.14", "3.13", "3.11"]
}

CI builds the Cartesian product (9 combinations). Tags follow the pattern:

{version}_node{N}_python{P}

The highest Node + highest Python combo also gets latest and the bare version tag.

Example tags for release 0.7.0:

0.7.0_node24.15.0_python3.14   <- also tagged: latest, 0.7.0
0.7.0_node24.15.0_python3.13
0.7.0_node24.15.0_python3.11
0.7.0_node22.22.2_python3.14
0.7.0_node22.22.2_python3.13
0.7.0_node22.22.2_python3.11
0.7.0_node20.20.2_python3.14
0.7.0_node20.20.2_python3.13
0.7.0_node20.20.2_python3.11

⁠Quick Reference

# Build locally
docker build -f image/Dockerfile \
  --build-arg USER_UID=$(id -u) \
  --build-arg USER_GID=$(id -g) \
  -t claude-code-sandbox .

# Run with API key
docker run -it --rm \
  -e ANTHROPIC_API_KEY=sk-ant-... \
  -v $(pwd):/workspace \
  spiriyu/claude-code-sandbox:latest

# Run with OAuth token
docker run -it --rm \
  -e CLAUDE_CODE_OAUTH_TOKEN=sk-ant-oat... \
  -v $(pwd):/workspace \
  spiriyu/claude-code-sandbox:latest

# Drop into bash instead of Claude
docker run -it --rm \
  -e ANTHROPIC_API_KEY=sk-ant-... \
  -v $(pwd):/workspace \
  spiriyu/claude-code-sandbox:latest bash -l

# Run tests
bash test/entrypoint.test.sh
bash test/profile.test.sh

⁠Docker Compose

Create a .env file with your credentials, then use this docker-compose.yml:

services:
    claude-code-sandbox:
        image: spiriyu/claude-code-sandbox:latest
        stdin_open: true # docker run -i
        tty: true # docker run -t
        env_file: .env # loads ANTHROPIC_API_KEY or CLAUDE_CODE_OAUTH_TOKEN
        volumes:
            - .:/workspace

.env:

ANTHROPIC_API_KEY=sk-ant-api03-...
# Start Claude Code interactively
docker compose run --rm claude-code-sandbox

# Drop into bash instead
docker compose run --rm claude-code-sandbox bash -l

# Run a one-off command
docker compose run --rm claude-code-sandbox bash -lc "claude -p 'explain this project'"

To match your host user's UID/GID (avoids file permission issues on mounted volumes), build locally:

services:
    claude-code-sandbox:
        build:
            context: .
            dockerfile: image/Dockerfile
            args:
                USER_UID: ${UID:-1000}
                USER_GID: ${GID:-1000}
        stdin_open: true
        tty: true
        env_file: .env
        volumes:
            - .:/workspace
# Build with your UID/GID and start
UID=$(id -u) GID=$(id -g) docker compose run --rm claude-code-sandbox

Tag summary

Content type

Image

Digest

sha256:fa03b6891…

Size

452.1 MB

Last updated

6 months ago

docker pull spiriyu/claude-code-sandbox