
Docker Sandbox
- 97 installs
- 61 repo stars
- Updated August 4, 2026
- joelhooks/joelclaw
Run claude and codex agent tools inside Docker Desktop sandboxes for isolated execution using existing Max/Pro subscription auth instead of API keys.
About
Creates and manages Docker sandboxes to run agent tools in process isolation with bidirectional workspace mounts. A developer uses it when running agent loops or subprocesses that need safe, isolated code execution.
- Pre-warm sandbox at loop start, exec per story (~90ms), destroy at end
- Injects Claude setup-token and codex auth.json from agent-secrets, with host-mode fallback
Docker Sandbox by the numbers
- 97 all-time installs (skills.sh)
- Ranked #554 of 1,435 DevOps & CI/CD skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/joelhooks/joelclaw --skill docker-sandboxAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 97 |
|---|---|
| repo stars | ★ 61 |
| Last updated | August 4, 2026 |
| Repository | joelhooks/joelclaw ↗ |
What it does
Run claude and codex agent tools inside Docker Desktop sandboxes for isolated execution using existing Max/Pro subscription auth instead of API keys.
Files
Docker Sandbox for Agent Tools
Isolated execution of claude, codex, and other agent tools using Docker Desktop's docker sandbox (v0.11.0+). Uses existing Claude Max and ChatGPT Pro subscriptions — no API key billing.
ADR: ADR-0023
Prerequisites
- Docker Desktop running (OrbStack works)
docker sandbox versionreturns ≥0.11.0- Auth secrets stored in
agent-secrets: claude_setup_token— fromclaude setup-token(1-year token, Max subscription)codex_auth_json— contents of~/.codex/auth.json(ChatGPT Pro subscription)
Quick Reference
# Create a sandbox
docker sandbox create --name my-sandbox claude /path/to/project
# Run a command in it
docker sandbox exec -e "CLAUDE_CODE_OAUTH_TOKEN=..." -w /path/to/project my-sandbox \
claude -p "implement the feature" --output-format text --dangerously-skip-permissions
# List sandboxes
docker sandbox ls
# Remove
docker sandbox rm my-sandboxAuth Setup (One-Time)
Claude (Max subscription)
Run interactively on the host (needs browser for OAuth):
claude setup-tokenThis opens a browser, completes OAuth, and prints a token like sk-ant-oat01-.... Valid for 1 year.
Store it:
secrets add claude_setup_token --value "sk-ant-oat01-..."Use in sandbox:
TOKEN=$(secrets lease claude_setup_token --ttl 1h --raw)
docker sandbox exec -e "CLAUDE_CODE_OAUTH_TOKEN=$TOKEN" my-sandbox claude auth status
# → loggedIn: true, authMethod: oauth_tokenCodex (ChatGPT Pro subscription)
Authenticate codex locally (needs browser):
codex # Select "Sign in with ChatGPT", complete OAuthThe auth file at ~/.codex/auth.json is portable (not host-tied). Store it:
secrets add codex_auth_json --value "$(cat ~/.codex/auth.json)"Inject into sandbox:
AUTH=$(secrets lease codex_auth_json --ttl 1h --raw)
docker sandbox exec my-sandbox bash -c "mkdir -p ~/.codex && cat > ~/.codex/auth.json << 'EOF'
${AUTH}
EOF"Token Refresh
| Token | Lifetime | Refresh |
|---|---|---|
claude_setup_token | 1 year | Run claude setup-token again, update secret |
codex_auth_json | Until subscription change | Re-run codex login if auth fails, update secret |
Agent Loop Integration
Pre-warm Pattern
Create sandbox(es) at loop start, reuse for all stories, destroy at loop end.
PLANNER (loop start)
├── docker sandbox create --name loop-{loopId}-claude claude {workDir}
├── docker sandbox create --name loop-{loopId}-codex codex {workDir} # if needed
└── inject auth into both
IMPLEMENTOR / TEST-WRITER / REVIEWER (per story)
└── docker sandbox exec -w {workDir} -e CLAUDE_CODE_OAUTH_TOKEN=... loop-{loopId}-{tool} \
{tool command}
# ~90ms overhead, workspace changes visible on host immediately
COMPLETE / CANCEL (loop end)
├── docker sandbox rm loop-{loopId}-claude
└── docker sandbox rm loop-{loopId}-codexTiming
| Operation | Time |
|---|---|
| Create (cached image) | ~14s |
| Exec (warm sandbox) | ~90ms |
| Stop | ~11s |
| Remove | ~150ms |
Net overhead per loop: ~14s create + ~90ms × N stories = negligible for loops running 5-10 stories at 5-15min each.
Workspace Mount
The workspace is bidirectional — same path on host and in sandbox:
- File created in sandbox → visible on host at same path
- File created on host → visible in sandbox
- Git operations work normally (host sees sandbox changes, sandbox sees host commits)
Sandbox Templates
| Template | Tools Included |
|---|---|
claude | claude 2.1.42, git, node 20, npm |
codex | codex 0.101.0, git, node 20, npm |
Neither includes bun. If bun is needed, use host-mode fallback or install it post-create.
Env Vars
Pass via docker sandbox exec -e:
docker sandbox exec \
-e "CLAUDE_CODE_OAUTH_TOKEN=$TOKEN" \
-e "NODE_ENV=development" \
-w /path/to/project \
my-sandbox \
claude -p "prompt" --output-format text --dangerously-skip-permissionsNetwork Control
Sandboxes have network access by default. Restrict with proxy rules:
# Allow only API endpoints
docker sandbox network proxy my-sandbox --policy deny
docker sandbox network proxy my-sandbox --allow-host api.anthropic.com
docker sandbox network proxy my-sandbox --allow-host api.openai.comFallback to Host Mode
If Docker is unavailable:
# Check availability
docker info >/dev/null 2>&1 || echo "Docker not available"
# Force host mode
export AGENT_LOOP_HOST=1Saving Custom Templates
If you install additional tools in a sandbox, save it as a template:
# Install tools
docker sandbox exec my-sandbox bash -c 'npm i -g @anthropic-ai/claude-code @openai/codex'
# Save as template
docker sandbox save my-sandbox my-agent-template:v1
# Use the template for future sandboxes
docker sandbox create --name fast-sandbox -t my-agent-template:v1 claude /path/to/projectImplementation in utils.ts
New Functions (ADR-0023)
// Create sandbox for a loop
async function createLoopSandbox(
loopId: string,
tool: "claude" | "codex",
workDir: string
): Promise<string> // returns sandbox name
// Execute command in existing sandbox
async function execInSandbox(
sandboxName: string,
command: string[],
opts: { env?: Record<string, string>; workDir?: string; timeout?: number }
): Promise<{ exitCode: number; output: string }>
// Destroy loop sandbox(es)
async function destroyLoopSandbox(loopId: string): Promise<void>Replacing spawnTool()
Current spawnTool() in implement.ts checks AGENT_LOOP_HOST and isDockerAvailable(). Update it to:
1. Check if sandbox loop-{loopId}-{tool} exists (created by planner) 2. If yes → execInSandbox() with auth env vars 3. If no → fall back to spawnToolHost() (current host-mode behavior)
Troubleshooting
"Not logged in" in sandbox
Auth not injected. Check:
docker sandbox exec my-sandbox bash -c 'claude auth status'
docker sandbox exec my-sandbox bash -c 'cat ~/.codex/auth.json | head -3'Sandbox creation slow
First pull downloads ~500MB image. Subsequent creates use cached image (~14s). Use docker sandbox save to create a pre-configured template.
File not visible between host and sandbox
Only the workspace path is mounted. Files outside the workspace directory are not shared.
"docker sandbox: command not found"
Docker Desktop must be running. Check version: docker sandbox version. Requires Docker Desktop 4.40+ with sandbox extension.
interface:
icon_small: "./assets/small-logo.svg"
icon_large: "./assets/large-logo.png"
<svg width="16" height="16" viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="JoelClaw icon">
<defs>
<clipPath id="circle-clip">
<circle cx="256" cy="256" r="248" />
</clipPath>
</defs>
<circle cx="256" cy="256" r="248" fill="#0a0a0a" />
<g clip-path="url(#circle-clip)">
<path fill="#ff1493" d="M175.656 22.375l-48.47 82.094c-23.017 4.384-43.547 11.782-60.124 22.374-24.436 15.613-40.572 37.414-45.5 67.875-4.79 29.62 1.568 68.087 24.125 116.093 93.162 22.88 184.08-10.908 257.25-18.813 37.138-4.012 71.196-.898 96.344 22.97 22.33 21.19 36.21 56.808 41.908 113.436 29.246-35.682 44.538-69.065 49.343-99.594 5.543-35.207-2.526-66.97-20.31-95.593-8.52-13.708-19.368-26.618-32-38.626l14.217-33-41.218 10.625c-8.637-6.278-17.765-12.217-27.314-17.782l-7.03-59.782-38.157 37.406c-12.418-5.186-25.184-9.804-38.158-13.812l-8.375-71.28-57.625 56.5c-9.344-1.316-18.625-2.333-27.812-2.97l-31.094-78.125zM222 325.345c-39.146 7.525-82.183 14.312-127.156 11.686 47.403 113.454 207.056 224.082 260.125 87-101.18 33.84-95.303-49.595-132.97-98.686z" />
</g>
</svg>