- ✨ Overview
- 🎯 Features
- 🚀 Quick Start
- 📚 API Reference
- 🧪 Code Interpreter
- 🌐 Port Forwarding
- 💡 Examples
- 🏗️ Architecture
- 🛠️ Advanced Usage
- 🔍 Debugging
- 🚧 Known Limitations
- 🤝 Contributing
- 📄 License
- 🙌 Acknowledgments
The Cloudflare Sandbox SDK enables you to run isolated code environments directly on Cloudflare's edge network using Durable Objects and the Cloudflare Containers. Execute commands, manage files, run services, and expose them via public URLs - all within secure, sandboxed containers.
- 🔒 Secure Isolation: Each sandbox runs in its own container with full process isolation
- ⚡ Edge-Native: Runs on Cloudflare's global network for low latency worldwide
- 📁 File System Access: Read, write, and manage files within the sandbox
- 🔧 Command Execution: Run any command or process inside the container
- 🌐 Preview URLs: Expose services running in your sandbox via public URLs
- 🔄 Git Integration: Clone repositories directly into sandboxes
- 🚀 Streaming Support: Real-time output streaming for long-running commands
- 🎮 Session Management: Maintain state across multiple operations
- 🧪 Code Interpreter: Execute Python and JavaScript with rich outputs (charts, tables, formatted data)
- 📊 Multi-Language Support: Persistent execution contexts for Python and JavaScript/TypeScript
- 🎨 Rich MIME Types: Automatic processing of images, HTML, charts, and structured data
npm install @cloudflare/sandbox
- Create a Dockerfile (temporary requirement, will be removed in future releases):
FROM docker.io/cloudflare/sandbox:0.2.3
# Expose the ports you want to expose
EXPOSE 3000
- Configure wrangler.json:
NOTE: In an upcoming release, this step will be removed entirely and you can reference a single Docker image published by us directly in your wrangler configuration below.
- Create your Worker:
import { getSandbox } from "@cloudflare/sandbox";
// Export the Sandbox class in your Worker
export { Sandbox } from "@cloudflare/sandbox";
export default {
async fetch(request: Request, env: Env) {
const sandbox = getSandbox(env.Sandbox, "my-sandbox");
// Execute a command
const result = await sandbox.exec("echo 'Hello from the edge!'");
return new Response(result.stdout);
},
};
exec(command, options?)
- Enhanced command execution that always returns results
// Simple execution
const result = await sandbox.exec("npm install express");
console.log(result.stdout, result.exitCode);
// With streaming callbacks
const result = await sandbox.exec("npm run build", {
stream: true,
onOutput: (stream, data) => console.log(`[${stream}] ${data}`)
});
execStream(command, options?)
- Dedicated streaming method returning SSE stream
import { parseSSEStream, type ExecEvent } from '@cloudflare/sandbox';
const stream = await sandbox.execStream("npm run test");
for await (const event of parseSSEStream<ExecEvent>(stream)) {
switch (event.type) {
case 'stdout':
console.log(`Test output: ${event.data}`);
break;
case 'complete':
console.log(`Tests ${event.exitCode === 0 ? 'passed' : 'failed'}`);
break;
}
}
startProcess(command, options?)
- Start background processes with lifecycle management
const process = await sandbox.startProcess("node server.js");
console.log(`Started process ${process.id} with PID ${process.pid}`);
// Monitor the process
const logStream = await sandbox.streamProcessLogs(process.id);
for await (const log of parseSSEStream<LogEvent>(logStream)) {
console.log(`Server: ${log.data}`);
}
Write content to a file.
await sandbox.writeFile("/workspace/app.js", "console.log('Hello!');");
Read a file from the sandbox.
const file = await sandbox.readFile("/package.json");
console.log(file.content);
Clone a git repository.
await sandbox.gitCheckout("https://github.com/user/repo", {
branch: "main",
targetDir: "my-project",
});
Set environment variables dynamically in the sandbox.
Important: This method must be called immediately after
getSandbox()
and before any other operations. Once a sandbox instance starts up, environment variables cannot be changed for that instance.
const sandbox = getSandbox(env.Sandbox, "my-sandbox");
// Set environment variables FIRST, before any other operations
await sandbox.setEnvVars({
NODE_ENV: "production",
API_KEY: "your-api-key",
DATABASE_URL: "postgresql://localhost:5432/mydb"
});
// Now you can run commands - environment variables are available
const result = await sandbox.exec("echo $NODE_ENV");
console.log(result.stdout); // "production"
listProcesses()
- List all running processesgetProcess(id)
- Get detailed process statuskillProcess(id, signal?)
- Terminate specific processeskillAllProcesses()
- Kill all processesstreamProcessLogs(id, options?)
- Stream logs from running processesgetProcessLogs(id)
- Get accumulated process output
writeFile(path, content, options?)
- Write content to a filereadFile(path, options?)
- Read a file from the sandboxmkdir(path, options?)
- Create a directorydeleteFile(path)
- Delete a filerenameFile(oldPath, newPath)
- Rename a filemoveFile(sourcePath, destinationPath)
- Move a filegitCheckout(repoUrl, options?)
- Clone git repositories
exposePort(port, options?)
- Expose a port and get a public URLunexposePort(port)
- Remove port exposuregetExposedPorts()
- List all exposed ports with their URLs
The Sandbox SDK includes powerful code interpreter capabilities, allowing you to execute Python and JavaScript code with rich outputs including charts, tables, and formatted data.
Creates a new code execution context with persistent state.
// Create a Python context
const pythonCtx = await sandbox.createCodeContext({ language: 'python' });
// Create a JavaScript context
const jsCtx = await sandbox.createCodeContext({ language: 'javascript' });
Options:
language
: Programming language ('python'
|'javascript'
|'typescript'
)cwd
: Working directory (default:/workspace
)envVars
: Environment variables for the context
Executes code with optional streaming callbacks.
// Simple execution
const execution = await sandbox.runCode('print("Hello World")', {
context: pythonCtx
});
// With streaming callbacks
await sandbox.runCode(`
for i in range(5):
print(f"Step {i}")
time.sleep(1)
`, {
context: pythonCtx,
onStdout: (output) => console.log('Real-time:', output.text),
onResult: (result) => console.log('Result:', result)
});
Options:
context
: Context to run the code inlanguage
: Language if no context providedonStdout
: Callback for stdout outputonStderr
: Callback for stderr outputonResult
: Callback for execution resultsonError
: Callback for errors
Returns a streaming response for real-time processing.
const stream = await sandbox.runCodeStream('import time; [print(i) for i in range(10)]');
// Process the stream as needed
The code interpreter automatically detects and processes various output types:
// Data visualization
const execution = await sandbox.runCode(`
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0, 10, 100)
y = np.sin(x)
plt.plot(x, y)
plt.title('Sine Wave')
plt.show()
`, {
context: pythonCtx,
onResult: (result) => {
if (result.png) {
// Base64 encoded PNG image
console.log('Chart generated!');
}
}
});
// HTML tables with pandas
const tableExecution = await sandbox.runCode(`
import pandas as pd
df = pd.DataFrame({
'name': ['Alice', 'Bob', 'Charlie'],
'score': [92, 88, 95]
})
df
`, { context: pythonCtx });
// Access HTML table in execution.results[0].html
Results can include multiple formats:
text
: Plain text representationhtml
: HTML (often pandas DataFrames)png
/jpeg
: Base64 encoded imagessvg
: Vector graphicsjson
: Structured datachart
: Parsed chart information
Check available formats with result.formats()
.
listCodeContexts()
- List all active code contextsdeleteCodeContext(contextId)
- Delete a specific context
The SDK automatically handles preview URL routing for exposed ports. Just add one line to your worker:
import { proxyToSandbox, getSandbox } from "@cloudflare/sandbox";
export default {
async fetch(request, env) {
// Route requests to exposed container ports via their preview URLs
const proxyResponse = await proxyToSandbox(request, env);
if (proxyResponse) return proxyResponse;
// Your custom routes here
// ...
},
};
When you expose a port, the SDK returns a preview URL that automatically routes to your service:
const preview = await sandbox.exposePort(3000);
console.log(preview.url); // https://3000-sandbox-id.your-worker.dev
The SDK handles:
- Subdomain routing (
3000-sandbox-id.domain.com
) for both production and local development - All localhost variants (127.0.0.1, ::1, etc.)
- Request forwarding with proper headers
Important for Local Development: When developing locally with
wrangler dev
, you must explicitly expose ports in your Dockerfile using theEXPOSE
instruction. This is only required for local development - in production, all container ports are automatically accessible.
# In your Dockerfile (only needed for local dev)
FROM docker.io/cloudflare/sandbox:0.1.3
# Expose the ports you'll be using
EXPOSE 3000 # For a web server
EXPOSE 8080 # For an API server
EXPOSE 3001 # For any additional services
# Your container setup...
Without the EXPOSE
instruction in local development, you'll see this error:
connect(): Connection refused: container port not found. Make sure you exposed the port in your container definition.
For more details, see the Cloudflare Containers local development guide.
ping()
- Health check for the sandboxcontainerFetch(request)
- Direct container communication
const sandbox = getSandbox(env.Sandbox, "node-app");
// Write a simple Express server
await sandbox.writeFile(
"/workspace/app.js",
`
const express = require('express');
const app = express();
app.get('/', (req, res) => {
res.json({ message: 'Hello from Cloudflare!' });
});
app.listen(3000);
`
);
// Install dependencies and start the server
await sandbox.exec("npm init -y");
await sandbox.exec("npm install express");
const server = await sandbox.startProcess("node app.js");
// Expose it to the internet
const preview = await sandbox.exposePort(3000);
console.log(`API available at: ${preview.url}`);
const sandbox = getSandbox(env.Sandbox, "test-env");
// Clone a repository
await sandbox.gitCheckout("https://github.com/user/project");
// Run tests
const testResult = await sandbox.exec("npm test");
// Build the project
const buildResult = await sandbox.exec("npm run build");
return new Response(
JSON.stringify({
tests: testResult.exitCode === 0 ? "passed" : "failed",
build: buildResult.exitCode === 0 ? "success" : "failed",
output: testResult.stdout,
})
);
// Create a development sandbox with hot reload
const sandbox = getSandbox(env.Sandbox, "dev-env");
// Set up the project
await sandbox.gitCheckout("https://github.com/user/my-app");
await sandbox.exec("npm install");
// Start dev server
const devServer = await sandbox.startProcess("npm run dev");
// Expose the dev server
const preview = await sandbox.exposePort(3000, { name: "dev-server" });
// Make changes and see them live!
await sandbox.writeFile("/src/App.jsx", updatedCode);
// Create and start a web server
await sandbox.writeFile(
"/server.js",
`Bun.serve({
port: 8080,
fetch(req) {
return new Response("Hello from sandbox!");
}
});`
);
const server = await sandbox.startProcess("bun run /server.js");
// Expose the port - returns a public URL
const preview = await sandbox.exposePort(8080);
console.log(`Service available at: ${preview.url}`);
// Note: Your Worker needs to handle preview URL routing.
// See the example in examples/basic/src/index.ts for the routing implementation.
const sandbox = getSandbox(env.Sandbox, "analysis");
// Create a Python context for data analysis
const pythonCtx = await sandbox.createCodeContext({ language: 'python' });
// Load and analyze data
const analysis = await sandbox.runCode(`
import pandas as pd
import matplotlib.pyplot as plt
# Create sample data
data = {
'Month': ['Jan', 'Feb', 'Mar', 'Apr', 'May'],
'Sales': [10000, 12000, 15000, 14000, 18000],
'Profit': [2000, 2500, 3200, 2800, 4000]
}
df = pd.DataFrame(data)
# Display summary statistics
print("Sales Summary:")
print(df.describe())
# Create visualization
plt.figure(figsize=(10, 6))
plt.subplot(1, 2, 1)
plt.bar(df['Month'], df['Sales'])
plt.title('Monthly Sales')
plt.xlabel('Month')
plt.ylabel('Sales ($)')
plt.subplot(1, 2, 2)
plt.plot(df['Month'], df['Profit'], marker='o', color='green')
plt.title('Monthly Profit')
plt.xlabel('Month')
plt.ylabel('Profit ($)')
plt.tight_layout()
plt.show()
# Return the data as JSON
df.to_dict('records')
`, {
context: pythonCtx,
onResult: (result) => {
if (result.png) {
// Handle the chart image
console.log('Chart generated:', result.png.substring(0, 50) + '...');
}
if (result.json) {
// Handle the structured data
console.log('Data:', result.json);
}
}
});
// Multi-language workflow: Process in Python, analyze in JavaScript
await sandbox.runCode(`
# Save processed data
df.to_json('/tmp/sales_data.json', orient='records')
`, { context: pythonCtx });
const jsCtx = await sandbox.createCodeContext({ language: 'javascript' });
const jsAnalysis = await sandbox.runCode(`
const fs = require('fs');
const data = JSON.parse(fs.readFileSync('/tmp/sales_data.json', 'utf8'));
// Calculate growth rate
const growth = data.map((curr, idx) => {
if (idx === 0) return { ...curr, growth: 0 };
const prev = data[idx - 1];
return {
...curr,
growth: ((curr.Sales - prev.Sales) / prev.Sales * 100).toFixed(2) + '%'
};
});
console.log('Growth Analysis:', growth);
growth;
`, { context: jsCtx });
The SDK leverages Cloudflare's infrastructure:
- Durable Objects: Manages sandbox lifecycle and state
- Containers: Provides isolated execution environments with Jupyter kernels
- Workers: Handles HTTP routing and API interface
- Edge Network: Enables global distribution and low latency
- Jupyter Integration: Python (IPython) and JavaScript (TSLab) kernels for code execution
- MIME Processing: Automatic detection and handling of rich output formats
The SDK provides powerful streaming capabilities with typed AsyncIterable support:
import { parseSSEStream, type ExecEvent } from '@cloudflare/sandbox';
// Stream command execution
const stream = await sandbox.execStream('npm run build');
for await (const event of parseSSEStream<ExecEvent>(stream)) {
switch (event.type) {
case 'start':
console.log(`Build started: ${event.command}`);
break;
case 'stdout':
console.log(`Build: ${event.data}`);
break;
case 'complete':
console.log(`Exit code: ${event.exitCode}`);
break;
case 'error':
console.error(`Error: ${event.error}`);
break;
}
}
The SDK exports utilities for working with Server-Sent Event streams:
parseSSEStream<T>(stream)
- Convert ReadableStream to typed AsyncIterableresponseToAsyncIterable<T>(response)
- Convert SSE Response to AsyncIterableasyncIterableToSSEStream<T>(iterable)
- Convert AsyncIterable back to SSE stream
CI/CD Build System:
export async function runBuild(env: Env, buildId: string) {
const sandbox = getSandbox(env.SANDBOX, buildId);
const stream = await sandbox.execStream('npm run build');
for await (const event of parseSSEStream<ExecEvent>(stream)) {
switch (event.type) {
case 'start':
await env.BUILDS.put(buildId, { status: 'running' });
break;
case 'complete':
await env.BUILDS.put(buildId, {
status: event.exitCode === 0 ? 'success' : 'failed',
exitCode: event.exitCode
});
break;
}
}
}
System Monitoring:
const monitor = await sandbox.startProcess('tail -f /var/log/system.log');
const logStream = await sandbox.streamProcessLogs(monitor.id);
for await (const log of parseSSEStream<LogEvent>(logStream)) {
if (log.type === 'stdout' && log.data.includes('ERROR')) {
await env.ALERTS.send({
severity: 'high',
message: log.data,
timestamp: log.timestamp
});
}
}
Maintain context across commands:
const sessionId = crypto.randomUUID();
// Commands in the same session share working directory
await sandbox.exec("cd /workspace", { sessionId });
await sandbox.exec("npm install", { sessionId });
const app = await sandbox.startProcess("npm start", { sessionId });
Enable verbose logging:
const sandbox = getSandbox(env.Sandbox, "debug-sandbox");
sandbox.client.onCommandStart = (cmd, args) =>
console.log(`Starting: ${cmd} ${args.join(" ")}`);
sandbox.client.onOutput = (stream, data) => console.log(`[${stream}] ${data}`);
sandbox.client.onCommandComplete = (success, code) =>
console.log(`Completed: ${success} (${code})`);
- Maximum container runtime is limited by Durable Object constraints
- WebSocket support for preview URLs coming soon
- Some system calls may be restricted in the container environment
- Code interpreter has no internet access (sandbox restriction)
- Some Python/JavaScript packages may not be pre-installed
- Resource limits apply to code execution (CPU, memory)
We welcome contributions! Please see our Contributing Guide for details.
# Clone the repo
git clone https://github.com/cloudflare/sandbox-sdk
cd sandbox-sdk
# Install dependencies
npm install
# Install Bun (if not already installed)
# Visit https://bun.sh for installation instructions
curl -fsSL https://bun.sh/install | bash
# Install container dependencies (required for TypeScript checking)
cd packages/sandbox/container_src && bun install && cd -
# Run tests
npm test
# Build the project
npm run build
# Run type checking and linting
npm run check
Built with ❤️ by the Cloudflare team. Special thanks to all early adopters and contributors.