**Status**: Production Ready (Open Beta)
Execute Python, Node.js, and shell commands in isolated Ubuntu containers. Use when you need to run untrusted code, build projects, or automate workflows with full Linux capabilities.
/plugin marketplace add secondsky/claude-skills/plugin install cloudflare-sandbox@claude-skillsThis skill inherits all available tools. When active, it can use any tool Claude has access to.
assets/example-template.txtreferences/advanced.mdreferences/api-reference.mdreferences/common-errors.mdreferences/example-reference.mdreferences/naming-strategies.mdreferences/patterns.mdreferences/persistence-guide.mdreferences/session-management.mdscripts/example-script.shscripts/setup-sandbox-binding.shscripts/test-sandbox.tstemplates/basic-executor.tstemplates/chat-agent.tstemplates/ci-cd.tstemplates/workspace.tsStatus: Production Ready (Open Beta)
Last Updated: 2025-12-10
Dependencies: cloudflare-worker-base, cloudflare-durable-objects (recommended for understanding)
Latest Versions: @cloudflare/sandbox@0.6.3, Docker image: cloudflare/sandbox:0.6.3-python
⚠️ BREAKING CHANGE (v0.6.0): Python is no longer included in the default image. Use cloudflare/sandbox:<version>-python for Python support (~1.3GB with data science packages). The lean variant (~600-800MB) excludes Python.
bun add @cloudflare/sandbox@latest # preferred
# or: bun add @cloudflare/sandbox@latest
wrangler.jsonc:
{
"name": "my-sandbox-worker",
"main": "src/index.ts",
"compatibility_flags": ["nodejs_compat"],
"containers": [{
"class_name": "Sandbox",
"image": "cloudflare/sandbox:0.6.3-python",
"instance_type": "lite"
}],
"durable_objects": {
"bindings": [{
"class_name": "Sandbox",
"name": "Sandbox"
}]
},
"migrations": [{
"tag": "v1",
"new_sqlite_classes": ["Sandbox"]
}]
}
Why this matters:
nodejs_compat enables Node.js APIs required by SDKcontainers defines the Ubuntu container imagedurable_objects binding enables persistent routingmigrations registers the Sandbox classimport { getSandbox, type Sandbox } from '@cloudflare/sandbox';
export { Sandbox } from '@cloudflare/sandbox';
type Env = {
Sandbox: DurableObjectNamespace<Sandbox>;
};
export default {
async fetch(request: Request, env: Env): Promise<Response> {
// Get sandbox instance (creates if doesn't exist)
const sandbox = getSandbox(env.Sandbox, 'my-first-sandbox');
// Execute Python code
const result = await sandbox.exec('python3 -c "print(2 + 2)"');
return Response.json({
output: result.stdout,
success: result.success,
exitCode: result.exitCode
});
}
};
CRITICAL:
{ Sandbox } from @cloudflare/sandbox in your Workernpm run deploy
curl https://your-worker.workers.dev
Expected output:
{
"output": "4\n",
"success": true,
"exitCode": 0
}
┌─────────────────────────────────────────┐
│ Your Worker (Layer 1) │
│ - Handles HTTP requests │
│ - Calls getSandbox() │
│ - Uses sandbox.exec(), writeFile(), etc│
└──────────────┬──────────────────────────┘
│ RPC via Durable Object
┌──────────────▼──────────────────────────┐
│ Durable Object (Layer 2) │
│ - Routes by sandbox ID │
│ - Maintains persistent identity │
│ - Geographic stickiness │
└──────────────┬──────────────────────────┘
│ Container API
┌──────────────▼──────────────────────────┐
│ Ubuntu Container (Layer 3) │
│ - Full Linux environment │
│ - Python 3.11, Node 20, Git, etc. │
│ - Filesystem: /workspace, /tmp, /home │
│ - Process isolation (VM-based) │
└─────────────────────────────────────────┘
Key Insight: Workers handle API logic (fast), Durable Objects route requests (persistent identity), Containers execute code (full capabilities).
┌─────────┐ First request ┌────────┐ ~10 min idle ┌──────┐
│ Not │ ───────────────>│ Active │ ─────────────> │ Idle │
│ Created │ │ │ │ │
└─────────┘ └───┬────┘ └──┬───┘
│ ^ │
│ │ New request │
│ └──────────────────────┘
│ │
▼ ▼
Files persist ALL FILES DELETED
Processes run ALL PROCESSES KILLED
State maintained ALL STATE RESET
While Container is Active (~10 min after last request):
/workspace, /tmp, /home persistWhen Container Goes Idle (after inactivity):
This is NOT like a traditional server. Sandboxes are ephemeral by design.
For Important Data: Use external storage
// Save to R2 before container goes idle
await sandbox.writeFile('/workspace/data.txt', content);
const fileData = await sandbox.readFile('/workspace/data.txt');
await env.R2.put('backup/data.txt', fileData);
// Restore on next request
const restored = await env.R2.get('backup/data.txt');
if (restored) {
await sandbox.writeFile('/workspace/data.txt', await restored.text());
}
For Build Artifacts: Accept ephemerality or use caching
// Check if setup needed (handles cold starts)
const exists = await sandbox.readdir('/workspace/project').catch(() => null);
if (!exists) {
await sandbox.gitCheckout(repoUrl, '/workspace/project');
await sandbox.exec('npm install', { cwd: '/workspace/project' });
}
// Now safe to run build
await sandbox.exec('npm run build', { cwd: '/workspace/project' });
Sessions are bash shell contexts within one sandbox. Think terminal tabs.
Key Properties:
type ConversationState = {
sandboxId: string;
sessionId: string;
};
// First message: Create sandbox and session
const sandboxId = `user-${userId}`;
const sandbox = getSandbox(env.Sandbox, sandboxId);
const sessionId = await sandbox.createSession();
// Store in conversation state (database, KV, etc.)
await env.KV.put(`conversation:${conversationId}`, JSON.stringify({
sandboxId,
sessionId
}));
// Later messages: Reuse same session
const state = await env.KV.get(`conversation:${conversationId}`);
const { sandboxId, sessionId } = JSON.parse(state);
const sandbox = getSandbox(env.Sandbox, sandboxId);
// Commands run in same context
await sandbox.exec('cd /workspace/project', { session: sessionId });
await sandbox.exec('ls -la', { session: sessionId }); // Still in /workspace/project
await sandbox.exec('git status', { session: sessionId }); // Still in /workspace/project
// ❌ WRONG: Each command runs in separate session
await sandbox.exec('cd /workspace/project');
await sandbox.exec('ls'); // NOT in /workspace/project (different session)
const session1 = await sandbox.createSession();
const session2 = await sandbox.createSession();
// Run different tasks simultaneously
await Promise.all([
sandbox.exec('python train_model.py', { session: session1 }),
sandbox.exec('node generate_reports.js', { session: session2 })
]);
const sandbox = getSandbox(env.Sandbox, `user-${userId}`);
Pros: User's work persists while actively using (10 min idle time) Cons: Geographic lock-in (first request determines location) Use Cases: Interactive notebooks, IDEs, persistent workspaces
const sandboxId = `session-${Date.now()}-${crypto.randomUUID()}`;
const sandbox = getSandbox(env.Sandbox, sandboxId);
// Always destroy after use
await sandbox.destroy();
Pros: Clean environment, no state pollution Cons: No persistence between requests Use Cases: One-shot code execution, CI/CD, testing
const sandbox = getSandbox(env.Sandbox, `build-${repoName}-${commitSha}`);
Pros: Reproducible, debuggable, cacheable Cons: Need explicit cleanup strategy Use Cases: Build systems, data pipelines, automated workflows
The Sandbox SDK provides methods for command execution, file operations, Git operations, code interpretation (Jupyter-like), background processes, and cleanup.
📖 Load references/api-reference.md when you need detailed API method signatures, parameter options, or implementation examples for:
✅ Check exit codes - if (!result.success) { handle error }
✅ Use sessions for multi-step workflows - Preserve working directory
✅ Handle cold starts - Check if files exist before assuming they're there
✅ Set timeouts - Prevent hanging on long operations
✅ Destroy ephemeral sandboxes - Cleanup temp/session-based sandboxes
✅ Use external storage for persistence - R2/KV/D1 for important data
✅ Validate user input - Sanitize before exec() to prevent command injection
✅ Export Sandbox class - export { Sandbox } from '@cloudflare/sandbox'
❌ Assume files persist after idle - Container resets after ~10 min
❌ Ignore exit codes - Always check result.success or result.exitCode
❌ Chain commands without sessions - cd /dir then ls won't work
❌ Execute unsanitized user input - Use code interpreter or validate thoroughly
❌ Forget nodejs_compat flag - Required in wrangler.jsonc
❌ Skip migrations - Durable Objects need migration entries
❌ Use .workers.dev for preview URLs - Need custom domain
❌ Create unlimited sandboxes - Destroy ephemeral ones to avoid leaks
This skill prevents 10 documented issues:
Error: ReferenceError: fetch is not defined or Buffer is not defined
Source: https://developers.cloudflare.com/sandbox/get-started/
Why It Happens: SDK requires Node.js APIs not available in standard Workers
Prevention: Add "compatibility_flags": ["nodejs_compat"] to wrangler.jsonc
Error: Error: Class 'Sandbox' not found
Source: https://developers.cloudflare.com/durable-objects/
Why It Happens: Durable Objects must be registered via migrations
Prevention: Include migrations array in wrangler.jsonc
Error: Files disappear after inactivity Source: https://developers.cloudflare.com/sandbox/concepts/sandboxes/ Why It Happens: Containers go idle after ~10 min, all state reset Prevention: Use external storage (R2/KV) or check existence on each request
Error: Commands execute in wrong directory
Source: https://developers.cloudflare.com/sandbox/concepts/sessions/
Why It Happens: Each exec() uses new session unless explicitly specified
Prevention: Create session with createSession(), pass to all related commands
Error: Assuming command succeeded when it failed
Source: Shell best practices
Why It Happens: Not checking result.success or result.exitCode
Prevention: Always check: if (!result.success) throw new Error(result.stderr)
Error: Commands fail because dependencies aren't installed Source: https://developers.cloudflare.com/sandbox/concepts/sandboxes/ Why It Happens: Container resets after idle period Prevention: Check if setup needed before running commands
Error: Failed to build container during local development
Source: https://developers.cloudflare.com/sandbox/get-started/
Why It Happens: Local dev requires Docker daemon
Prevention: Ensure Docker Desktop is running before npm run dev
Error: API methods not available or behaving unexpectedly
Source: GitHub issues
Why It Happens: npm package version doesn't match Docker image version
Prevention: Keep @cloudflare/sandbox package and cloudflare/sandbox image in sync
Error: Resource exhaustion, unexpected costs
Source: Resource management best practices
Why It Happens: Creating sandboxes without destroying them
Prevention: await sandbox.destroy() in finally block for temp sandboxes
Error: Security breach from unsanitized user input
Source: Security best practices
Why It Happens: Passing user input directly to exec()
Prevention: Use code interpreter API or validate/sanitize input thoroughly
{
"name": "my-sandbox-app",
"main": "src/index.ts",
"compatibility_date": "2025-10-29",
"compatibility_flags": ["nodejs_compat"], // ← REQUIRED
"containers": [{
"class_name": "Sandbox",
"image": "cloudflare/sandbox:0.6.3-python", // ← Use -python for Python support
"instance_type": "lite"
}],
"durable_objects": {
"bindings": [{"class_name": "Sandbox", "name": "Sandbox"}]
},
"migrations": [{
"tag": "v1",
"new_sqlite_classes": ["Sandbox"]
}]
}
Four production-ready patterns for building with Cloudflare Sandboxes: one-shot code execution, persistent user workspaces, CI/CD pipelines, and AI agent integration.
📖 Load references/patterns.md when you need complete implementation examples for:
setup-sandbox-binding.sh - Interactive wrangler.jsonc configurationtest-sandbox.ts - Validation script to test sandbox setupExample Usage:
# Setup wrangler config
./scripts/setup-sandbox-binding.sh
# Test sandbox
bunx tsx scripts/test-sandbox.ts
references/persistence-guide.md - Deep dive on container lifecycle and persistencereferences/session-management.md - Advanced session patterns and best practicesreferences/common-errors.md - Complete list of errors with solutionsreferences/naming-strategies.md - Choosing sandbox IDs for different use casesWhen Claude should load these:
persistence-guide.md when debugging state issues or cold startssession-management.md when building multi-step workflows or chat agentscommon-errors.md when encountering specific errorsnaming-strategies.md when designing sandbox architecture📖 Load references/advanced.md for production patterns:
Package: @cloudflare/sandbox@0.6.3 | Docker: cloudflare/sandbox:0.6.3-python
Docs: Cloudflare Sandboxes | API Reference | GitHub SDK
Use when working with Payload CMS projects (payload.config.ts, collections, fields, hooks, access control, Payload API). Use when debugging validation errors, security issues, relationship queries, transactions, or hook behavior.