Build secure, isolated code execution environments on Cloudflare.
The Sandbox SDK lets you run untrusted code safely in isolated containers. Execute commands, manage files, run background processes, and expose services — all from your Workers applications.
Perfect for AI code execution, interactive development environments, data analysis platforms, CI/CD systems, and any application that needs secure code execution at the edge.
Getting Started
Prerequisites
- Install Node.js (version 16.17.0 or later)
- Ensure Docker is running locally (see setup guide)
- For deploying to production, sign up for a Cloudflare account
1. Create a new project
Create a new Sandbox SDK project using the minimal template:
npm create cloudflare@latest -- my-sandbox --template=cloudflare/sandbox-sdk/examples/minimal
cd my-sandbox2. Test locally
Start the development server:
npm run dev
Note: First run builds the Docker container (2-3 minutes). Subsequent runs are much faster.
Test the endpoints:
# Execute Python code curl http://localhost:8787/run # File operations curl http://localhost:8787/file
3. Deploy to production
Deploy your Worker and container:
npx wrangler deploy
Wait for provisioning: After first deployment, wait 2-3 minutes before making requests.
📖 View the complete getting started guide for detailed instructions and explanations.
Quick API Example
import { getSandbox, proxyToSandbox, 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> { // Required for preview URLs const proxyResponse = await proxyToSandbox(request, env); if (proxyResponse) return proxyResponse; const url = new URL(request.url); const sandbox = getSandbox(env.Sandbox, 'my-sandbox'); // Execute Python code if (url.pathname === '/run') { const result = await sandbox.exec('python3 -c "print(2 + 2)"'); return Response.json({ output: result.stdout, success: result.success }); } // Work with files if (url.pathname === '/file') { await sandbox.writeFile('/workspace/hello.txt', 'Hello, Sandbox!'); const file = await sandbox.readFile('/workspace/hello.txt'); return Response.json({ content: file.content }); } return new Response('Try /run or /file'); } };
Sandbox options
Pass options as the third argument to getSandbox() to configure sandbox
lifetime and container startup metadata:
const sandbox = getSandbox(env.Sandbox, 'tenant-workspace', { sleepAfter: '30m', labels: { tenantId: 'tenant_123', workload: 'code-workspace' } });
Container labels are attached to the underlying Cloudflare Container for analytics and observability. Labels are applied when the container starts; if labels are changed while a container is already running, the new labels apply the next time the container starts.
Quick tunnels
sandbox.tunnels.get(port) exposes a service running inside the
sandbox on a *.trycloudflare.com URL. No Cloudflare account or DNS
setup required — cloudflared opens a persistent QUIC connection to
Cloudflare's edge and Cloudflare hands back a hostname.
// Inside a Worker with an RPC-transport sandbox: const tunnel = await sandbox.tunnels.get(8080); console.log(tunnel.url); // → https://random-words-here.trycloudflare.com // Repeated calls for the same port return the same record: const same = await sandbox.tunnels.get(8080); console.log(same.url === tunnel.url); // true // Tear down by port number or by the record: await sandbox.tunnels.destroy(8080); // or: await sandbox.tunnels.destroy(tunnel);
get() is idempotent: it consults a per-sandbox cache in Durable
Object storage, returns the cached record on a hit, and only spawns a
fresh cloudflared process on a miss. list() returns every cached
tunnel.
Notes:
- Requires the RPC transport. The route-based transport's
tunnelsstub throws "RPC transport required". - URLs do not survive a container restart. Cloudflare assigns the
hostname during cloudflared's startup handshake, so every restart
yields a new URL. The SDK clears its cache on container start, so
the next
get(port)after a restart returns a fresh record. - The first fetch through a brand-new URL can take a couple of
seconds while DNS propagates, even after
get()resolves. *.trycloudflare.combufferstext/event-streamresponses. WebSockets work fine.- Local builds behind a TLS-intercepting proxy (e.g. Cloudflare WARP) need the host CA bundle injected at build time — see DOCKER_README.md.
Documentation
- Get Started Guide - Step-by-step tutorial
- API Reference - Complete API docs
- Guides - Execute commands, manage files, expose services
- Examples - AI agents, data analysis, CI/CD pipelines
Key Features
- Secure Isolation - Each sandbox runs in its own container
- Edge-Native - Runs on Cloudflare's global network
- Code Interpreter - Execute Python and JavaScript with rich outputs
- File System Access - Read, write, and manage files
- Command Execution - Run any command with streaming support
- Preview URLs - Expose services with public URLs
- Quick tunnels - Zero-config
*.trycloudflare.comURLs viasandbox.tunnels.get(port) - Git Integration - Clone repositories directly
Contributing
We welcome contributions from the community! See CONTRIBUTING.md for guidelines on:
- Setting up your development environment
- Creating pull requests
- Code style and testing requirements
Development
This repository contains the SDK source code. Quick start:
# Clone the repo git clone https://github.com/cloudflare/sandbox-sdk cd sandbox-sdk # Install dependencies npm install # Run tests npm test # Build the project npm run build # Type checking and linting npm run check
Examples
See the examples directory for complete working examples:
- Minimal - Start here: exec commands, read/write files
- Code Interpreter - Give gpt-oss on Workers AI a Python REPL
- Claude Code - Run Claude Code headless on any repo
- OpenAI Agents -
ShellandEditortools for OpenAI Agents SDK - OpenCode - OpenCode web UI or SDK in a sandbox
- Git Repo Per Sandbox - One Artifacts Git repo per sandbox
- TypeScript Validator - Build with npm in sandbox, execute in isolates
Status
Beta - The SDK is in active development. APIs may change before v1.0.