# E2B Documentation
Source: https://e2b.mintlify.app/docs
## What is E2B?
E2B provides isolated sandboxes that let agents safely execute code, process data, and run tools. Our SDKs make it easy to start and manage these environments.
Start a sandbox and run code in a few lines:
```bash JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
npm i e2b
```
```bash Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
pip install e2b
```
```javascript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const sandbox = await Sandbox.create() // Needs E2B_API_KEY environment variable
const result = await sandbox.commands.run('echo "Hello from E2B Sandbox!"')
console.log(result.stdout)
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
sandbox = Sandbox.create() # Needs E2B_API_KEY environment variable
result = sandbox.commands.run('echo "Hello from E2B Sandbox!"')
print(result.stdout)
```
## E2B building blocks
A quick overview of the core building blocks you'll interact with when using E2B.
* [**Sandbox**](/docs/sandbox) — A fast, secure Linux VM created on demand for your agent
* [**Template**](/docs/template/quickstart) — Defines what environment a sandbox starts with
## How to use the docs
The documentation is split into three main sections:
* [**Quickstart**](#quickstart) — Step-by-step tutorials that walk you through creating your first E2B sandboxes.
* [**Examples**](#examples) — In-depth tutorials focused on specific use cases. Pick the topics that match what you're building.
* [**SDK Reference**](https://e2b.dev/docs/sdk-reference) — A complete technical reference for every SDK method, parameter, and configuration option.
## Quickstart
## Examples
Build AI agents that see, understand, and control virtual Linux desktops using E2B Desktop sandboxes.
Use E2B sandboxes in your GitHub Actions workflows to run testing, validation, and AI code reviews.
# Amp
Source: https://e2b.mintlify.app/docs/agents/amp
Run Amp in a secure E2B sandbox with full filesystem, terminal, and git access.
[Amp](https://ampcode.com) is a coding agent with multi-model architecture and built-in code intelligence. E2B provides a pre-built `amp` template with Amp already installed.
## CLI
Create a sandbox with the [E2B CLI](/docs/cli).
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
e2b sbx create amp
```
Once inside the sandbox, start Amp.
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
amp
```
## Run headless
Use `-x` for non-interactive mode and `--dangerously-allow-all` to auto-approve all tool calls (safe inside E2B sandboxes). Amp uses its own API key from [ampcode.com/settings](https://ampcode.com/settings).
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const sandbox = await Sandbox.create('amp', {
envs: { AMP_API_KEY: process.env.AMP_API_KEY },
})
const result = await sandbox.commands.run(
`amp --dangerously-allow-all -x "Create a hello world HTTP server in Go"`
)
console.log(result.stdout)
await sandbox.kill()
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import os
from e2b import Sandbox
sandbox = Sandbox.create("amp", envs={
"AMP_API_KEY": os.environ["AMP_API_KEY"],
})
result = sandbox.commands.run(
'amp --dangerously-allow-all -x "Create a hello world HTTP server in Go"',
)
print(result.stdout)
sandbox.kill()
```
### Example: work on a cloned repository
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const sandbox = await Sandbox.create('amp', {
envs: { AMP_API_KEY: process.env.AMP_API_KEY },
timeoutMs: 600_000,
})
await sandbox.git.clone('https://github.com/your-org/your-repo.git', {
path: '/home/user/repo',
username: 'x-access-token',
password: process.env.GITHUB_TOKEN,
depth: 1,
})
const result = await sandbox.commands.run(
`cd /home/user/repo && amp --dangerously-allow-all -x "Add error handling to all API endpoints"`,
{ onStdout: (data) => process.stdout.write(data) }
)
const diff = await sandbox.commands.run('cd /home/user/repo && git diff')
console.log(diff.stdout)
await sandbox.kill()
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import os
from e2b import Sandbox
sandbox = Sandbox.create("amp", envs={
"AMP_API_KEY": os.environ["AMP_API_KEY"],
}, timeout=600)
sandbox.git.clone("https://github.com/your-org/your-repo.git",
path="/home/user/repo",
username="x-access-token",
password=os.environ["GITHUB_TOKEN"],
depth=1,
)
result = sandbox.commands.run(
'cd /home/user/repo && amp --dangerously-allow-all -x "Add error handling to all API endpoints"',
on_stdout=lambda data: print(data, end=""),
)
diff = sandbox.commands.run("cd /home/user/repo && git diff")
print(diff.stdout)
sandbox.kill()
```
## Streaming JSON
Use `--stream-json` to get a real-time JSONL event stream with rich metadata — including tool calls, token usage, thinking blocks, and permission decisions.
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const sandbox = await Sandbox.create('amp', {
envs: { AMP_API_KEY: process.env.AMP_API_KEY },
})
const result = await sandbox.commands.run(
`cd /home/user/repo && amp --dangerously-allow-all --stream-json -x "Find and fix all TODO comments"`,
{
onStdout: (data) => {
for (const line of data.split('\n').filter(Boolean)) {
const event = JSON.parse(line)
if (event.type === 'assistant') {
console.log(`[assistant] tokens: ${event.message.usage?.output_tokens}`)
} else if (event.type === 'result') {
console.log(`[done] ${event.message.subtype} in ${event.message.duration_ms}ms`)
}
}
},
}
)
await sandbox.kill()
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import os
import json
from e2b import Sandbox
sandbox = Sandbox.create("amp", envs={
"AMP_API_KEY": os.environ["AMP_API_KEY"],
})
def handle_event(data):
for line in data.strip().split("\n"):
if line:
event = json.loads(line)
if event["type"] == "assistant":
usage = event.get("message", {}).get("usage", {})
print(f"[assistant] tokens: {usage.get('output_tokens')}")
elif event["type"] == "result":
msg = event["message"]
print(f"[done] {msg['subtype']} in {msg['duration_ms']}ms")
result = sandbox.commands.run(
'cd /home/user/repo && amp --dangerously-allow-all --stream-json -x "Find and fix all TODO comments"',
on_stdout=handle_event,
)
sandbox.kill()
```
## Thread management
Amp persists conversations as threads that can be resumed or continued with follow-up tasks.
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const sandbox = await Sandbox.create('amp', {
envs: { AMP_API_KEY: process.env.AMP_API_KEY },
timeoutMs: 600_000,
})
// Start a new thread
const initial = await sandbox.commands.run(
`cd /home/user/repo && amp --dangerously-allow-all -x "Analyze the codebase and create a refactoring plan"`,
{ onStdout: (data) => process.stdout.write(data) }
)
// List threads and get the most recent thread ID
const threads = await sandbox.commands.run('amp threads list --json')
const threadId = JSON.parse(threads.stdout)[0].id
// Continue the thread with a follow-up task
const followUp = await sandbox.commands.run(
`cd /home/user/repo && amp threads continue ${threadId} --dangerously-allow-all -x "Now implement step 1 of the plan"`,
{ onStdout: (data) => process.stdout.write(data) }
)
const diff = await sandbox.commands.run('cd /home/user/repo && git diff')
console.log(diff.stdout)
await sandbox.kill()
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import os
import json
from e2b import Sandbox
sandbox = Sandbox.create("amp", envs={
"AMP_API_KEY": os.environ["AMP_API_KEY"],
}, timeout=600)
# Start a new thread
initial = sandbox.commands.run(
'cd /home/user/repo && amp --dangerously-allow-all -x "Analyze the codebase and create a refactoring plan"',
on_stdout=lambda data: print(data, end=""),
)
# List threads and get the most recent thread ID
threads = sandbox.commands.run("amp threads list --json")
thread_id = json.loads(threads.stdout)[0]["id"]
# Continue the thread with a follow-up task
follow_up = sandbox.commands.run(
f'cd /home/user/repo && amp threads continue {thread_id} --dangerously-allow-all -x "Now implement step 1 of the plan"',
on_stdout=lambda data: print(data, end=""),
)
diff = sandbox.commands.run("cd /home/user/repo && git diff")
print(diff.stdout)
sandbox.kill()
```
## Build a custom template
If you need to customize the environment (e.g. pre-install dependencies, add config files), build your own template on top of the pre-built `amp` template.
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
// template.ts
import { Template } from 'e2b'
export const template = Template()
.fromTemplate('amp')
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
# template.py
from e2b import Template
template = (
Template()
.from_template("amp")
)
```
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
// build.ts
import { Template, defaultBuildLogger } from 'e2b'
import { template as ampTemplate } from './template'
await Template.build(ampTemplate, 'my-amp', {
cpuCount: 2,
memoryMB: 2048,
onBuildLogs: defaultBuildLogger(),
})
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
# build.py
from e2b import Template, default_build_logger
from template import template as amp_template
Template.build(amp_template, "my-amp",
cpu_count=2,
memory_mb=2048,
on_build_logs=default_build_logger(),
)
```
Run the build script to create the template.
```bash JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
npx tsx build.ts
```
```bash Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
python build.py
```
## Related guides
Auto-pause, resume, and manage sandbox lifecycle
Clone repos, manage branches, and push changes
Connect to the sandbox via SSH for interactive sessions
# Claude Code
Source: https://e2b.mintlify.app/docs/agents/claude-code
Run Claude Code in a secure E2B sandbox with full filesystem, terminal, and git access.
[Claude Code](https://docs.anthropic.com/en/docs/claude-code) is Anthropic's agentic coding tool. E2B provides a pre-built `claude` template with Claude Code already installed.
## CLI
Create a sandbox with the [E2B CLI](/docs/cli).
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
e2b sbx create claude
```
Once inside the sandbox, start Claude Code.
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
claude
```
## Run headless
Use `-p` for non-interactive mode and `--dangerously-skip-permissions` to auto-approve all tool calls (safe inside E2B sandboxes).
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const sandbox = await Sandbox.create('claude', {
envs: { ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY },
})
const result = await sandbox.commands.run(
`claude --dangerously-skip-permissions -p "Create a hello world HTTP server in Go"`
)
console.log(result.stdout)
await sandbox.kill()
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import os
from e2b import Sandbox
sandbox = Sandbox.create("claude", envs={
"ANTHROPIC_API_KEY": os.environ["ANTHROPIC_API_KEY"],
})
result = sandbox.commands.run(
'claude --dangerously-skip-permissions -p "Create a hello world HTTP server in Go"',
)
print(result.stdout)
sandbox.kill()
```
### Example: work on a cloned repository
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const sandbox = await Sandbox.create('claude', {
envs: { ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY },
timeoutMs: 600_000,
})
await sandbox.git.clone('https://github.com/your-org/your-repo.git', {
path: '/home/user/repo',
username: 'x-access-token',
password: process.env.GITHUB_TOKEN,
depth: 1,
})
const result = await sandbox.commands.run(
`cd /home/user/repo && claude --dangerously-skip-permissions -p "Add error handling to all API endpoints"`,
{ onStdout: (data) => process.stdout.write(data) }
)
const diff = await sandbox.commands.run('cd /home/user/repo && git diff')
console.log(diff.stdout)
await sandbox.kill()
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import os
from e2b import Sandbox
sandbox = Sandbox.create("claude", envs={
"ANTHROPIC_API_KEY": os.environ["ANTHROPIC_API_KEY"],
}, timeout=600)
sandbox.git.clone("https://github.com/your-org/your-repo.git",
path="/home/user/repo",
username="x-access-token",
password=os.environ["GITHUB_TOKEN"],
depth=1,
)
result = sandbox.commands.run(
'cd /home/user/repo && claude --dangerously-skip-permissions -p "Add error handling to all API endpoints"',
on_stdout=lambda data: print(data, end=""),
)
diff = sandbox.commands.run("cd /home/user/repo && git diff")
print(diff.stdout)
sandbox.kill()
```
## Structured output
Use `--output-format json` to get machine-readable responses — useful for building pipelines or extracting specific results.
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const sandbox = await Sandbox.create('claude', {
envs: { ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY },
})
const result = await sandbox.commands.run(
`claude --dangerously-skip-permissions --output-format json -p "Review this codebase and list all security issues as JSON"`
)
const response = JSON.parse(result.stdout)
console.log(response)
await sandbox.kill()
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import os
import json
from e2b import Sandbox
sandbox = Sandbox.create("claude", envs={
"ANTHROPIC_API_KEY": os.environ["ANTHROPIC_API_KEY"],
})
result = sandbox.commands.run(
'claude --dangerously-skip-permissions --output-format json -p "Review this codebase and list all security issues as JSON"',
)
response = json.loads(result.stdout)
print(response)
sandbox.kill()
```
## Streaming output
Use `--output-format stream-json` to get a real-time JSONL event stream — including tool calls, token usage, and result metadata.
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const sandbox = await Sandbox.create('claude', {
envs: { ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY },
})
const result = await sandbox.commands.run(
`cd /home/user/repo && claude --dangerously-skip-permissions --output-format stream-json -p "Find and fix all TODO comments"`,
{
onStdout: (data) => {
for (const line of data.split('\n').filter(Boolean)) {
const event = JSON.parse(line)
if (event.type === 'assistant') {
console.log(`[assistant] tokens: ${event.message.usage?.output_tokens}`)
} else if (event.type === 'result') {
console.log(`[done] ${event.subtype} in ${event.duration_ms}ms`)
}
}
},
}
)
await sandbox.kill()
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import os
import json
from e2b import Sandbox
sandbox = Sandbox.create("claude", envs={
"ANTHROPIC_API_KEY": os.environ["ANTHROPIC_API_KEY"],
})
def handle_event(data):
for line in data.strip().split("\n"):
if line:
event = json.loads(line)
if event["type"] == "assistant":
usage = event.get("message", {}).get("usage", {})
print(f"[assistant] tokens: {usage.get('output_tokens')}")
elif event["type"] == "result":
print(f"[done] {event['subtype']} in {event['duration_ms']}ms")
result = sandbox.commands.run(
'cd /home/user/repo && claude --dangerously-skip-permissions --output-format stream-json -p "Find and fix all TODO comments"',
on_stdout=handle_event,
)
sandbox.kill()
```
## Resume a session
Claude Code persists conversations that can be resumed with follow-up tasks using `--resume`.
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const sandbox = await Sandbox.create('claude', {
envs: { ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY },
timeoutMs: 600_000,
})
// Start a new session
const initial = await sandbox.commands.run(
`cd /home/user/repo && claude --dangerously-skip-permissions --output-format json -p "Analyze the codebase and create a refactoring plan"`
)
// Extract session ID from the JSON response
const response = JSON.parse(initial.stdout)
const sessionId = response.session_id
// Continue with a follow-up task
const followUp = await sandbox.commands.run(
`cd /home/user/repo && claude --dangerously-skip-permissions --resume ${sessionId} -p "Now implement step 1 of the plan"`,
{ onStdout: (data) => process.stdout.write(data) }
)
const diff = await sandbox.commands.run('cd /home/user/repo && git diff')
console.log(diff.stdout)
await sandbox.kill()
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import os
import json
from e2b import Sandbox
sandbox = Sandbox.create("claude", envs={
"ANTHROPIC_API_KEY": os.environ["ANTHROPIC_API_KEY"],
}, timeout=600)
# Start a new session
initial = sandbox.commands.run(
'cd /home/user/repo && claude --dangerously-skip-permissions --output-format json -p "Analyze the codebase and create a refactoring plan"',
)
# Extract session ID from the JSON response
response = json.loads(initial.stdout)
session_id = response["session_id"]
# Continue with a follow-up task
follow_up = sandbox.commands.run(
f'cd /home/user/repo && claude --dangerously-skip-permissions --resume {session_id} -p "Now implement step 1 of the plan"',
on_stdout=lambda data: print(data, end=""),
)
diff = sandbox.commands.run("cd /home/user/repo && git diff")
print(diff.stdout)
sandbox.kill()
```
## Custom system prompt
Write a `CLAUDE.md` file into the sandbox for project context or use `--system-prompt` to provide task-specific instructions.
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const sandbox = await Sandbox.create('claude', {
envs: { ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY },
})
// Write project context
await sandbox.files.write('/home/user/repo/CLAUDE.md', `
You are working on a Go microservice.
Always use structured logging with slog.
Follow the project's error handling conventions in pkg/errors.
`)
const result = await sandbox.commands.run(
`cd /home/user/repo && claude --dangerously-skip-permissions -p "Add a /healthz endpoint"`
)
console.log(result.stdout)
await sandbox.kill()
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import os
from e2b import Sandbox
sandbox = Sandbox.create("claude", envs={
"ANTHROPIC_API_KEY": os.environ["ANTHROPIC_API_KEY"],
})
# Write project context
sandbox.files.write("/home/user/repo/CLAUDE.md", """
You are working on a Go microservice.
Always use structured logging with slog.
Follow the project's error handling conventions in pkg/errors.
""")
result = sandbox.commands.run(
'cd /home/user/repo && claude --dangerously-skip-permissions -p "Add a /healthz endpoint"',
)
print(result.stdout)
sandbox.kill()
```
## Connect MCP tools
Claude Code has built-in support for [MCP](https://modelcontextprotocol.io/). E2B provides an [MCP gateway](/docs/mcp) that gives Claude access to 200+ tools from the [Docker MCP Catalog](https://hub.docker.com/mcp).
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const sandbox = await Sandbox.create('claude', {
envs: { ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY },
mcp: {
browserbase: {
apiKey: process.env.BROWSERBASE_API_KEY,
projectId: process.env.BROWSERBASE_PROJECT_ID,
},
},
})
const mcpUrl = sandbox.getMcpUrl()
const mcpToken = await sandbox.getMcpToken()
await sandbox.commands.run(
`claude mcp add --transport http e2b-mcp-gateway ${mcpUrl} --header "Authorization: Bearer ${mcpToken}"`
)
const result = await sandbox.commands.run(
`claude --dangerously-skip-permissions -p "Use browserbase to research E2B and summarize your findings"`,
{ onStdout: console.log }
)
await sandbox.kill()
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import os
from e2b import Sandbox
sandbox = Sandbox.create("claude", envs={
"ANTHROPIC_API_KEY": os.environ["ANTHROPIC_API_KEY"],
}, mcp={
"browserbase": {
"apiKey": os.environ["BROWSERBASE_API_KEY"],
"projectId": os.environ["BROWSERBASE_PROJECT_ID"],
},
})
mcp_url = sandbox.get_mcp_url()
mcp_token = sandbox.get_mcp_token()
sandbox.commands.run(
f'claude mcp add --transport http e2b-mcp-gateway {mcp_url} --header "Authorization: Bearer {mcp_token}"',
)
result = sandbox.commands.run(
'claude --dangerously-skip-permissions -p "Use browserbase to research E2B and summarize your findings"',
on_stdout=lambda data: print(data, end=""),
)
sandbox.kill()
```
## Build a custom template
If you need to customize the environment (e.g. pre-install dependencies, add config files), build your own template on top of the pre-built `claude` template.
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
// template.ts
import { Template } from 'e2b'
export const template = Template()
.fromTemplate('claude')
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
# template.py
from e2b import Template
template = (
Template()
.from_template("claude")
)
```
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
// build.ts
import { Template, defaultBuildLogger } from 'e2b'
import { template as claudeCodeTemplate } from './template'
await Template.build(claudeCodeTemplate, 'my-claude', {
cpuCount: 2,
memoryMB: 2048,
onBuildLogs: defaultBuildLogger(),
})
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
# build.py
from e2b import Template, default_build_logger
from template import template as claude_code_template
Template.build(claude_code_template, "my-claude",
cpu_count=2,
memory_mb=2048,
on_build_logs=default_build_logger(),
)
```
Run the build script to create the template.
```bash JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
npx tsx build.ts
```
```bash Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
python build.py
```
## Related guides
Connect Claude Code to 200+ MCP tools
Auto-pause, resume, and manage sandbox lifecycle
Clone repos, manage branches, and push changes
# Codex
Source: https://e2b.mintlify.app/docs/agents/codex
Run OpenAI Codex in a secure E2B sandbox with full filesystem, terminal, and git access.
[Codex](https://github.com/openai/codex) is OpenAI's open-source coding agent. E2B provides a pre-built `codex` template with Codex already installed.
## CLI
Create a sandbox with the [E2B CLI](/docs/cli).
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
e2b sbx create codex
```
Once inside the sandbox, start Codex.
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
codex
```
## Run headless
Use `codex exec` for non-interactive mode and `--full-auto` to auto-approve tool calls (safe inside E2B sandboxes). Pass `--skip-git-repo-check` to bypass git directory ownership checks inside the sandbox. Pass `CODEX_API_KEY` as an environment variable.
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const sandbox = await Sandbox.create('codex', {
envs: { CODEX_API_KEY: process.env.CODEX_API_KEY },
})
const result = await sandbox.commands.run(
`codex exec --full-auto --skip-git-repo-check "Create a hello world HTTP server in Go"`
)
console.log(result.stdout)
await sandbox.kill()
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import os
from e2b import Sandbox
sandbox = Sandbox.create("codex", envs={
"CODEX_API_KEY": os.environ["CODEX_API_KEY"],
})
result = sandbox.commands.run(
'codex exec --full-auto --skip-git-repo-check "Create a hello world HTTP server in Go"',
)
print(result.stdout)
sandbox.kill()
```
### Example: work on a cloned repository
Use `-C` to set Codex's working directory to a cloned repo.
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const sandbox = await Sandbox.create('codex', {
envs: { CODEX_API_KEY: process.env.CODEX_API_KEY },
timeoutMs: 600_000,
})
await sandbox.git.clone('https://github.com/your-org/your-repo.git', {
path: '/home/user/repo',
username: 'x-access-token',
password: process.env.GITHUB_TOKEN,
depth: 1,
})
const result = await sandbox.commands.run(
`codex exec --full-auto --skip-git-repo-check -C /home/user/repo "Add error handling to all API endpoints"`,
{ onStdout: (data) => process.stdout.write(data) }
)
const diff = await sandbox.commands.run('cd /home/user/repo && git diff')
console.log(diff.stdout)
await sandbox.kill()
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import os
from e2b import Sandbox
sandbox = Sandbox.create("codex", envs={
"CODEX_API_KEY": os.environ["CODEX_API_KEY"],
}, timeout=600)
sandbox.git.clone("https://github.com/your-org/your-repo.git",
path="/home/user/repo",
username="x-access-token",
password=os.environ["GITHUB_TOKEN"],
depth=1,
)
result = sandbox.commands.run(
'codex exec --full-auto --skip-git-repo-check -C /home/user/repo "Add error handling to all API endpoints"',
on_stdout=lambda data: print(data, end=""),
)
diff = sandbox.commands.run("cd /home/user/repo && git diff")
print(diff.stdout)
sandbox.kill()
```
## Schema-validated output
Use `--output-schema` to constrain the agent's final response to a JSON Schema. This ensures the output conforms to a specific structure — useful for building reliable pipelines.
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const sandbox = await Sandbox.create('codex', {
envs: { CODEX_API_KEY: process.env.CODEX_API_KEY },
})
await sandbox.files.write('/home/user/schema.json', JSON.stringify({
type: 'object',
properties: {
issues: {
type: 'array',
items: {
type: 'object',
properties: {
file: { type: 'string' },
line: { type: 'number' },
severity: { type: 'string', enum: ['low', 'medium', 'high', 'critical'] },
description: { type: 'string' },
},
required: ['file', 'severity', 'description'],
},
},
},
required: ['issues'],
}))
const result = await sandbox.commands.run(
`codex exec --full-auto --skip-git-repo-check --output-schema /home/user/schema.json -C /home/user/repo "Review this codebase for security issues"`
)
const response = JSON.parse(result.stdout)
console.log(response.issues)
await sandbox.kill()
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import os
import json
from e2b import Sandbox
sandbox = Sandbox.create("codex", envs={
"CODEX_API_KEY": os.environ["CODEX_API_KEY"],
})
sandbox.files.write("/home/user/schema.json", json.dumps({
"type": "object",
"properties": {
"issues": {
"type": "array",
"items": {
"type": "object",
"properties": {
"file": {"type": "string"},
"line": {"type": "number"},
"severity": {"type": "string", "enum": ["low", "medium", "high", "critical"]},
"description": {"type": "string"},
},
"required": ["file", "severity", "description"],
},
},
},
"required": ["issues"],
}))
result = sandbox.commands.run(
'codex exec --full-auto --skip-git-repo-check --output-schema /home/user/schema.json -C /home/user/repo "Review this codebase for security issues"',
)
response = json.loads(result.stdout)
print(response["issues"])
sandbox.kill()
```
## Streaming events
Use `--json` to get a JSONL event stream. Each line is a JSON object representing an agent event (tool calls, file changes, messages). Progress goes to stderr; events go to stdout.
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const sandbox = await Sandbox.create('codex', {
envs: { CODEX_API_KEY: process.env.CODEX_API_KEY },
})
const result = await sandbox.commands.run(
`codex exec --full-auto --skip-git-repo-check --json -C /home/user/repo "Refactor the utils module into separate files"`,
{
onStdout: (data) => {
for (const line of data.split('\n').filter(Boolean)) {
const event = JSON.parse(line)
console.log(`[${event.type}]`, event)
}
},
}
)
await sandbox.kill()
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import os
import json
from e2b import Sandbox
sandbox = Sandbox.create("codex", envs={
"CODEX_API_KEY": os.environ["CODEX_API_KEY"],
})
def handle_event(data):
for line in data.strip().split("\n"):
if line:
event = json.loads(line)
print(f"[{event['type']}]", event)
result = sandbox.commands.run(
'codex exec --full-auto --skip-git-repo-check --json -C /home/user/repo "Refactor the utils module into separate files"',
on_stdout=handle_event,
)
sandbox.kill()
```
## Image input
Pass screenshots or design mockups with `--image` to give Codex visual context alongside the prompt.
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import fs from 'fs'
import { Sandbox } from 'e2b'
const sandbox = await Sandbox.create('codex', {
envs: { CODEX_API_KEY: process.env.CODEX_API_KEY },
timeoutMs: 600_000,
})
// Upload a design mockup to the sandbox
await sandbox.files.write(
'/home/user/mockup.png',
fs.readFileSync('./mockup.png')
)
const result = await sandbox.commands.run(
`codex exec --full-auto --skip-git-repo-check --image /home/user/mockup.png -C /home/user/repo "Implement this UI design as a React component"`,
{ onStdout: (data) => process.stdout.write(data) }
)
await sandbox.kill()
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import os
from e2b import Sandbox
sandbox = Sandbox.create("codex", envs={
"CODEX_API_KEY": os.environ["CODEX_API_KEY"],
}, timeout=600)
# Upload a design mockup to the sandbox
with open("./mockup.png", "rb") as f:
sandbox.files.write("/home/user/mockup.png", f)
result = sandbox.commands.run(
'codex exec --full-auto --skip-git-repo-check --image /home/user/mockup.png -C /home/user/repo "Implement this UI design as a React component"',
on_stdout=lambda data: print(data, end=""),
)
sandbox.kill()
```
## Build a custom template
If you need to customize the environment (e.g. pre-install dependencies, add config files), build your own template on top of the pre-built `codex` template.
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
// template.ts
import { Template } from 'e2b'
export const template = Template()
.fromTemplate('codex')
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
# template.py
from e2b import Template
template = (
Template()
.from_template("codex")
)
```
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
// build.ts
import { Template, defaultBuildLogger } from 'e2b'
import { template as codexTemplate } from './template'
await Template.build(codexTemplate, 'my-codex', {
cpuCount: 2,
memoryMB: 2048,
onBuildLogs: defaultBuildLogger(),
})
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
# build.py
from e2b import Template, default_build_logger
from template import template as codex_template
Template.build(codex_template, "my-codex",
cpu_count=2,
memory_mb=2048,
on_build_logs=default_build_logger(),
)
```
Run the build script to create the template.
```bash JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
npx tsx build.ts
```
```bash Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
python build.py
```
## Related guides
Auto-pause, resume, and manage sandbox lifecycle
Clone repos, manage branches, and push changes
Connect to the sandbox via SSH for interactive sessions
# OpenAI Agents SDK
Source: https://e2b.mintlify.app/docs/agents/openai-agents-sdk
Use E2B sandboxes with the OpenAI Agents SDK.
The [OpenAI Agents SDK](https://github.com/openai/openai-agents-python) is a framework for building agentic workflows. E2B provides a native integration that lets you run `SandboxAgent` instances inside isolated E2B sandboxes — giving your agents full filesystem, terminal, and network access in a secure environment.
To use E2B as the sandbox backend:
1. Create a sandbox session with `E2BSandboxClient`.
2. Build a `SandboxAgent` with your instructions and model.
3. Run the agent and pass the sandbox session through `RunConfig`.
## Install the dependencies
Install the [OpenAI Agents SDK](https://pypi.org/project/openai-agents/) with the E2B extra to pull in the sandbox integration.
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
pip install openai-agents[e2b]
```
You will also need API keys for OpenAI and E2B.
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
export OPENAI_API_KEY="..."
export E2B_API_KEY="..."
```
## Basic example
Create an `E2BSandboxClient`, start a session, and run a `SandboxAgent` inside it. The agent gets full access to the sandbox environment — it can run commands, read and write files, and inspect the workspace.
### Create a session
Initialize the `E2BSandboxClient` and create a sandbox session. The `pause_on_exit` option keeps the sandbox available after the script finishes so you can inspect its state.
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from agents.extensions.sandbox import (
E2BSandboxClient,
E2BSandboxClientOptions,
E2BSandboxType,
)
client = E2BSandboxClient()
session = await client.create(
options=E2BSandboxClientOptions(
sandbox_type=E2BSandboxType.E2B,
timeout=900,
pause_on_exit=True,
)
)
```
### Build and run the agent
Define a `SandboxAgent` with a name, model, and instructions, then run it against the sandbox session using `Runner.run`. The result contains the agent's final output.
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from agents import ModelSettings, Runner
from agents.run import RunConfig
from agents.sandbox import SandboxAgent, SandboxRunConfig
agent = SandboxAgent(
name="Workspace Inspector",
model="gpt-5.4",
instructions=(
"Inspect the workspace, explain what files exist, and summarize the project."
),
model_settings=ModelSettings(tool_choice="required"),
)
result = await Runner.run(
agent,
"Look around the workspace and summarize what you find.",
run_config=RunConfig(sandbox=SandboxRunConfig(session=session)),
)
print(result.final_output)
```
### Shut down
Always shut down the session when you're done to release sandbox resources.
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
await session.shutdown()
```
### Full example
The complete script that ties the steps above together.
```python expandable theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import asyncio
from agents import ModelSettings, Runner
from agents.run import RunConfig
from agents.sandbox import SandboxAgent, SandboxRunConfig
from agents.extensions.sandbox import (
E2BSandboxClient,
E2BSandboxClientOptions,
E2BSandboxType,
)
async def main() -> None:
client = E2BSandboxClient()
session = await client.create(
options=E2BSandboxClientOptions(
sandbox_type=E2BSandboxType.E2B,
timeout=900,
pause_on_exit=True,
)
)
try:
agent = SandboxAgent(
name="Workspace Inspector",
model="gpt-5.4",
instructions=(
"Inspect the workspace, explain what files exist, and summarize the project."
),
model_settings=ModelSettings(tool_choice="required"),
)
result = await Runner.run(
agent,
"Look around the workspace and summarize what you find.",
run_config=RunConfig(sandbox=SandboxRunConfig(session=session)),
)
print(result.final_output)
finally:
await session.shutdown()
asyncio.run(main())
```
***
## Build an app with multiple versions
A common pattern is to start from the same starter app and create multiple versions in separate sandboxes — useful when comparing a first pass with a polished revision, or generating live preview URLs for each version.
Based on the [`homepage_vite_basic_updated.ipynb`](https://github.com/openai/openai-agents-python/blob/main/examples/sandbox/e2b_demos/homepage_vite_basic_updated.ipynb) notebook from the Agents SDK repo.
### Define a manifest
A `Manifest` describes the starter files your agent will work with. Each entry is a `File` with its content encoded as bytes. This lets you seed multiple sandboxes from the same baseline — useful when comparing different versions of an app.
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from agents.sandbox import Manifest
from agents.sandbox.entries import File
def build_manifest() -> Manifest:
return Manifest(
entries={
"package.json": File(content=PACKAGE_JSON.encode()),
"index.html": File(content=INDEX_HTML.encode()),
"vite.config.js": File(content=VITE_CONFIG_JS.encode()),
"src/main.tsx": File(content=MAIN_TSX.encode()),
"src/App.tsx": File(content=APP_TSX.encode()),
}
)
```
### Create a sandbox session
Create a sandbox session with the manifest, exposed ports for live previews, and internet access so the agent can install npm packages.
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from agents.extensions.sandbox import (
E2BSandboxClient,
E2BSandboxClientOptions,
E2BSandboxType,
)
session = await E2BSandboxClient().create(
manifest=build_manifest(),
options=E2BSandboxClientOptions(
sandbox_type=E2BSandboxType.E2B,
timeout=1800,
exposed_ports=(4173,),
allow_internet_access=True,
pause_on_exit=True,
),
)
await session.start()
```
### Run the agent
Build a `SandboxAgent` with capabilities like `ApplyPatch` and `Shell`, then run it against the sandbox session.
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from agents import ModelSettings, Runner
from agents.run import RunConfig
from agents.sandbox import SandboxAgent, SandboxRunConfig
from agents.sandbox.capabilities import ApplyPatch, Shell
agent = SandboxAgent(
name="E2B Vite Builder",
model="gpt-5.4-mini",
instructions="Update the Vite app in the sandbox workspace and return a concise summary.",
default_manifest=build_manifest(),
capabilities=[ApplyPatch(), Shell()],
model_settings=ModelSettings(tool_choice="required"),
)
result = await Runner.run(
agent,
"Make the basic version now.",
run_config=RunConfig(sandbox=SandboxRunConfig(session=session)),
)
```
### Start a preview server
After the agent finishes, install dependencies, start the Vite dev server, and resolve the exposed port to get a live preview URL.
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
await session.exec(
"sh", "-lc",
(
"npm install >/tmp/e2b-demo-npm-install.log 2>&1 && "
"nohup npm run dev -- --host 0.0.0.0 --port 4173 "
">/tmp/e2b-demo-vite.log 2>&1 &"
),
shell=False,
timeout=120,
)
preview_url = (await session.resolve_exposed_port(4173)).url_for("http")
```
### Full example
The complete `run_version()` helper ties all the steps above together. Call it once per version to get isolated sandboxes with their own preview URLs. Based on the [`homepage_vite_basic_updated.ipynb`](https://github.com/openai/openai-agents-python/blob/main/examples/sandbox/e2b_demos/homepage_vite_basic_updated.ipynb) notebook from the Agents SDK repo.
```python expandable theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from agents import ModelSettings, Runner
from agents.run import RunConfig
from agents.sandbox import Manifest, SandboxAgent, SandboxRunConfig
from agents.sandbox.capabilities import ApplyPatch, Shell
from agents.sandbox.entries import File
from agents.extensions.sandbox import (
E2BSandboxClient,
E2BSandboxClientOptions,
E2BSandboxType,
)
def build_manifest() -> Manifest:
return Manifest(
entries={
"package.json": File(content=PACKAGE_JSON.encode()),
"index.html": File(content=INDEX_HTML.encode()),
"vite.config.js": File(content=VITE_CONFIG_JS.encode()),
"src/main.tsx": File(content=MAIN_TSX.encode()),
"src/App.tsx": File(content=APP_TSX.encode()),
}
)
async def run_version(version_name: str, version_prompt: str) -> dict[str, str]:
session = await E2BSandboxClient().create(
manifest=build_manifest(),
options=E2BSandboxClientOptions(
sandbox_type=E2BSandboxType.E2B,
timeout=1800,
exposed_ports=(4173,),
allow_internet_access=True,
pause_on_exit=True,
),
)
await session.start()
agent = SandboxAgent(
name=f"E2B Vite {version_name.title()} Builder",
model="gpt-5.4-mini",
instructions=(
"Update the Vite app in the sandbox workspace and return a concise summary."
),
developer_instructions=(
f"Version goal: {version_prompt}\n"
"Start from the tiny Vite starter. You may create src/styles.css if you want."
),
default_manifest=build_manifest(),
capabilities=[ApplyPatch(), Shell()],
model_settings=ModelSettings(tool_choice="required"),
)
result = await Runner.run(
agent,
f"Make the {version_name} version now.",
run_config=RunConfig(sandbox=SandboxRunConfig(session=session)),
)
await session.exec(
"sh", "-lc",
(
"npm install >/tmp/e2b-demo-npm-install.log 2>&1 && "
"nohup npm run dev -- --host 0.0.0.0 --port 4173 "
">/tmp/e2b-demo-vite.log 2>&1 &"
),
shell=False,
timeout=120,
)
preview_url = (await session.resolve_exposed_port(4173)).url_for("http")
return {
"summary": str(result.final_output),
"preview_url": preview_url,
}
```
***
## MCP-powered research agents
You can create sandboxes with [MCP](https://modelcontextprotocol.io/) servers enabled, then connect the Agents SDK to the sandbox's MCP gateway. This gives you an agent that can discover sources with search-oriented MCP servers, verify pages in a browser, and keep all of that execution inside the sandbox.
The Agents SDK repo includes a concrete example in [`deep_research_mcp.py`](https://github.com/openai/openai-agents-python/blob/main/examples/sandbox/e2b_demos/deep_research_mcp.py).
### Configure MCP servers
Pass MCP server configurations when creating the sandbox session. This example enables [Browserbase](https://www.browserbase.com/) for browser automation and [Exa](https://exa.ai/) for search.
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
session = await client.create(
options=E2BSandboxClientOptions(
sandbox_type=E2BSandboxType.E2B,
timeout=900,
pause_on_exit=True,
mcp={
"browserbase": {
"apiKey": os.environ["BROWSERBASE_API_KEY"],
"geminiApiKey": os.environ["GEMINI_API_KEY"],
"projectId": os.environ["BROWSERBASE_PROJECT_ID"],
},
"exa": {
"apiKey": os.environ["EXA_API_KEY"],
},
},
),
)
```
### Connect to the MCP gateway
Use `MCPServerStreamableHttp` to connect to the sandbox's MCP gateway. This gives the agent access to all the MCP servers you configured above.
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from agents.mcp import MCPServerStreamableHttp
async with MCPServerStreamableHttp(
name="E2B MCP Gateway",
params={
"url": mcp_url,
"headers": {"Authorization": f"Bearer {mcp_token}"},
"timeout": 30,
"sse_read_timeout": 300,
},
) as server:
...
```
### Run the agent
Pass the MCP server to the `SandboxAgent` via `mcp_servers`. The agent can now discover and call tools from all configured MCP servers.
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from agents import ModelSettings, Runner
from agents.run import RunConfig
from agents.sandbox import SandboxAgent, SandboxRunConfig
agent = SandboxAgent(
name="Deep Research Assistant",
model="gpt-5.4",
instructions=(
"Use Exa to discover strong sources and Browserbase to verify important claims."
),
mcp_servers=[server],
model_settings=ModelSettings(tool_choice="required"),
)
result = await Runner.run(
agent,
"Research browser automation infrastructure for AI agents.",
run_config=RunConfig(sandbox=SandboxRunConfig(session=session)),
)
```
### Full example
The complete script combining session creation, MCP gateway connection, and agent execution.
```python expandable theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from agents import ModelSettings, Runner
from agents.mcp import MCPServerStreamableHttp
from agents.run import RunConfig
from agents.sandbox import SandboxAgent, SandboxRunConfig
session = await client.create(
options=E2BSandboxClientOptions(
sandbox_type=E2BSandboxType.E2B,
timeout=900,
pause_on_exit=True,
mcp={
"browserbase": {
"apiKey": os.environ["BROWSERBASE_API_KEY"],
"geminiApiKey": os.environ["GEMINI_API_KEY"],
"projectId": os.environ["BROWSERBASE_PROJECT_ID"],
},
"exa": {
"apiKey": os.environ["EXA_API_KEY"],
},
},
),
)
async with MCPServerStreamableHttp(
name="E2B MCP Gateway",
params={
"url": mcp_url,
"headers": {"Authorization": f"Bearer {mcp_token}"},
"timeout": 30,
"sse_read_timeout": 300,
},
) as server:
agent = SandboxAgent(
name="Deep Research Assistant",
model="gpt-5.4",
instructions=(
"Use Exa to discover strong sources and Browserbase to verify important claims."
),
mcp_servers=[server],
model_settings=ModelSettings(tool_choice="required"),
)
result = await Runner.run(
agent,
"Research browser automation infrastructure for AI agents.",
run_config=RunConfig(sandbox=SandboxRunConfig(session=session)),
)
```
***
## Parallel sandbox workers
Use one coordinator agent to launch multiple specialized review or analysis lanes across separate sandboxes. Each lane runs in its own isolated environment, and the coordinator synthesizes the results into a single summary.
The Agents SDK repo includes a concrete example in [`fullstack_code_review_parallel.py`](https://github.com/openai/openai-agents-python/blob/main/examples/sandbox/e2b_demos/fullstack_code_review_parallel.py). That example uses separate E2B-backed lanes for frontend review, backend review, and git tree review.
### Define the agents
Create specialized `SandboxAgent` instances for each review lane, plus a regular `Agent` as the coordinator that will synthesize the results.
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from agents import Agent
from agents.sandbox import SandboxAgent
frontend_agent = SandboxAgent(
name="Frontend Reviewer",
model="gpt-5.4",
instructions="Review the rendered frontend and identify UX and code risks.",
)
backend_agent = SandboxAgent(
name="Backend Reviewer",
model="gpt-5.4",
instructions="Review the API implementation and identify validation and auth risks.",
)
coordinator = Agent(
name="Review Coordinator",
model="gpt-5.4",
instructions=(
"Run the available review lanes, compare their evidence, and produce a single summary."
),
)
```
### Run the lanes
Run each review agent against its own sandbox session. The lanes are independent and can run concurrently.
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from agents import Runner
from agents.run import RunConfig
from agents.sandbox import SandboxRunConfig
frontend_result = await Runner.run(
frontend_agent,
"Review the frontend workspace.",
run_config=RunConfig(sandbox=SandboxRunConfig(session=frontend_session)),
)
backend_result = await Runner.run(
backend_agent,
"Review the backend workspace.",
run_config=RunConfig(sandbox=SandboxRunConfig(session=backend_session)),
)
```
### Synthesize the results
Feed the findings from each lane into the coordinator agent to produce a single summary.
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
final_result = await Runner.run(
coordinator,
f"Frontend findings: {frontend_result.final_output}\n\n"
f"Backend findings: {backend_result.final_output}",
)
```
### Full example
The complete script defining agents, running parallel review lanes, and synthesizing results.
```python expandable theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from agents import Agent, Runner
from agents.run import RunConfig
from agents.sandbox import SandboxAgent, SandboxRunConfig
frontend_agent = SandboxAgent(
name="Frontend Reviewer",
model="gpt-5.4",
instructions="Review the rendered frontend and identify UX and code risks.",
)
backend_agent = SandboxAgent(
name="Backend Reviewer",
model="gpt-5.4",
instructions="Review the API implementation and identify validation and auth risks.",
)
coordinator = Agent(
name="Review Coordinator",
model="gpt-5.4",
instructions=(
"Run the available review lanes, compare their evidence, and produce a single summary."
),
)
frontend_result = await Runner.run(
frontend_agent,
"Review the frontend workspace.",
run_config=RunConfig(sandbox=SandboxRunConfig(session=frontend_session)),
)
backend_result = await Runner.run(
backend_agent,
"Review the backend workspace.",
run_config=RunConfig(sandbox=SandboxRunConfig(session=backend_session)),
)
final_result = await Runner.run(
coordinator,
f"Frontend findings: {frontend_result.final_output}\n\n"
f"Backend findings: {backend_result.final_output}",
)
```
## Reference examples
The OpenAI Agents SDK repository includes several complete examples demonstrating E2B sandbox integration.
Build and iterate on a Vite app across isolated sandboxes
Research agent with Browserbase and Exa via MCP
Multi-lane code review with a coordinator agent
Getting started notebook for E2B sandbox integration
# Deploy OpenClaw
Source: https://e2b.mintlify.app/docs/agents/openclaw/openclaw-gateway
Start the OpenClaw gateway in an E2B sandbox and connect your browser.
## Quick start
This launches your OpenClaw [gateway](https://docs.openclaw.ai/gateway) site (web UI for chatting with agents).
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const TOKEN = process.env.OPENCLAW_APP_TOKEN || 'my-gateway-token'
const PORT = 18789
// 1. Create sandbox
const sandbox = await Sandbox.create('openclaw', {
envs: { OPENAI_API_KEY: process.env.OPENAI_API_KEY },
timeoutMs: 3600_000,
})
// 2. Set the default model
await sandbox.commands.run('openclaw config set agents.defaults.model.primary openai/gpt-5.2')
// 3. Set insecure control UI flags and start the gateway with token auth
await sandbox.commands.run(
`bash -lc 'openclaw config set gateway.controlUi.allowInsecureAuth true && ` +
`openclaw config set gateway.controlUi.dangerouslyDisableDeviceAuth true && ` +
`openclaw gateway --allow-unconfigured --bind lan --auth token --token ${TOKEN} --port ${PORT}'`,
{ background: true }
)
// 4. Wait for the gateway to start listening
for (let i = 0; i < 45; i++) {
const probe = await sandbox.commands.run(
`bash -lc 'ss -ltn | grep -q ":${PORT} " && echo ready || echo waiting'`
)
if (probe.stdout.trim() === 'ready') break
await new Promise((r) => setTimeout(r, 1000))
}
const url = `https://${sandbox.getHost(PORT)}/?token=${TOKEN}`
console.log(`Gateway: ${url}`)
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import os, time
from e2b import Sandbox
TOKEN = os.environ.get("OPENCLAW_APP_TOKEN", "my-gateway-token")
PORT = 18789
# 1. Create sandbox
sandbox = Sandbox.create("openclaw", envs={
"OPENAI_API_KEY": os.environ["OPENAI_API_KEY"],
}, timeout=3600)
# 2. Set the default model
sandbox.commands.run("openclaw config set agents.defaults.model.primary openai/gpt-5.2")
# 3. Set insecure control UI flags and start the gateway with token auth
sandbox.commands.run(
f"bash -lc 'openclaw config set gateway.controlUi.allowInsecureAuth true && "
f"openclaw config set gateway.controlUi.dangerouslyDisableDeviceAuth true && "
f"openclaw gateway --allow-unconfigured --bind lan --auth token --token {TOKEN} --port {PORT}'",
background=True,
)
# 4. Wait for the gateway to start listening
for _ in range(45):
probe = sandbox.commands.run(
f'bash -lc \'ss -ltn | grep -q ":{PORT} " && echo ready || echo waiting\''
)
if probe.stdout.strip() == "ready":
break
time.sleep(1)
url = f"https://{sandbox.get_host(PORT)}/?token={TOKEN}"
print(f"Gateway: {url}")
```
Visit the printed `Gateway` URL in your browser.
If you run in secure mode (set `gateway.controlUi.dangerouslyDisableDeviceAuth false`), run this after opening the URL to poll pending pairing requests and approve the first one.
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
// 5. Poll for the browser's pending device request and approve it
for (let i = 0; i < 30; i++) {
try {
const res = await sandbox.commands.run(
`openclaw devices list --json --url ws://127.0.0.1:${PORT} --token ${TOKEN}`
)
const data = JSON.parse(res.stdout)
if (data.pending?.length) {
const rid = data.pending[0].requestId
await sandbox.commands.run(
`openclaw devices approve ${rid} --token ${TOKEN} --url ws://127.0.0.1:${PORT}`
)
console.log(`Device approved: ${rid}`)
break
}
} catch {}
await new Promise((r) => setTimeout(r, 2000))
}
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import json
# 5. Poll for the browser's pending device request and approve it
for _ in range(30):
try:
res = sandbox.commands.run(
f"openclaw devices list --json --url ws://127.0.0.1:{PORT} --token {TOKEN}"
)
data = json.loads(res.stdout)
if data.get("pending"):
rid = data["pending"][0]["requestId"]
sandbox.commands.run(
f"openclaw devices approve {rid} --token {TOKEN} --url ws://127.0.0.1:{PORT}"
)
print(f"Device approved: {rid}")
break
except Exception:
pass
time.sleep(2)
```
Once approved, the browser connects and the gateway UI loads.
## How it works
| Step | What happens |
| ---------------------------- | ---------------------------------------------------------------------------- |
| `--bind lan` | Gateway listens on `0.0.0.0` so E2B can proxy it |
| `--auth token` | Requires `?token=` on the URL for HTTP and WebSocket auth |
| Browser opens URL | Gateway serves the UI, browser opens a WebSocket |
| `code=1008 pairing required` | Gateway closes the WebSocket until the device is approved (secure mode only) |
| `devices approve` | Approves the browser's device fingerprint (secure mode only) |
| Browser reconnects | WebSocket connects successfully, UI is live |
## Gateway flags reference
| Flag | Purpose |
| ---------------------- | -------------------------------------------------- |
| `--allow-unconfigured` | Start without a full config file |
| `--bind lan` | Bind to `0.0.0.0` (required for E2B port proxying) |
| `--auth token` | Enable token-based authentication |
| `--token ` | The auth token (passed as `?token=` in the URL) |
| `--port ` | Gateway listen port (default: `18789`) |
## How to restart the gateway
Use this when the gateway is already running and you want a clean restart (for example, after changing model or env settings).
We can't use the `openclaw gateway restart` command here. Some SDK environments cannot target a specific Unix user in `commands.run`. The commands below use the default command user context.
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
const TOKEN = process.env.OPENCLAW_APP_TOKEN || 'my-gateway-token'
const PORT = 18789
// 1) Kill existing gateway processes if present
await sandbox.commands.run(
`bash -lc 'for p in "[o]penclaw gateway" "[o]penclaw-gateway"; do for pid in $(pgrep -f "$p" || true); do kill "$pid" >/dev/null 2>&1 || true; done; done'`
)
await new Promise((r) => setTimeout(r, 1000))
// 2) Start gateway again
await sandbox.commands.run(
`openclaw gateway --allow-unconfigured --bind lan --auth token --token ${TOKEN} --port ${PORT}`,
{ background: true }
)
// 3) Wait for listening socket
for (let i = 0; i < 45; i++) {
const probe = await sandbox.commands.run(
`bash -lc 'ss -ltn | grep -q ":${PORT} " && echo ready || echo waiting'`
)
if (probe.stdout.trim() === 'ready') break
await new Promise((r) => setTimeout(r, 1000))
}
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import os, time
TOKEN = os.environ.get("OPENCLAW_APP_TOKEN", "my-gateway-token")
PORT = 18789
# 1) Kill existing gateway processes if present
sandbox.commands.run(
"""bash -lc 'for p in "[o]penclaw gateway" "[o]penclaw-gateway"; do
for pid in $(pgrep -f "$p" || true); do
kill "$pid" >/dev/null 2>&1 || true
done
done'"""
)
time.sleep(1)
# 2) Start gateway again
sandbox.commands.run(
f"openclaw gateway --allow-unconfigured --bind lan --auth token --token {TOKEN} --port {PORT}",
background=True,
)
# 3) Wait for listening socket
for _ in range(45):
probe = sandbox.commands.run(
f'bash -lc \'ss -ltn | grep -q ":{PORT} " && echo ready || echo waiting\''
)
if probe.stdout.strip() == "ready":
break
time.sleep(1)
```
## Turn insecure flags off (recommended after testing)
Use this to restore secure device authentication after initial testing.
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
const TOKEN = process.env.OPENCLAW_APP_TOKEN || 'my-gateway-token'
const PORT = 18789
await sandbox.commands.run(
`bash -lc 'openclaw config set gateway.controlUi.allowInsecureAuth false && ` +
`openclaw config set gateway.controlUi.dangerouslyDisableDeviceAuth false'`
)
await sandbox.commands.run(
`bash -lc 'for p in "[o]penclaw gateway" "[o]penclaw-gateway"; do for pid in $(pgrep -f "$p" || true); do kill "$pid" >/dev/null 2>&1 || true; done; done'`
)
await sandbox.commands.run(
`openclaw gateway --allow-unconfigured --bind lan --auth token --token ${TOKEN} --port ${PORT}`,
{ background: true }
)
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import os
TOKEN = os.environ.get("OPENCLAW_APP_TOKEN", "my-gateway-token")
PORT = 18789
sandbox.commands.run(
"bash -lc 'openclaw config set gateway.controlUi.allowInsecureAuth false && "
"openclaw config set gateway.controlUi.dangerouslyDisableDeviceAuth false'"
)
sandbox.commands.run(
"""bash -lc 'for p in "[o]penclaw gateway" "[o]penclaw-gateway"; do
for pid in $(pgrep -f "$p" || true); do
kill "$pid" >/dev/null 2>&1 || true
done
done'"""
)
sandbox.commands.run(
f"openclaw gateway --allow-unconfigured --bind lan --auth token --token {TOKEN} --port {PORT}",
background=True,
)
```
## Related
Connect OpenClaw to Telegram and approve pairing
# OpenClaw Telegram
Source: https://e2b.mintlify.app/docs/agents/openclaw/openclaw-telegram
Connect OpenClaw to Telegram in an E2B sandbox, approve pairing, and chat through your bot.
OpenClaw supports Telegram as a chat channel. In E2B you can run OpenClaw in a sandbox, attach your bot token, and approve user pairing from the terminal.
This guide covers the working flow we used:
1. Start OpenClaw in a sandbox.
2. Enable the Telegram plugin.
3. Add Telegram channel credentials.
4. Start the channel runtime in background.
5. Approve Telegram pairing.
## Prerequisites
* A Telegram bot token from [@BotFather](https://t.me/BotFather). There's instructions to follow there, it runs /newbot for you and walks you through naming and creating your bot.
* An OpenAI API key for the OpenClaw model.
* E2B API key configured locally.
## Quick start
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const GATEWAY_PORT = 18789
const GATEWAY_TOKEN = process.env.OPENCLAW_APP_TOKEN || 'my-openclaw-token'
const sandbox = await Sandbox.create('openclaw', {
envs: {
OPENAI_API_KEY: process.env.OPENAI_API_KEY,
TELEGRAM_BOT_TOKEN: process.env.TELEGRAM_BOT_TOKEN,
},
timeoutMs: 3600_000,
})
await sandbox.commands.run('openclaw config set agents.defaults.model.primary openai/gpt-5.2')
// Enable the Telegram plugin (required before adding the channel)
await sandbox.commands.run('openclaw config set plugins.entries.telegram.enabled true')
await sandbox.commands.run('openclaw channels add --channel telegram --token "$TELEGRAM_BOT_TOKEN"')
await sandbox.commands.run(
`bash -lc 'openclaw config set gateway.controlUi.allowInsecureAuth true && ` +
`openclaw config set gateway.controlUi.dangerouslyDisableDeviceAuth true && ` +
`openclaw gateway --allow-unconfigured --bind lan --auth token --token ${GATEWAY_TOKEN} --port ${GATEWAY_PORT}'`,
{ background: true }
)
for (let i = 0; i < 45; i++) {
const probe = await sandbox.commands.run(
`bash -lc 'ss -ltn | grep -q ":${GATEWAY_PORT} " && echo ready || echo waiting'`
)
if (probe.stdout.trim() === 'ready') break
await new Promise((r) => setTimeout(r, 1000))
}
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import os
import time
from e2b import Sandbox
GATEWAY_PORT = 18789
GATEWAY_TOKEN = os.environ.get("OPENCLAW_APP_TOKEN", "my-openclaw-token")
sandbox = Sandbox.create("openclaw", envs={
"OPENAI_API_KEY": os.environ["OPENAI_API_KEY"],
"TELEGRAM_BOT_TOKEN": os.environ["TELEGRAM_BOT_TOKEN"],
}, timeout=3600)
sandbox.commands.run("openclaw config set agents.defaults.model.primary openai/gpt-5.2")
# Enable the Telegram plugin (required before adding the channel)
sandbox.commands.run("openclaw config set plugins.entries.telegram.enabled true")
sandbox.commands.run('openclaw channels add --channel telegram --token "$TELEGRAM_BOT_TOKEN"')
sandbox.commands.run(
f"bash -lc 'openclaw config set gateway.controlUi.allowInsecureAuth true && "
f"openclaw config set gateway.controlUi.dangerouslyDisableDeviceAuth true && "
f"openclaw gateway --allow-unconfigured --bind lan --auth token --token {GATEWAY_TOKEN} --port {GATEWAY_PORT}'",
background=True,
)
for _ in range(45):
probe = sandbox.commands.run(
f"bash -lc 'ss -ltn | grep -q \":{GATEWAY_PORT} \" && echo ready || echo waiting'"
)
if probe.stdout.strip() == "ready":
break
time.sleep(1)
```
For Telegram setup, you do **not** need to open the gateway URL in a browser. The gateway process is used here as a long-running channel runtime.
## Pair your Telegram user
1. Open your bot in Telegram and send a message (for example: `hi`).
2. Telegram will return a pairing prompt similar to:
```text theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
OpenClaw: access not configured.
Your Telegram user id: ...
Pairing code: XXXXXXXX
Ask the bot owner to approve with:
openclaw pairing approve telegram XXXXXXXX
```
3. Approve that pairing code via `sandbox.commands.run(...)`:
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
const PAIRING_CODE = 'XXXXXXXX' // from Telegram
await sandbox.commands.run(
`openclaw pairing approve --channel telegram ${PAIRING_CODE}`
)
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
PAIRING_CODE = "XXXXXXXX" # from Telegram
sandbox.commands.run(
f"openclaw pairing approve --channel telegram {PAIRING_CODE}"
)
```
`openclaw pairing approve telegram ` also works if you prefer that form.
## Verify channel status
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
const channels = await sandbox.commands.run('openclaw channels list --json')
const status = await sandbox.commands.run('openclaw channels status --json --probe')
const pairing = await sandbox.commands.run('openclaw pairing list --json --channel telegram')
console.log(JSON.parse(channels.stdout))
console.log(JSON.parse(status.stdout))
console.log(JSON.parse(pairing.stdout))
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import json
channels = sandbox.commands.run("openclaw channels list --json")
status = sandbox.commands.run("openclaw channels status --json --probe")
pairing = sandbox.commands.run("openclaw pairing list --json --channel telegram")
print(json.loads(channels.stdout))
print(json.loads(status.stdout))
print(json.loads(pairing.stdout))
```
If you need logs from channel handlers:
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
const logs = await sandbox.commands.run(
'openclaw channels logs --channel telegram --lines 200'
)
console.log(logs.stdout)
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
logs = sandbox.commands.run(
"openclaw channels logs --channel telegram --lines 200"
)
print(logs.stdout)
```
## Troubleshooting
* `Unknown channel: telegram`
* The Telegram plugin is not enabled. Run `openclaw config set plugins.entries.telegram.enabled true` before adding the channel.
* `OpenClaw: access not configured`
* Pairing has not been approved yet. Run `openclaw pairing approve ...`.
* `No API key found for provider ...`
* This guide uses `openai/gpt-5.2`. Set `OPENAI_API_KEY` in sandbox envs.
* No pending pairing requests from `pairing list`
* Send a fresh message to the bot first, then retry `pairing list --channel telegram`.
## Related
Run OpenClaw's web gateway with token auth
# OpenCode
Source: https://e2b.mintlify.app/docs/agents/opencode
Run OpenCode in a secure E2B sandbox with full filesystem, terminal, and git access.
[OpenCode](https://opencode.ai) is an open-source coding agent that supports multiple LLM providers. E2B provides a pre-built `opencode` template with OpenCode already installed.
## CLI
Create a sandbox with the [E2B CLI](/docs/cli).
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
e2b sbx create opencode
```
Once inside the sandbox, start OpenCode.
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
opencode
```
## Run headless
Use `opencode run` for non-interactive mode. Pass your LLM provider's API key as an environment variable — OpenCode supports `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, `GEMINI_API_KEY`, and [others](https://opencode.ai/docs/config/).
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const sandbox = await Sandbox.create('opencode', {
envs: { ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY },
})
const result = await sandbox.commands.run(
`opencode run "Create a hello world HTTP server in Go"`
)
console.log(result.stdout)
await sandbox.kill()
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import os
from e2b import Sandbox
sandbox = Sandbox.create("opencode", envs={
"ANTHROPIC_API_KEY": os.environ["ANTHROPIC_API_KEY"],
})
result = sandbox.commands.run(
'opencode run "Create a hello world HTTP server in Go"',
)
print(result.stdout)
sandbox.kill()
```
### Example: work on a cloned repository
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const sandbox = await Sandbox.create('opencode', {
envs: { ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY },
timeoutMs: 600_000,
})
await sandbox.git.clone('https://github.com/your-org/your-repo.git', {
path: '/home/user/repo',
username: 'x-access-token',
password: process.env.GITHUB_TOKEN,
depth: 1,
})
const result = await sandbox.commands.run(
`cd /home/user/repo && opencode run "Add error handling to all API endpoints"`,
{ onStdout: (data) => process.stdout.write(data) }
)
const diff = await sandbox.commands.run('cd /home/user/repo && git diff')
console.log(diff.stdout)
await sandbox.kill()
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import os
from e2b import Sandbox
sandbox = Sandbox.create("opencode", envs={
"ANTHROPIC_API_KEY": os.environ["ANTHROPIC_API_KEY"],
}, timeout=600)
sandbox.git.clone("https://github.com/your-org/your-repo.git",
path="/home/user/repo",
username="x-access-token",
password=os.environ["GITHUB_TOKEN"],
depth=1,
)
result = sandbox.commands.run(
'cd /home/user/repo && opencode run "Add error handling to all API endpoints"',
on_stdout=lambda data: print(data, end=""),
)
diff = sandbox.commands.run("cd /home/user/repo && git diff")
print(diff.stdout)
sandbox.kill()
```
## Connect with the OpenCode SDK
OpenCode includes a [headless HTTP server](https://opencode.ai/docs/server/) that you can control programmatically using the [`@opencode-ai/sdk`](https://opencode.ai/docs/sdk/) client. Start the server inside a sandbox, get the public URL with `sandbox.getHost()`, and connect from your application.
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
import { createOpencodeClient } from '@opencode-ai/sdk'
const sandbox = await Sandbox.create('opencode', {
envs: { ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY },
lifecycle: {
onTimeout: 'pause', // "pause" | "kill"
},
timeoutMs: 10 * 60 * 1000,
})
// Start the OpenCode server
sandbox.commands.run('opencode serve --hostname 0.0.0.0 --port 4096', {
background: true,
})
// Wait for the server to be ready
const host = sandbox.getHost(4096)
const baseUrl = `https://${host}`
while (true) {
try {
await fetch(`${baseUrl}/global/health`)
break
} catch {
await new Promise((r) => setTimeout(r, 500))
}
}
// Connect to the server
const client = createOpencodeClient({
baseUrl,
})
// Create a session and send a prompt
const { data: session } = await client.session.create({
body: { title: 'E2B Session' },
})
const { data: result } = await client.session.prompt({
path: { id: session.id },
body: {
parts: [{ type: 'text', text: 'Create a hello world HTTP server in Go' }],
},
})
console.log(result)
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import os
import time
import requests
from e2b import Sandbox
sandbox = Sandbox.beta_create("opencode", envs={
"ANTHROPIC_API_KEY": os.environ["ANTHROPIC_API_KEY"],
}, auto_pause=True, timeout=10 * 60)
# Start the OpenCode server
sandbox.commands.run(
"opencode serve --hostname 0.0.0.0 --port 4096",
background=True,
)
# Wait for the server to be ready
host = sandbox.get_host(4096)
base_url = f"https://{host}"
while True:
try:
requests.get(f"{base_url}/global/health")
break
except requests.ConnectionError:
time.sleep(0.5)
# Create a session and send a prompt via the HTTP API
session = requests.post(f"{base_url}/session").json()
result = requests.post(
f"{base_url}/session/{session['id']}/message",
json={
"parts": [{"type": "text", "text": "Create a hello world HTTP server in Go"}],
},
).json()
print(result)
```
## Build a custom template
If you need to customize the environment (e.g. pre-install dependencies, add config files), build your own template on top of the pre-built `opencode` template.
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
// template.ts
import { Template, waitForPort } from 'e2b'
export const template = Template()
.fromTemplate('opencode')
.setEnvs({
OPENCODE_SERVER_PASSWORD: 'your-password',
})
// Optional - start the OpenCode server on sandbox start
.setStartCmd(
'opencode serve --hostname 0.0.0.0 --port 4096',
waitForPort(4096)
)
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
# template.py
from e2b import Template, wait_for_port
template = (
Template()
.from_template("opencode")
.set_envs({
"OPENCODE_SERVER_PASSWORD": "your-password",
})
# Optional - start the OpenCode server on sandbox start
.set_start_cmd(
"opencode serve --hostname 0.0.0.0 --port 4096",
wait_for_port(4096)
)
)
```
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
// build.ts
import { Template, defaultBuildLogger } from 'e2b'
import { template as openCodeTemplate } from './template'
await Template.build(openCodeTemplate, 'my-opencode', {
cpuCount: 2,
memoryMB: 2048,
onBuildLogs: defaultBuildLogger(),
})
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
# build.py
from e2b import Template, default_build_logger
from template import template as opencode_template
Template.build(opencode_template, "my-opencode",
cpu_count=2,
memory_mb=2048,
on_build_logs=default_build_logger(),
)
```
Run the build script to create the template.
```bash JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
npx tsx build.ts
```
```bash Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
python build.py
```
## Related guides
Auto-pause, resume, and manage sandbox lifecycle
Clone repos, manage branches, and push changes
Connect to the sandbox via SSH for interactive sessions
# API key
Source: https://e2b.mintlify.app/docs/api-key
To use the API key, you can either:
* **Set the API key as the `E2B_API_KEY` environment variable** to avoid passing it each time you create a sandbox.
* Or pass it directly to the `Sandbox` constructor as shown below:
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const sandbox = await Sandbox.create({ apiKey: 'YOUR_API_KEY' })
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
sbx = Sandbox.create(api_key="YOUR_API_KEY")
```
## Where to find API key
You can get your API key at [dashboard](https://e2b.dev/dashboard?tab=keys).
# Access token
The access token is used only in the CLI and is **not needed in the SDK**. There's no need to set it when logging into the CLI using `e2b auth login`.
To authenticate without the browser, you can set `E2B_ACCESS_TOKEN` as an environment variable. This is useful for CI/CD pipelines.
## Where to find access token
You can get your **Access token key** at the [dashboard](https://e2b.dev/dashboard/account).
# Billing & limits
Source: https://e2b.mintlify.app/docs/billing
E2B uses [usage-based pricing](#usage-based-pricing) - you pay only for what you use. New users receive \$100 in free credits to get started.
[Manage billing in dashboard](https://e2b.dev/dashboard?tab=billing)
## Plans
| Feature | Hobby | Pro | Enterprise |
| -------------------------------------------- | ---------------- | ------------------------------ | ---------- |
| **Base price** | \$0/month | \$150/month | Custom |
| **Free credits** | \$100 (one-time) | \$100 (one-time) | Custom |
| **Max vCPUs** | 8 | 8+ | Custom |
| **Max memory** | 8 GB | 8+ GB | Custom |
| **Disk size** | 10 GB | 20+ GB | Custom |
| **Max session length** | 1 hour | 24 hours | Custom |
| **Concurrent sandboxes** | 20 | 100 - 1,100 | 1,100+ |
| **Concurrent builds** | 20 | 20 | Custom |
| **Sandbox creation rate** | 1 / sec | 5 / sec | Custom |
To upgrade your plan or purchase add-ons, visit the [dashboard billing tab](https://e2b.dev/dashboard?tab=billing). For Enterprise plans, [contact sales](mailto:enterprise@e2b.dev).
***
## Usage-based pricing
You pay per second for compute resources while your sandbox is running.
### Compute costs
Use the [usage cost calculator](https://e2b.dev/pricing#:~:text=Usage%20Cost%20Calculator) on our pricing page to estimate costs for your specific configuration.
### Customizing compute resources
You can customize allocated CPU and RAM when building custom templates by specifying `cpuCount` and `memoryMB` in the build configuration.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Template, defaultBuildLogger } from 'e2b'
await Template.build(template, 'my-template', {
cpuCount: 8,
memoryMB: 4096,
onBuildLogs: defaultBuildLogger(),
})
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Template, default_build_logger
Template.build(
template,
'my-template',
cpu_count=8,
memory_mb=4096,
on_build_logs=default_build_logger(),
)
```
See [template quickstart](/docs/template/quickstart) for more details on building custom templates.
Need higher CPU or RAM limits? Contact [support@e2b.dev](mailto:support@e2b.dev) for more information.
***
## Monitoring usage
Check your usage and costs in the [dashboard usage tab](https://e2b.dev/dashboard?tab=usage).
***
## FAQ
Automatically at the start of the month for the previous month's usage.
Your account will be blocked. Add a payment method to continue using E2B.
Yes, you can set spending limits on the [budget page](https://e2b.dev/dashboard?tab=budget) in your dashboard.
* **Enable auto-pause** - Automatically pause sandboxes after a period of inactivity to stop billing while preserving state
* **Pause sandboxes when idle** - Use `sbx.pause()` to stop billing while keeping state available for later
* **Kill sandboxes you no longer need** - Use `sbx.kill()` to stop billing and release resources permanently
* **Allocate only what you need** - Start with default resources (2 vCPU, 1 GB RAM) and increase only if necessary
* **Monitor actively running sandboxes** - Use the [CLI](/docs/cli/list-sandboxes) or [dashboard](https://e2b.dev/dashboard?tab=usage) to track active sandboxes
* **Use lifecycle events** - Set up [webhooks](/docs/sandbox/lifecycle-events-webhooks) to get notified when sandboxes are created
No. You only pay while a sandbox is actively running. Once a sandbox is paused, killed or times out, billing stops immediately.
# E2B CLI
Source: https://e2b.mintlify.app/docs/cli
E2B CLI is a command line tool that allows you to manage and interact with sandboxes and [templates](/docs/template/quickstart).
## Installation
**Using Homebrew (on macOS)**
```bash Terminal theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
brew install e2b
```
**Using NPM**
You can install E2B CLI using the following command:
```bash Terminal theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
npm i -g @e2b/cli
```
# Authentication in CLI
Source: https://e2b.mintlify.app/docs/cli/auth
There are two ways to authenticate with the E2B CLI:
## Option 1: browser authentication
This option requires an interactive browser environment.
Run the following command to sign in through your browser:
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
e2b auth login
```
This will open your default browser and prompt you to authenticate with your E2B account.
## Option 2: setup environment variables
Set your environment variables:
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
export E2B_API_KEY=your_api_key_here
export E2B_ACCESS_TOKEN=your_acces_token_here
```
Learn more about obtaining and managing your API key and access token on the [API Key page](/docs/api-key).
# Connect to sandbox
Source: https://e2b.mintlify.app/docs/cli/connect-to-sandbox
You can connect an interactive terminal to an already running sandbox.
```bash Terminal theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
e2b sandbox connect
```
Unlike the `create` command, `connect` does not kill the sandbox when you disconnect. When you exit the terminal, only your terminal session is closed—the sandbox continues running.
Once connected, you can inspect the sandbox filesystem and processes to debug or experiment, or use it as a dedicated environment for running agents instead of your local computer.
# Create sandbox
Source: https://e2b.mintlify.app/docs/cli/create-sandbox
You can create a sandbox and connect an interactive terminal to it.
```bash Terminal theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
e2b sandbox create
```
For example, to create a sandbox from the `base` template:
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
e2b sandbox create base
```
This will:
1. Create a new sandbox from the specified template
2. Connect your terminal to the sandbox
3. Keep the sandbox alive while you're connected
4. Automatically kill the sandbox when you exit the terminal
Once connected, you can inspect the sandbox filesystem and processes to debug or experiment, or use it as a dedicated environment for running agents instead of your local computer.
# Execute commands in sandbox
Source: https://e2b.mintlify.app/docs/cli/exec-command
You can execute commands in a running sandbox.
```bash Terminal theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
e2b sandbox exec
```
### Pipe command from stdin
You can pipe directly into the sandbox as well:
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
echo "foo" | e2b sandbox exec
```
### Run in background
Use the `--background` flag to run a command in the background and return immediately. The command will print the process ID (PID) to stderr:
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
e2b sandbox exec --background "sleep 60 && echo done"
```
### Set working directory
Use the `--cwd` flag to specify the working directory for the command:
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
e2b sandbox exec --cwd /home/user ls
```
### Run as specific user
Use the `--user` flag to run the command as a specific user:
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
e2b sandbox exec --user root apt-get update
```
### Set environment variables
Use the `--env` flag to set environment variables. This flag can be repeated for multiple variables:
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
e2b sandbox exec --env NODE_ENV=production --env DEBUG=true node app.js
```
# List sandboxes
Source: https://e2b.mintlify.app/docs/cli/list-sandboxes
You can list all sandboxes using the following command:
```bash Terminal theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
e2b sandbox list
```
This will return running sandboxes, you can specify `--state` to get paused or both.
### Filter by state
To filter the sandboxes by their state you can specify the `--state` flag, which can either be "**running**", "**paused**" or both.
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
e2b sandbox list --state running,paused
```
### Filter by metadata
To filter the sandboxes by their metadata, use the `--metadata` flag.
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
e2b sandbox list --metadata key1=value1,key2=value2
```
### List limit
To limit the amount of sandboxes returned by the command, use the `--limit` flag.
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
e2b sandbox list --limit 10
```
By default, the command will return all sandboxes.
### Output format
To output the sandboxes in JSON format, use the `--format` flag.
**Pretty print (default)**
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
e2b sandbox list --format pretty
```
**JSON**
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
e2b sandbox list --format json
```
# Shutdown running sandboxes
Source: https://e2b.mintlify.app/docs/cli/shutdown-sandboxes
You can shutdown single or all running sandboxes with the E2B CLI.
## Shutdown single or multiple sandboxes
To shutdown a single or multiple sandboxes, run the following command:
```bash Terminal theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
e2b sandbox kill
```
## Shutdown all sandboxes
To shutdown all running sandboxes, run the following command:
```bash Terminal theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
e2b sandbox kill --all
```
Further, you can filter the sandboxes to be shutdown by state, metadata or both.
```bash Terminal theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
e2b sandbox kill --all --state=running,paused
```
```bash Terminal theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
e2b sandbox kill --all --metadata=key=value
```
```bash Terminal theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
e2b sandbox kill --all --state=running,paused --metadata=key=value
```
# Analyze data with AI
Source: https://e2b.mintlify.app/docs/code-interpreting/analyze-data-with-ai
You can use E2B Sandbox to run AI-generated code to analyze data. Here's how the AI data analysis workflow usually looks like:
1. Your user has a dataset in CSV format or other formats.
2. You prompt the LLM to generate code (usually Python) based on the user's data.
3. The sandbox runs the AI-generated code and returns the results.
4. You display the results to the user.
***
## Example: Analyze CSV file with E2B and Claude 3.5 Sonnet
This short example will show you how to use E2B Sandbox to run AI-generated code to analyze CSV data.
### Table of Contents
1. [Install dependencies](#1-install-dependencies)
2. [Set your API keys](#2-set-your-api-keys)
3. [Download example CSV file](#3-download-example-csv-file)
4. [Initialize the sandbox and upload the dataset to the sandbox](#4-initialize-the-sandbox-and-upload-the-dataset-to-the-sandbox)
5. [Prepare the method for running AI-generated code](#5-prepare-the-method-for-running-ai-generated-code)
6. [Prepare the prompt and initialize Anthropic client](#6-prepare-the-prompt-and-initialize-anthropic-client)
7. [Connect the sandbox to the LLM with tool calling](#7-connect-the-sandbox-to-the-llm-with-tool-calling)
8. [Parse the LLM response and run the AI-generated code in the sandbox](#8-parse-the-llm-response-and-run-the-ai-generated-code-in-the-sandbox)
9. [Save the generated chart](#9-save-the-generated-chart)
10. [Run the code](#10-run-the-code)
11. [Full final code](#full-final-code)
### 1. Install dependencies
Install the E2B SDK and Claude SDK to your project by running the following command in your terminal.
```bash JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
npm i @e2b/code-interpreter @anthropic-ai/sdk dotenv
```
```bash Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
pip install e2b-code-interpreter anthropic python-dotenv
```
### 2. Set your API keys
1. Get your E2B API key from [E2B Dashboard](https://e2b.dev/dashboard?tab=keys).
2. Get your Claude API key from [Claude API Dashboard](https://console.anthropic.com/settings/keys).
3. Paste the keys into your `.env` file.
```bash .env theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
E2B_API_KEY=e2b_***
ANTHROPIC_API_KEY=sk-ant-***
```
### 3. Download example CSV file
We'll be using the publicly available [dataset of about 10,000 movies](https://www.kaggle.com/datasets/muqarrishzaib/tmdb-10000-movies-dataset).
1. Click the "Download" button at the top of the page.
2. Select "Download as zip (2 MB)".
3. Unzip the file and you should see `dataset.csv` file. Move it to the root of your project.
### 4. Initialize the sandbox and upload the dataset to the sandbox
We'll upload the dataset from the third step to the sandbox and save it as `dataset.csv` file.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import 'dotenv/config'
import fs from 'fs'
import { Sandbox } from '@e2b/code-interpreter'
// Create sandbox
const sbx = await Sandbox.create()
// Upload the dataset to the sandbox
const content = fs.readFileSync('dataset.csv')
const datasetPathInSandbox = await sbx.files.write('dataset.csv', content)
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from dotenv import load_dotenv
load_dotenv()
from e2b_code_interpreter import Sandbox
# Create sandbox
sbx = Sandbox.create()
# Upload the dataset to the sandbox
dataset_path_in_sandbox = ""
with open("dataset.csv", "rb") as f:
dataset_path_in_sandbox = sbx.files.write("dataset.csv", f)
```
### 5. Prepare the method for running AI-generated code
Add the following code to the file. Here we're adding the method for code execution.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
// ... code from the previous step
async function runAIGeneratedCode(aiGeneratedCode: string) {
console.log('Running the code in the sandbox....')
const execution = await sbx.runCode(aiGeneratedCode)
console.log('Code execution finished!')
console.log(execution)
}
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
# ... code from the previous step
def run_ai_generated_code(ai_generated_code: str):
print('Running the code in the sandbox....')
execution = sbx.run_code(ai_generated_code)
print('Code execution finished!')
print(execution)
```
### 6. Prepare the prompt and initialize Anthropic client
The prompt we'll be using describes the dataset and the analysis we want to perform like this:
1. Describe the columns in the CSV dataset.
2. Ask the LLM what we want to analyze - here we want to analyze the vote average over time. We're asking for a chart as the output.
3. Instruct the LLM to generate Python code for the data analysis.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import Anthropic from '@anthropic-ai/sdk'
const prompt = `
I have a CSV file about movies. It has about 10k rows. It's saved in the sandbox at ${dataset_path_in_sandbox.path}.
These are the columns:
- 'id': number, id of the movie
- 'original_language': string like "eng", "es", "ko", etc
- 'original_title': string that's name of the movie in the original language
- 'overview': string about the movie
- 'popularity': float, from 0 to 9137.939. It's not normalized at all and there are outliers
- 'release_date': date in the format yyyy-mm-dd
- 'title': string that's the name of the movie in english
- 'vote_average': float number between 0 and 10 that's representing viewers voting average
- 'vote_count': int for how many viewers voted
Write Python code that creates a line chart showing how vote_average changed over the years.
Do NOT print or explore the data. Just create the visualization directly.
CRITICAL: Your code MUST end with this exact line to display the plot:
display(plt.gcf())`
const anthropic = new Anthropic()
console.log('Waiting for the model response...')
const msg = await anthropic.messages.create({
model: 'claude-haiku-4-5-20251001',
max_tokens: 1024,
messages: [{ role: 'user', content: prompt }],
})
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from anthropic import Anthropic
prompt = f'''
I have a CSV file about movies. It has about 10k rows. It's saved in the sandbox at {dataset_path_in_sandbox.path}.
These are the columns:
- 'id': number, id of the movie
- 'original_language': string like "eng", "es", "ko", etc
- 'original_title': string that's name of the movie in the original language
- 'overview': string about the movie
- 'popularity': float, from 0 to 9137.939. It's not normalized at all and there are outliers
- 'release_date': date in the format yyyy-mm-dd
- 'title': string that's the name of the movie in english
- 'vote_average': float number between 0 and 10 that's representing viewers voting average
- 'vote_count': int for how many viewers voted
Write Python code that creates a line chart showing how vote_average changed over the years.
Do NOT print or explore the data. Just create the visualization directly.
CRITICAL: Your code MUST end with this exact line to display the plot:
display(plt.gcf())'''
anthropic = Anthropic()
msg = anthropic.messages.create(
model='claude-haiku-4-5-20251001',
max_tokens=1024,
messages=[
{"role": "user", "content": prompt}
]
)
```
### 7. Connect the sandbox to the LLM with tool calling
We'll use Claude's ability to [use tools (function calling)](https://docs.anthropic.com/en/docs/build-with-claude/tool-use) to run the code in the sandbox.
The way we'll do it is by connecting the method for running AI-generated code we created in the previous step to the Claude model.
Update the initialization of the Anthropic client to include the tool use like this:
```js JavaScript & TypeScript highlight={5-20} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
const msg = await anthropic.messages.create({
model: 'claude-haiku-4-5-20251001',
max_tokens: 1024,
messages: [{ role: 'user', content: prompt }],
tools: [
{
name: 'run_python_code',
description: 'Run Python code',
input_schema: {
type: 'object',
properties: {
code: {
type: 'string',
description: 'The Python code to run',
},
},
required: ['code'],
},
},
],
})
```
```python Python highlight={7-19} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
msg = anthropic.messages.create(
model='claude-haiku-4-5-20251001',
max_tokens=1024,
messages=[
{"role": "user", "content": prompt}
],
tools=[
{
"name": "run_python_code",
"description": "Run Python code",
"input_schema": {
"type": "object",
"properties": {
"code": { "type": "string", "description": "The Python code to run" },
},
"required": ["code"],
},
},
],
)
```
### 8. Parse the LLM response and run the AI-generated code in the sandbox
Now we'll parse the `msg` object to get the code from the LLM's response based on the tool we created in the previous step.
Once we have the code, we'll pass it to the `runAIGeneratedCode` method in JavaScript or `run_ai_generated_code` method in Python we created in the previous step to run the code in the sandbox.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
// ... code from the previous steps
interface CodeRunToolInput {
code: string
}
for (const contentBlock of msg.content) {
if (contentBlock.type === 'tool_use') {
if (contentBlock.name === 'run_python_code') {
const code = (contentBlock.input as CodeRunToolInput).code
console.log('Will run following code in the sandbox', code)
// Execute the code in the sandbox
await runAIGeneratedCode(code)
}
}
}
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
for content_block in msg.content:
if content_block.type == 'tool_use':
if content_block.name == 'run_python_code':
code = content_block.input['code']
print('Will run following code in the sandbox', code)
# Execute the code in the sandbox
run_ai_generated_code(code)
```
### 9. Save the generated chart
When running code in the sandbox for data analysis, you can get different types of results.
Including stdout, stderr, charts, tables, text, runtime errors, and more.
In this example we're specifically asking for a chart so we'll be looking for the chart in the results.
Let's update the `runAIGeneratedCode` method in JavaScript and `run_ai_generated_code` method in Python to check for the chart in the results and save it to the file.
```js JavaScript & TypeScript highlight={7-13,16-18,21-25} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
async function runAIGeneratedCode(aiGeneratedCode: string) {
console.log('Running the code in the sandbox....')
const execution = await sbx.runCode(aiGeneratedCode)
console.log('Code execution finished!')
// First let's check if the code ran successfully.
if (execution.error) {
console.error('AI-generated code had an error.')
console.log(execution.error.name)
console.log(execution.error.value)
console.log(execution.error.traceback)
process.exit(1)
}
// Iterate over all the results and specifically check for png files that will represent the chart.
let resultIdx = 0
for (const result of execution.results) {
if (result.png) {
// Save the png to a file
// The png is in base64 format.
fs.writeFileSync(`chart-${resultIdx}.png`, result.png, { encoding: 'base64' })
console.log(`Chart saved to chart-${resultIdx}.png`)
resultIdx++
}
}
}
```
```python Python highlight={1-2,7-12,15-17,20-23} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import sys
import base64
def run_ai_generated_code(ai_generated_code: str):
print('Running the code in the sandbox....')
execution = sbx.run_code(ai_generated_code)
print('Code execution finished!')
# First let's check if the code ran successfully.
if execution.error:
print('AI-generated code had an error.')
print(execution.error.name)
print(execution.error.value)
print(execution.error.traceback)
sys.exit(1)
# Iterate over all the results and specifically check for png files that will represent the chart.
result_idx = 0
for result in execution.results:
if result.png:
# Save the png to a file
# The png is in base64 format.
with open(f'chart-{result_idx}.png', 'wb') as f:
f.write(base64.b64decode(result.png))
print(f'Chart saved to chart-{result_idx}.png')
result_idx += 1
```
### 10. Run the code
Now you can run the whole code to see the results.
```bash JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
npx tsx index.ts
```
```bash Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
python main.py
```
You should see the chart in the root of your project that will look similar to this:
### Full final code
Check the full code in JavaScript and Python below:
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import 'dotenv/config'
import fs from 'fs'
import Anthropic from '@anthropic-ai/sdk'
import { Sandbox } from '@e2b/code-interpreter'
// Create sandbox
const sbx = await Sandbox.create()
// Upload the dataset to the sandbox
const content = fs.readFileSync('dataset.csv')
const datasetPathInSandbox = await sbx.files.write('/home/user/dataset.csv', content)
async function runAIGeneratedCode(aiGeneratedCode: string) {
const execution = await sbx.runCode(aiGeneratedCode)
if (execution.error) {
console.error('AI-generated code had an error.')
console.log(execution.error.name)
console.log(execution.error.value)
console.log(execution.error.traceback)
process.exit(1)
}
// Iterate over all the results and specifically check for png files that will represent the chart.
let resultIdx = 0
for (const result of execution.results) {
if (result.png) {
// Save the png to a file
// The png is in base64 format.
fs.writeFileSync(`chart-${resultIdx}.png`, result.png, { encoding: 'base64' })
console.log(`Chart saved to chart-${resultIdx}.png`)
resultIdx++
}
}
}
const prompt = `
I have a CSV file about movies. It has about 10k rows. It's saved in the sandbox at ${dataset_path_in_sandbox.path}.
These are the columns:
- 'id': number, id of the movie
- 'original_language': string like "eng", "es", "ko", etc
- 'original_title': string that's name of the movie in the original language
- 'overview': string about the movie
- 'popularity': float, from 0 to 9137.939. It's not normalized at all and there are outliers
- 'release_date': date in the format yyyy-mm-dd
- 'title': string that's the name of the movie in english
- 'vote_average': float number between 0 and 10 that's representing viewers voting average
- 'vote_count': int for how many viewers voted
Write Python code that creates a line chart showing how vote_average changed over the years.
Do NOT print or explore the data. Just create the visualization directly.
CRITICAL: Your code MUST end with this exact line to display the plot:
display(plt.gcf())`
const anthropic = new Anthropic()
console.log('Waiting for the model response...')
const msg = await anthropic.messages.create({
model: 'claude-haiku-4-5-20251001',
max_tokens: 1024,
messages: [{ role: 'user', content: prompt }],
tools: [
{
name: 'run_python_code',
description: 'Run Python code',
input_schema: {
type: 'object',
properties: {
code: {
type: 'string',
description: 'The Python code to run',
},
},
required: ['code'],
},
},
],
})
interface CodeRunToolInput {
code: string
}
for (const contentBlock of msg.content) {
if (contentBlock.type === 'tool_use') {
if (contentBlock.name === 'run_python_code') {
const code = (contentBlock.input as CodeRunToolInput).code
console.log('Will run following code in the sandbox', code)
// Execute the code in the sandbox
await runAIGeneratedCode(code)
}
}
}
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import sys
import base64
from dotenv import load_dotenv
load_dotenv()
from e2b_code_interpreter import Sandbox
from anthropic import Anthropic
# Create sandbox
sbx = Sandbox.create()
# Upload the dataset to the sandbox
with open("../dataset.csv", "rb") as f:
dataset_path_in_sandbox = sbx.files.write("dataset.csv", f)
def run_ai_generated_code(ai_generated_code: str):
print('Running the code in the sandbox....')
execution = sbx.run_code(ai_generated_code)
print('Code execution finished!')
# First let's check if the code ran successfully.
if execution.error:
print('AI-generated code had an error.')
print(execution.error.name)
print(execution.error.value)
print(execution.error.traceback)
sys.exit(1)
# Iterate over all the results and specifically check for png files that will represent the chart.
result_idx = 0
for result in execution.results:
if result.png:
# Save the png to a file
# The png is in base64 format.
with open(f'chart-{result_idx}.png', 'wb') as f:
f.write(base64.b64decode(result.png))
print(f'Chart saved to chart-{result_idx}.png')
result_idx += 1
prompt = f"""
I have a CSV file about movies. It has about 10k rows. It's saved in the sandbox at {dataset_path_in_sandbox.path}.
These are the columns:
- 'id': number, id of the movie
- 'original_language': string like "eng", "es", "ko", etc
- 'original_title': string that's name of the movie in the original language
- 'overview': string about the movie
- 'popularity': float, from 0 to 9137.939. It's not normalized at all and there are outliers
- 'release_date': date in the format yyyy-mm-dd
- 'title': string that's the name of the movie in english
- 'vote_average': float number between 0 and 10 that's representing viewers voting average
- 'vote_count': int for how many viewers voted
Write Python code that creates a line chart showing how vote_average changed over the years.
Do NOT print or explore the data. Just create the visualization directly.
CRITICAL: Your code MUST end with this exact line to display the plot:
display(plt.gcf())"""
anthropic = Anthropic()
print("Waiting for model response...")
msg = anthropic.messages.create(
model='claude-haiku-4-5-20251001',
max_tokens=1024,
messages=[
{"role": "user", "content": prompt}
],
tools=[
{
"name": "run_python_code",
"description": "Run Python code",
"input_schema": {
"type": "object",
"properties": {
"code": { "type": "string", "description": "The Python code to run" },
},
"required": ["code"]
}
}
]
)
for content_block in msg.content:
if content_block.type == "tool_use":
if content_block.name == "run_python_code":
code = content_block.input["code"]
print("Will run following code in the sandbox", code)
# Execute the code in the sandbox
run_ai_generated_code(code)
```
# Pre-installed libraries
Source: https://e2b.mintlify.app/docs/code-interpreting/analyze-data-with-ai/pre-installed-libraries
The sandbox comes with a set of pre-installed Python libraries for data analysis but you can install additional packages
# Code contexts
Source: https://e2b.mintlify.app/docs/code-interpreting/contexts
Run code in different code execution contexts with E2B Code Interpreter SDK. This allows you to parallelize code execution by running code in different contexts at the same time.
By default the code is run in the Sandbox's default code execution context. You can change it by passing the `context` parameter to the `runCode()` method in JavaScript or `run_code()` in Python.
## Create a new Code Context
You can create a new code execution context by calling the `createCodeContext()` method in JavaScript or `create_code_context()` in Python and passing the context parameters.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from '@e2b/code-interpreter'
const sandbox = await Sandbox.create()
const context = await sandbox.createCodeContext({
cwd: '/home/user',
language: 'python',
requestTimeoutMs: 60_000,
})
const result = await sandbox.runCode('print("Hello, world!")', { context })
console.log(result)
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b_code_interpreter import Sandbox
sandbox = Sandbox.create()
context = sandbox.create_code_context(
cwd='/home/user',
language='python',
request_timeout=60_000,
)
result = sandbox.run_code('print("Hello, world!")', context=context)
print(result)
```
## List active Code Contexts
You can list active code execution contexts by calling the `listCodeContexts()` method in JavaScript or `list_code_contexts()` in Python. This will return a list of active code execution contexts.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from '@e2b/code-interpreter'
const sandbox = await Sandbox.create()
const contexts = await sandbox.listCodeContexts()
console.log(contexts)
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b_code_interpreter import Sandbox
sandbox = Sandbox.create()
contexts = sandbox.list_code_contexts()
print(contexts)
```
## Restart a Code Context
You can restart an active code execution context by calling the `restartCodeContext()` method in JavaScript or `restart_code_context()` in Python and passing the context object or context ID.
Restarting a context will clear its state and start a new code execution session in the same context.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from '@e2b/code-interpreter'
const sandbox = await Sandbox.create()
const context = await sandbox.createCodeContext({
cwd: '/home/user',
language: 'python',
requestTimeoutMs: 60_000,
})
// using context object
await sandbox.restartCodeContext(context)
// using context ID
await sandbox.restartCodeContext(context.contextId)
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b_code_interpreter import Sandbox
sandbox = Sandbox.create()
context = sandbox.create_code_context(
cwd='/home/user',
language='python',
request_timeout=60_000,
)
# using context object
restarted_context = sandbox.restart_code_context(context)
print(restarted_context)
# using context ID
restarted_context = sandbox.restart_code_context(context.contextId)
print(restarted_context)
```
## Remove a Code Context
You can remove an active code execution context by calling the `removeCodeContext()` method in JavaScript or `remove_code_context()` in Python and passing the context object or context ID.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from '@e2b/code-interpreter'
const sandbox = await Sandbox.create()
const context = await sandbox.createCodeContext({
cwd: '/home/user',
language: 'python',
requestTimeoutMs: 60_000,
})
// using context object
await sandbox.removeCodeContext(context)
// using context ID
await sandbox.removeCodeContext(context.contextId)
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b_code_interpreter import Sandbox
sandbox = Sandbox.create()
context = sandbox.create_code_context(
cwd='/home/user',
language='python',
request_timeout=60_000,
)
# using context object
sandbox.remove_code_context(context)
# using context ID
sandbox.remove_code_context(context.contextId)
```
# Create charts & visualizations
Source: https://e2b.mintlify.app/docs/code-interpreting/create-charts-visualizations
E2B Sandbox allows you to create charts and visualizations by executing Python code inside the sandbox with `runCode()` method in JavaScript and `run_code()` method in Python.
These charts and visualizations can be [static](/docs/code-interpreting/create-charts-visualizations/static-charts) or [interactive](/docs/code-interpreting/create-charts-visualizations/interactive-charts) plots.
# Interactive charts
Source: https://e2b.mintlify.app/docs/code-interpreting/create-charts-visualizations/interactive-charts
E2B also allows you to create interactive charts with custom styling.
E2B automatically detects charts when executing Python code with `runCode()` in JavaScript or `run_code()` in Python. The Python code must include Matplotlib charts.
When a chart is detected, E2B sends the data of the chart back to the client. You can access the chart in the `execution.results` array where each item is a `Result` object with the `chart` property.
Try out [AI Data Analyst](https://github.com/e2b-dev/ai-analyst/) - a Next.js app that uses E2B to create interactive charts.
Here's a simple example of bar chart:
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox, BarChart } from '@e2b/code-interpreter'
const code = `
import matplotlib.pyplot as plt
# Prepare data
authors = ['Author A', 'Author B', 'Author C', 'Author D']
sales = [100, 200, 300, 400]
# Create and customize the bar chart
plt.figure(figsize=(10, 6))
plt.bar(authors, sales, label='Books Sold', color='blue')
plt.xlabel('Authors')
plt.ylabel('Number of Books Sold')
plt.title('Book Sales by Authors')
# Display the chart
plt.tight_layout()
plt.show()
`
const sandbox = await Sandbox.create()
const result = await sandbox.runCode(code)
const chart = result.results[0].chart as BarChart
console.log('Type:', chart.type)
console.log('Title:', chart.title)
console.log('X Label:', chart.x_label)
console.log('Y Label:', chart.y_label)
console.log('X Unit:', chart.x_unit)
console.log('Y Unit:', chart.y_unit)
console.log('Elements:', chart.elements)
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b_code_interpreter import Sandbox
code = """
import matplotlib.pyplot as plt
# Prepare data
authors = ['Author A', 'Author B', 'Author C', 'Author D']
sales = [100, 200, 300, 400]
# Create and customize the bar char
plt.figure(figsize=(10, 6))
plt.bar(authors, sales, label='Books Sold', color='blue')
plt.xlabel('Authors')
plt.ylabel('Number of Books Sold')
plt.title('Book Sales by Authors')
# Display the chart
plt.tight_layout()
plt.show()
"""
sandbox = Sandbox.create()
execution = sandbox.run_code(code)
chart = execution.results[0].chart
print('Type:', chart.type)
print('Title:', chart.title)
print('X Label:', chart.x_label)
print('Y Label:', chart.y_label)
print('X Unit:', chart.x_unit)
print('Y Unit:', chart.y_unit)
print('Elements:')
for element in chart.elements:
print('\n Label:', element.label)
print(' Value:', element.value)
print(' Group:', element.group)
```
The code above will output the following:
```bash JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
Type: bar
Title: Book Sales by Authors
X Label: Authors
Y Label: Number of Books Sold
X Unit: null
Y Unit: null
Elements: [
{
label: "Author A",
group: "Books Sold",
value: 100,
}, {
label: "Author B",
group: "Books Sold",
value: 200,
}, {
label: "Author C",
group: "Books Sold",
value: 300,
}, {
label: "Author D",
group: "Books Sold",
value: 400,
}
]
```
```bash Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
Type: ChartType.BAR
Title: Book Sales by Authors
X Label: Authors
Y Label: Number of Books Sold
X Unit: None
Y Unit: None
Elements:
Label: Author A
Value: 100.0
Group: Books Sold
Label: Author B
Value: 200.0
Group: Books Sold
Label: Author C
Value: 300.0
Group: Books Sold
Label: Author D
Value: 400.0
Group: Books Sold
```
You can send this data to your frontend to create an interactive chart with your favorite charting library.
***
## Supported intertactive charts
The following charts are currently supported:
* Line chart
* Bar chart
* Scatter plot
* Pie chart
* Box and whisker plot
# Static charts
Source: https://e2b.mintlify.app/docs/code-interpreting/create-charts-visualizations/static-charts
Every time you run Python code with `runCode()` in JavaScript or `run_code()` method in Python, the code is executed in a headless Jupyter server inside the sandbox.
E2B automatically detects any plots created with Matplotlib and sends them back to the client as images encoded in the base64 format.
These images are directly accesible on the `result` items in the `execution.results` array.
Here's how to retrieve a static chart from the executed Python code that contains a Matplotlib plot.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from '@e2b/code-interpreter'
import fs from 'fs'
const codeToRun = `
import matplotlib.pyplot as plt
plt.plot([1, 2, 3, 4])
plt.ylabel('some numbers')
plt.show()
`
const sandbox = await Sandbox.create()
// Run the code inside the sandbox
const execution = await sandbox.runCode(codeToRun)
// There's only one result in this case - the plot displayed with `plt.show()`
const firstResult = execution.results[0]
if (firstResult.png) {
// Save the png to a file. The png is in base64 format.
fs.writeFileSync('chart.png', firstResult.png, { encoding: 'base64' })
console.log('Chart saved as chart.png')
}
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import base64
from e2b_code_interpreter import Sandbox
code_to_run = """
import matplotlib.pyplot as plt
plt.plot([1, 2, 3, 4])
plt.ylabel('some numbers')
plt.show()
"""
sandbox = Sandbox.create()
# Run the code inside the sandbox
execution = sandbox.run_code(code_to_run)
# There's only one result in this case - the plot displayed with `plt.show()`
first_result = execution.results[0]
if first_result.png:
# Save the png to a file. The png is in base64 format.
with open('chart.png', 'wb') as f:
f.write(base64.b64decode(first_result.png))
print('Chart saved as chart.png')
```
The code in the variable `codeToRun`/`code_to_run` will produce this following plot that we're saving as `chart.png` file.
# Streaming
Source: https://e2b.mintlify.app/docs/code-interpreting/streaming
E2B Code Interpreter SDK allows you to stream the output, and results when executing code in the sandbox.
## Stream `stdout` and `stderr`
When using the `runCode()` method in JavaScript or `run_code()` in Python you can pass `onStdout`/`on_stdout` and `onStderr`/`on_stderr` callbacks to handle the output.
```js JavaScript & TypeScript highlight={15-17} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from '@e2b/code-interpreter'
const codeToRun = `
import time
import sys
print("This goes first to stdout")
time.sleep(3)
print("This goes later to stderr", file=sys.stderr)
time.sleep(3)
print("This goes last")
`
const sandbox = await Sandbox.create()
sandbox.runCode(codeToRun, {
// Use `onError` to handle runtime code errors
onError: error => console.error('error:', error),
onStdout: data => console.log('stdout:', data),
onStderr: data => console.error('stderr:', data),
})
```
```python Python highlight={17-19} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b_code_interpreter import Sandbox
code_to_run = """
import time
import sys
print("This goes first to stdout")
time.sleep(3)
print("This goes later to stderr", file=sys.stderr)
time.sleep(3)
print("This goes last")
"""
sandbox = Sandbox.create()
sandbox.run_code(
code_to_run,
# Use `on_error` to handle runtime code errors
on_error=lambda error: print('error:', error),
on_stdout=lambda data: print('stdout:', data),
on_stderr=lambda data: print('stderr:', data),
)
```
The code above will print the following:
```javascript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
stdout: {
error: false,
line: "This goes first to stdout\n",
timestamp: 1729049666861000,
}
stderr: {
error: true,
line: "This goes later to stderr\n",
timestamp: 1729049669924000,
}
stdout: {
error: false,
line: "This goes last\n",
timestamp: 1729049672664000,
}
```
```bash Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
stdout: This goes first to stdout
stderr: This goes later to stderr
stdout: This goes last
```
## Stream `results`
When using the `runCode()` method in JavaScript or `run_code()` in Python you can pass `onResults`/`on_results` callback
to receive results from the sandbox like charts, tables, text, and more.
```js JavaScript & TypeScript highlight={20} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
const codeToRun = `
import matplotlib.pyplot as plt
# Prepare data
categories = ['Category A', 'Category B', 'Category C', 'Category D']
values = [10, 20, 15, 25]
# Create and customize the bar chart
plt.figure(figsize=(10, 6))
plt.bar(categories, values, color='green')
plt.xlabel('Categories')
plt.ylabel('Values')
plt.title('Values by Category')
# Display the chart
plt.show()
`
const sandbox = await Sandbox.create()
await sandbox.runCode(codeToRun, {
onResult: result => console.log('result:', result),
})
```
```python Python highlight={24} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b_code_interpreter import Sandbox
code_to_run = """
import matplotlib.pyplot as plt
# Prepare data
categories = ['Category A', 'Category B', 'Category C', 'Category D']
values = [10, 20, 15, 25]
# Create and customize the bar chart
plt.figure(figsize=(10, 6))
plt.bar(categories, values, color='green')
plt.xlabel('Categories')
plt.ylabel('Values')
plt.title('Values by Category')
# Display the chart
plt.show()
"""
sandbox = Sandbox.create()
sandbox.run_code(
code_to_run,
on_result=lambda result: print('result:', result),
)
```
# Supported languages
Source: https://e2b.mintlify.app/docs/code-interpreting/supported-languages
Typically you use Python to run AI-generated code for data analysis but you can use other languages as well.
Out of the box E2B Sandbox supports:
* [Python](/docs/code-interpreting/supported-languages/python)
* [JavaScript and TypeScript](/docs/code-interpreting/supported-languages/javascript)
* [R](/docs/code-interpreting/supported-languages/r)
* [Java](/docs/code-interpreting/supported-languages/java)
* [Bash](/docs/code-interpreting/supported-languages/bash)
You can use any custom language runtime by creating a [custom sandbox template](/docs/template/quickstart).
# Run bash code
Source: https://e2b.mintlify.app/docs/code-interpreting/supported-languages/bash
Use the `runCode`/`run_code` method to run bash code inside the sandbox.
You'll need to pass the `language` parameter with value `bash`.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from '@e2b/code-interpreter'
const sbx = await Sandbox.create()
const execution = await sbx.runCode('echo "Hello, world!"', { language: 'bash' })
console.log(execution)
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b_code_interpreter import Sandbox
sbx = Sandbox.create()
execution = sbx.run_code("echo 'Hello, world!'", language="bash")
print(execution)
```
# Run Java code
Source: https://e2b.mintlify.app/docs/code-interpreting/supported-languages/java
Use the `runCode`/`run_code` method to run Java code inside the sandbox.
You'll need to pass the `language` parameter with value `java`.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from '@e2b/code-interpreter'
const sbx = await Sandbox.create()
const execution = await sbx.runCode('System.out.println("Hello, world!");', { language: 'java' })
console.log(execution)
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b_code_interpreter import Sandbox
sbx = Sandbox.create()
execution = sbx.run_code('System.out.println("Hello, world!");', language="java")
print(execution)
```
# Run JavaScript and TypeScript code
Source: https://e2b.mintlify.app/docs/code-interpreting/supported-languages/javascript
Use the `runCode`/`run_code` method to run JavaScript and TypeScript code inside the sandbox.
You'll need to pass the `language` parameter with value `javascript` or `js` for JavaScript and `typescript` or `ts` for TypeScript.
The E2B Code Interpreter supports TypeScript, top-level await, ESM-style imports and automatic promises resolution.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from '@e2b/code-interpreter';
// Create a new sandbox
const sbx = await Sandbox.create();
// Install the axios package
await sbx.commands.run("npm install axios");
// Run the code
const execution = await sbx.runCode(`
import axios from "axios";
const url: string = "https://api.github.com/status";
const response = await axios.get(url);
response.data;
`,
{ language: "ts" }
);
console.log(execution);
// Execution {
// results: [],
// logs: {
// stdout: [ "{ message: 'GitHub lives! (2025-05-28 10:49:55 -0700) (1)' }\n" ],
// stderr: [],
// },
// error: undefined,
// executionCount: 1,
// text: [Getter],
// toJSON: [Function: toJSON],
// }
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b_code_interpreter import Sandbox
# Create a new sandbox
sbx = Sandbox.create()
# Install the axios package
sbx.commands.run("npm install axios")
# Run the code
execution = sbx.run_code("""
import axios from "axios";
const url: string = "https://api.github.com/status";
const response = await axios.get(url);
response.data;
""",
language="ts",
)
print(execution)
# Execution(
# Results: [
# Result({ message: 'GitHub lives! (2025-05-28 10:48:47 -0700) (1)' })
# ],
# Logs: Logs(stdout: [], stderr: []),
# Error: None
# )
```
# Run Python code
Source: https://e2b.mintlify.app/docs/code-interpreting/supported-languages/python
Use the `runCode`/`run_code` method to run Python code inside the sandbox.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from '@e2b/code-interpreter'
const sbx = await Sandbox.create()
const execution = await sbx.runCode('print("Hello, world!")')
console.log(execution)
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b_code_interpreter import Sandbox
sbx = Sandbox.create()
execution = sbx.run_code('print("Hello, world!")')
print(execution)
```
# Run R code
Source: https://e2b.mintlify.app/docs/code-interpreting/supported-languages/r
Use the `runCode`/`run_code` method to run R code inside the sandbox.
You'll need to pass the `language` parameter with value `r`.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from '@e2b/code-interpreter'
const sbx = await Sandbox.create()
const execution = await sbx.runCode('print("Hello, world!")', { language: 'r' })
console.log(execution)
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b_code_interpreter import Sandbox
sbx = Sandbox.create()
execution = sbx.run_code('print("Hello, world!")', language="r")
print(execution)
```
# Running commands in sandbox
Source: https://e2b.mintlify.app/docs/commands
You can run terminal commands inside the sandbox using the `commands.run()` method.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const sandbox = await Sandbox.create()
const result = await sandbox.commands.run('ls -l')
console.log(result)
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
sandbox = Sandbox.create()
result = sandbox.commands.run('ls -l')
print(result)
```
# Running commands in background
Source: https://e2b.mintlify.app/docs/commands/background
To run commands in background, pass the `background` option to the `commands.run()` method. This will return immediately and the command will continue to run in the sandbox.
You can then later kill the command using the `commands.kill()` method.
```js JavaScript & TypeScript highlight={7} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const sandbox = await Sandbox.create()
// Start the command in the background
const command = await sandbox.commands.run('echo hello; sleep 10; echo world', {
background: true,
onStdout: (data) => {
console.log(data)
},
})
// Kill the command
await command.kill()
```
```python Python highlight={6} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
sandbox = Sandbox.create()
# Start the command in the background
command = sandbox.commands.run('echo hello; sleep 10; echo world', background=True)
# Get stdout and stderr from the command running in the background.
# You can run this code in a separate thread or use command.wait() to wait for the command to finish.
for stdout, stderr, _ in command:
if stdout:
print(stdout)
if stderr:
print(stderr)
# Kill the command
command.kill()
```
# Streaming command output
Source: https://e2b.mintlify.app/docs/commands/streaming
To stream command output as it is being executed, pass the `onStdout`, `onStderr` callbacks to the `commands.run()` method in JavaScript
or the `on_stdout`, `on_stderr` callbacks to the `commands.run()` method in Python.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const sandbox = await Sandbox.create()
const result = await sandbox.commands.run('echo hello; sleep 1; echo world', {
onStdout: (data) => {
console.log(data)
},
onStderr: (data) => {
console.log(data)
},
})
console.log(result)
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
sandbox = Sandbox.create()
result = sandbox.commands.run('echo hello; sleep 1; echo world', on_stdout=lambda data: print(data), on_stderr=lambda data: print(data))
print(result)
```
# Cookbook
Source: https://e2b.mintlify.app/docs/cookbook
# Filesystem
Source: https://e2b.mintlify.app/docs/filesystem
Each E2B Sandbox has its own isolated filesystem. The [Hobby tier](https://e2b.dev/pricing) sandboxes come with 10 GB of the free disk space and [Pro tier](https://e2b.dev/pricing) sandboxes come with 20 GB.
With E2B SDK you can:
* [Read and write files to the sandbox.](/docs/filesystem/read-write)
* [Watch directory for changes.](/docs/filesystem/watch)
* [Upload data to the sandbox.](/docs/filesystem/upload)
* [Download data from the sandbox.](/docs/filesystem/download)
# Download data from sandbox
Source: https://e2b.mintlify.app/docs/filesystem/download
You can download data from the sandbox using the `files.read()` method.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import fs from 'fs'
import { Sandbox } from 'e2b'
const sandbox = await Sandbox.create()
// Read file from sandbox
const content = await sandbox.files.read('/path/in/sandbox')
// Write file to local filesystem
fs.writeFileSync('/local/path', content)
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
sandbox = Sandbox.create()
# Read file from sandbox
content = sandbox.files.read('/path/in/sandbox')
# Write file to local filesystem
with open('/local/path', 'w') as file:
file.write(content)
```
## Download with pre-signed URL
Sometimes, you may want to let users from unauthorized environments, like a browser, download files from the sandbox.
For this use case, you can use pre-signed URLs to let users download files securely.
All you need to do is create a sandbox with the `secure: true` option. A download URL will then be generated with a signature that allows only authorized users to access files.
You can optionally set an expiration time for the URL so that it will be valid only for a limited time.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import fs from 'fs'
import { Sandbox } from 'e2b'
// Start a secured sandbox (all operations must be authorized by default)
const sandbox = await Sandbox.create(template, { secure: true })
// Create a pre-signed URL for file download with a 10 second expiration
const publicUrl = await sandbox.downloadUrl(
'demo.txt', {
useSignatureExpiration: 10_000, // optional
},
)
// Download a file with a pre-signed URL (this can be used in any environment, such as a browser)
const res = await fetch(publicUrl)
const content = await res.text()
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
# Start a secured sandbox (all operations must be authorized by default)
sandbox = Sandbox.create(timeout=12_000, secure=True)
# Create a pre-signed URL for file download with a 10 second expiration
# The user only has to visit the URL to download the file, this also works in a browser.
signed_url = sbx.download_url(path="demo.txt", user="user", use_signature_expiration=10_000)
```
# Get information about a file or directory
Source: https://e2b.mintlify.app/docs/filesystem/info
You can get information about a file or directory using the `files.getInfo()` / `files.get_info()` methods. Information such as file name, type, and path is returned.
### Getting information about a file
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const sandbox = await Sandbox.create()
// Create a new file
await sandbox.files.write('test_file.txt', 'Hello, world!')
// Get information about the file
const info = await sandbox.files.getInfo('test_file.txt')
console.log(info)
// {
// name: 'test_file.txt',
// type: 'file',
// path: '/home/user/test_file.txt',
// size: 13,
// mode: 0o644,
// permissions: '-rw-r--r--',
// owner: 'user',
// group: 'user',
// modifiedTime: '2025-05-26T12:00:00.000Z',
// symlinkTarget: null
// }
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
sandbox = Sandbox.create()
# Create a new file
sandbox.files.write('test_file', 'Hello, world!')
# Get information about the file
info = sandbox.files.get_info('test_file')
print(info)
# EntryInfo(
# name='test_file.txt',
# type=,
# path='/home/user/test_file.txt',
# size=13,
# mode=0o644,
# permissions='-rw-r--r--',
# owner='user',
# group='user',
# modified_time='2025-05-26T12:00:00.000Z',
# symlink_target=None
# )
```
### Getting information about a directory
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const sandbox = await Sandbox.create()
// Create a new directory
await sandbox.files.makeDir('test_dir')
// Get information about the directory
const info = await sandbox.files.getInfo('test_dir')
console.log(info)
// {
// name: 'test_dir',
// type: 'dir',
// path: '/home/user/test_dir',
// size: 0,
// mode: 0o755,
// permissions: 'drwxr-xr-x',
// owner: 'user',
// group: 'user',
// modifiedTime: '2025-05-26T12:00:00.000Z',
// symlinkTarget: null
// }
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
sandbox = Sandbox.create()
# Create a new directory
sandbox.files.make_dir('test_dir')
# Get information about the directory
info = sandbox.files.get_info('test_dir')
print(info)
# EntryInfo(
# name='test_dir',
# type=,
# path='/home/user/test_dir',
# size=0,
# mode=0o755,
# permissions='drwxr-xr-x',
# owner='user',
# group='user',
# modified_time='2025-05-26T12:00:00.000Z',
# symlink_target=None
# )
```
# Read & write files
Source: https://e2b.mintlify.app/docs/filesystem/read-write
## Reading files
You can read files from the sandbox filesystem using the `files.read()` method.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const sandbox = await Sandbox.create()
const fileContent = await sandbox.files.read('/path/to/file')
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
sandbox = Sandbox.create()
file_content = sandbox.files.read('/path/to/file')
```
## Writing single files
You can write single files to the sandbox filesystem using the `files.write()` method.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const sandbox = await Sandbox.create()
await sandbox.files.write('/path/to/file', 'file content')
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
sandbox = Sandbox.create()
sandbox.files.write('/path/to/file', 'file content')
```
## Writing multiple files
You can also write multiple files to the sandbox.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const sandbox = await Sandbox.create()
await sandbox.files.write([
{ path: '/path/to/a', data: 'file content' },
{ path: '/another/path/to/b', data: 'file content' }
])
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
sandbox = Sandbox.create()
sandbox.files.write_files([
{ "path": "/path/to/a", "data": "file content" },
{ "path": "another/path/to/b", "data": "file content" }
])
```
# Upload data to sandbox
Source: https://e2b.mintlify.app/docs/filesystem/upload
You can upload data to the sandbox using the `files.write()` method.
## Upload single file
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import fs from 'fs'
import { Sandbox } from 'e2b'
const sandbox = await Sandbox.create()
// Read file from local filesystem
const content = fs.readFileSync('/local/path')
// Upload file to sandbox
await sandbox.files.write('/path/in/sandbox', content)
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
sandbox = Sandbox.create()
# Read file from local filesystem
with open("path/to/local/file", "rb") as file:
# Upload file to sandbox
sandbox.files.write("/path/in/sandbox", file)
```
## Upload with pre-signed URL
Sometimes, you may want to let users from unauthorized environments, like a browser, upload files to the sandbox.
For this use case, you can use pre-signed URLs to let users upload files securely.
All you need to do is create a sandbox with the `secure: true` option. An upload URL will then be generated with a signature that allows only authorized users to upload files.
You can optionally set an expiration time for the URL so that it will be valid only for a limited time.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
// Start a secured sandbox (all operations must be authorized by default)
const sandbox = await Sandbox.create(template, { secure: true })
// Create a pre-signed URL for file upload with a 10 second expiration
const publicUploadUrl = await sandbox.uploadUrl(
'demo.txt', {
useSignatureExpiration: 10_000, // optional
},
)
// Upload a file with a pre-signed URL (this can be used in any environment, such as a browser)
const form = new FormData()
form.append('file', 'file content')
await fetch(publicUploadUrl, { method: 'POST', body: form })
// File is now available in the sandbox and you can read it
const content = sandbox.files.read('/path/in/sandbox')
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
import requests
# Start a secured sandbox (all operations must be authorized by default)
sandbox = Sandbox.create(timeout=12_000, secure=True)
# Create a pre-signed URL for file upload with a 10 second expiration
signed_url = sandbox.upload_url(path="demo.txt", user="user", use_signature_expiration=10_000)
form_data = {"file":"file content"}
requests.post(signed_url, data=form_data)
# File is now available in the sandbox and you can read it
content = sandbox.files.read('/path/in/sandbox')
```
## Upload directory / multiple files
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const sandbox = await Sandbox.create()
// Read all files in the directory and store their paths and contents in an array
const readDirectoryFiles = (directoryPath) => {
// Read all files in the local directory
const files = fs.readdirSync(directoryPath);
// Map files to objects with path and data
const filesArray = files
.filter(file => {
const fullPath = path.join(directoryPath, file);
// Skip if it's a directory
return fs.statSync(fullPath).isFile();
})
.map(file => {
const filePath = path.join(directoryPath, file);
// Read the content of each file
return {
path: filePath,
data: fs.readFileSync(filePath, 'utf8')
};
});
return filesArray;
};
// Usage example
const files = readDirectoryFiles('/local/dir');
console.log(files);
// [
// { path: '/local/dir/file1.txt', data: 'File 1 contents...' },
// { path: '/local/dir/file2.txt', data: 'File 2 contents...' },
// ...
// ]
await sandbox.files.write(files)
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import os
from e2b import Sandbox
sandbox = Sandbox.create()
def read_directory_files(directory_path):
files = []
# Iterate through all files in the directory
for filename in os.listdir(directory_path):
file_path = os.path.join(directory_path, filename)
# Skip if it's a directory
if os.path.isfile(file_path):
# Read file contents in binary mode
with open(file_path, "rb") as file:
files.append({
'path': file_path,
'data': file.read()
})
return files
files = read_directory_files("/local/dir")
print(files)
# [
# {"path": "/local/dir/file1.txt", "data": "File 1 contents..." },
# { "path": "/local/dir/file2.txt", "data": "File 2 contents..." },
# ...
# ]
sandbox.files.write_files(files)
```
# Watch sandbox directory for changes
Source: https://e2b.mintlify.app/docs/filesystem/watch
You can watch a directory for changes using the `files.watchDir()` method in JavaScript and `files.watch_dir()` method in Python.
Since events are tracked asynchronously, their delivery may be delayed.
It's recommended not to collect or close watcher immediately after making a change.
```js JavaScript & TypeScript highlight={7-12} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox, FilesystemEventType } from 'e2b'
const sandbox = await Sandbox.create()
const dirname = '/home/user'
// Start watching directory for changes
const handle = await sandbox.files.watchDir(dirname, async (event) => {
console.log(event)
if (event.type === FilesystemEventType.WRITE) {
console.log(`wrote to file ${event.name}`)
}
})
// Trigger file write event
await sandbox.files.write(`${dirname}/my-file`, 'hello')
```
```python Python highlight={7,12-16} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox, FilesystemEventType
sandbox = Sandbox.create()
dirname = '/home/user'
# Watch directory for changes
handle = sandbox.files.watch_dir(dirname)
# Trigger file write event
sandbox.files.write(f"{dirname}/my-file", "hello")
# Retrieve the latest new events since the last `get_new_events()` call
events = handle.get_new_events()
for event in events:
print(event)
if event.type == FilesystemEventType.WRITE:
print(f"wrote to file {event.name}")
```
## Recursive watching
You can enable recursive watching using the parameter `recursive`.
When rapidly creating new folders (e.g., deeply nested path of folders), events other than `CREATE` might not be emitted. To avoid this behavior, create the required folder structure in advance.
```js JavaScript & TypeScript highlight={13,17} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox, FilesystemEventType } from 'e2b'
const sandbox = await Sandbox.create()
const dirname = '/home/user'
// Start watching directory for changes
const handle = await sandbox.files.watchDir(dirname, async (event) => {
console.log(event)
if (event.type === FilesystemEventType.WRITE) {
console.log(`wrote to file ${event.name}`)
}
}, {
recursive: true
})
// Trigger file write event
await sandbox.files.write(`${dirname}/my-folder/my-file`, 'hello')
```
```python Python highlight={7,9} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox, FilesystemEventType
sandbox = Sandbox.create()
dirname = '/home/user'
# Watch directory for changes
handle = sandbox.files.watch_dir(dirname, recursive=True)
# Trigger file write event
sandbox.files.write(f"{dirname}/my-folder/my-file", "hello")
# Retrieve the latest new events since the last `get_new_events()` call
events = handle.get_new_events()
for event in events:
print(event)
if event.type == FilesystemEventType.WRITE:
print(f"wrote to file {event.name}")
```
# Overview
Source: https://e2b.mintlify.app/docs/mcp
Connect to 200+ tools through the Model Context Protocol
E2B provides a batteries-included MCP gateway that runs inside sandboxes, giving you type-safe access to 200+ MCP tools from the [Docker MCP Catalog](https://hub.docker.com/mcp) or [custom MCPs](/docs/mcp/custom-servers) through a unified interface. This integration gives developers instant access to tools like [Browserbase](https://www.browserbase.com/), [Exa](https://exa.ai/), [Notion](https://www.notion.so/), [Stripe](https://stripe.com/), or [GitHub](https://github.com/).
The [Model Context Protocol (MCP)](https://modelcontextprotocol.io/docs/getting-started/intro) is an open standard for connecting AI models to external tools and data sources. E2B sandboxes provide an ideal environment for running MCP tools, giving AI full access to an internet-connected Linux machine where it can safely install packages, write files, run terminal commands, and AI-generated code.
```typescript TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import Sandbox from 'e2b'
const sbx = await Sandbox.create({
mcp: {
browserbase: {
apiKey: process.env.BROWSERBASE_API_KEY!,
geminiApiKey: process.env.GEMINI_API_KEY!,
projectId: process.env.BROWSERBASE_PROJECT_ID!,
},
exa: {
apiKey: process.env.EXA_API_KEY!,
},
airtable: {
airtableApiKey: process.env.AIRTABLE_API_KEY!,
},
},
});
const mcpUrl = sbx.getMcpUrl();
const mcpToken = await sbx.getMcpToken();
// You can now connect the gateway to any MCP client, for example claude:
// This also works for your local claude!
await sbx.commands.run(`claude mcp add --transport http e2b-mcp-gateway ${mcpUrl} --header "Authorization: Bearer ${mcpToken}"`, { timeoutMs: 0, onStdout: console.log, onStderr: console.log });
await sbx.commands.run(
`echo 'Use browserbase and exa to research open positions at e2b.dev. Collect your findings in Airtable.' | claude -p --dangerously-skip-permissions`,
{ timeoutMs: 0, onStdout: console.log, onStderr: console.log }
)
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import asyncio
from e2b import AsyncSandbox
import os
import dotenv
dotenv.load_dotenv()
async def main():
sbx = await AsyncSandbox.create(mcp={
"browserbase": {
"apiKey": os.getenv("BROWSERBASE_API_KEY"),
"geminiApiKey": os.getenv("GEMINI_API_KEY"),
"projectId": os.getenv("BROWSERBASE_PROJECT_ID"),
},
"exa": {
"apiKey": os.getenv("EXA_API_KEY"),
},
"airtable": {
"airtableApiKey": os.getenv("AIRTABLE_API_KEY"),
},
})
mcp_url = sbx.get_mcp_url()
mcp_token = await sbx.get_mcp_token()
# You can now connect the gateway to any MCP client, for example claude:
# This also works for your local claude!
await sbx.commands.run(f'claude mcp add --transport http e2b-mcp-gateway {mcp_url} --header "Authorization: Bearer {mcp_token}"', timeout=0, on_stdout=print, on_stderr=print)
await sbx.commands.run(
"echo 'Use browserbase and exa to research open positions at e2b.dev. Collect your findings in Airtable.' | claude -p --dangerously-skip-permissions",
timeout=0, on_stdout=print, on_stderr=print
)
if __name__ == "__main__":
asyncio.run(main())
```
## Documentation
Get started with MCP
Browse 200+ pre-built MCP servers
Prepull MCP servers for faster runtime
Use custom MCP servers from GitHub
See examples
# Available servers
Source: https://e2b.mintlify.app/docs/mcp/available-servers
Browse available MCP servers
E2B provides access to 200+ MCP servers from [Docker's catalog](https://hub.docker.com/mcp). You can also run [custom MCP servers](/docs/mcp/custom-servers) inside the sandbox.
## Airtable
Provides AI assistants with direct access to Airtable bases, allowing them to read schemas, query records, and interact with your Airtable data. Supports listing bases, retrieving table structures, and searching through records to help automate workflows and answer questions about your organized data.
[View on Docker Hub](https://hub.docker.com/mcp/server/airtable-mcp-server/overview)
## Azure Kubernetes Service (AKS)
Azure Kubernetes Service (AKS) official MCP server.
[View on Docker Hub](https://hub.docker.com/mcp/server/aks/overview)
Access level for the MCP server, One of \[ readonly, readwrite, admin ]
Comma-separated list of additional tools, One of \[ helm, cilium ]
Comma-separated list of namespaces to allow access to. If not specified, all namespaces are allowed.
Path to the Azure configuration directory (e.g. /home/azureuser/.azure). Used for Azure CLI authentication, you should be logged in (e.g. run `az login`) on the host before starting the MCP server.
Username or UID of the container user (format ``\[:``] e.g. 10000), ensuring correct permissions to access the Azure and kubeconfig files. Leave empty to use default user in the container.
Path to the kubeconfig file for the AKS cluster (e.g. /home/azureuser/.kube/config). Used to connect to the AKS cluster.
## Apify
Apify is the world's largest marketplace of tools for web scraping, data extraction, and web automation. You can extract structured data from social media, e-commerce, search engines, maps, travel sites, or any other website.
[View on Docker Hub](https://hub.docker.com/mcp/server/apify-mcp-server/overview)
Comma-separated list of tools to enable. Can be either a tool category, a specific tool, or an Apify Actor. For example: "actors,docs,apify/rag-web-browser". For more details visit [https://mcp.apify.com](https://mcp.apify.com).
## Api-gateway
A universal MCP (Model Context Protocol) server to integrate any API with Claude Desktop using only Docker configurations.
[View on Docker Hub](https://hub.docker.com/mcp/server/mcp-api-gateway/overview)
## ArXiv
The ArXiv MCP Server provides a comprehensive bridge between AI assistants and arXiv's research repository through the Model Context Protocol (MCP).
**Features:**
* Search arXiv papers with advanced filtering
* Download and store papers locally as markdown
* Read and analyze paper content
* Deep research analysis prompts
* Local paper management and storage
* Enhanced tool descriptions optimized for local AI models
* Docker MCP Gateway compatible with detailed context
[View on Docker Hub](https://hub.docker.com/mcp/server/arxiv-mcp-server/overview)
Directory path where downloaded papers will be stored
The ArXiv MCP Server provides a comprehensive bridge between AI assistants and arXiv's research repository through the Model Context Protocol (MCP). Features: • Search arXiv papers with advanced filtering • Download and store papers locally as markdown • Read and analyze paper content • Deep research analysis prompts • Local paper management and storage • Enhanced tool descriptions optimized for local AI models • Docker MCP Gateway compatible with detailed context Perfect for researchers, academics, and AI assistants conducting literature reviews and research analysis. **Recent Update**: Enhanced tool descriptions specifically designed to resolve local AI model confusion and improve Docker MCP Gateway compatibility.
## ast-grep
ast-grep is a fast and polyglot tool for code structural search, lint, rewriting at large scale.
[View on Docker Hub](https://hub.docker.com/mcp/server/ast-grep/overview)
## Astra DB
An MCP server for Astra DB workloads.
[View on Docker Hub](https://hub.docker.com/mcp/server/astra-db/overview)
## Astro Docs
Access the latest Astro web framework documentation, guides, and API references.
[View on Docker Hub](https://hub.docker.com/mcp/server/astro-docs/overview)
## Atlan
MCP server for interacting with Atlan services including asset search, updates, and lineage traversal for comprehensive data governance and discovery.
[View on Docker Hub](https://hub.docker.com/mcp/server/atlan/overview)
## Atlas Docs
Provide LLMs hosted, clean markdown documentation of libraries and frameworks.
[View on Docker Hub](https://hub.docker.com/mcp/server/atlas-docs/overview)
## Atlassian
Tools for Atlassian products (Confluence and Jira). This integration supports both Atlassian Cloud and Jira Server/Data Center deployments.
[View on Docker Hub](https://hub.docker.com/mcp/server/atlassian/overview)
## Audiense Insights
Audiense Insights MCP Server is a server based on the Model Context Protocol (MCP) that allows Claude and other MCP-compatible clients to interact with your Audiense Insights account.
[View on Docker Hub](https://hub.docker.com/mcp/server/audiense-insights/overview)
## AWS CDK
AWS Cloud Development Kit (CDK) best practices, infrastructure as code patterns, and security compliance with CDK Nag.
[View on Docker Hub](https://hub.docker.com/mcp/server/aws-cdk-mcp-server/overview)
## AWS Core
Starting point for using the awslabs MCP servers.
[View on Docker Hub](https://hub.docker.com/mcp/server/aws-core-mcp-server/overview)
## AWS Diagram
Seamlessly create diagrams using the Python diagrams package DSL. This server allows you to generate AWS diagrams, sequence diagrams, flow diagrams, and class diagrams using Python code.
[View on Docker Hub](https://hub.docker.com/mcp/server/aws-diagram/overview)
## AWS Documentation
Tools to access AWS documentation, search for content, and get recommendations.
[View on Docker Hub](https://hub.docker.com/mcp/server/aws-documentation/overview)
## AWS KB Retrieval (Archived)
An MCP server implementation for retrieving information from the AWS Knowledge Base using the Bedrock Agent Runtime.
[View on Docker Hub](https://hub.docker.com/mcp/server/aws-kb-retrieval-server/overview)
## AWS Terraform
Terraform on AWS best practices, infrastructure as code patterns, and security compliance with Checkov.
[View on Docker Hub](https://hub.docker.com/mcp/server/aws-terraform/overview)
## Azure
The Azure MCP Server, bringing the power of Azure to your agents.
[View on Docker Hub](https://hub.docker.com/mcp/server/azure/overview)
## Beagle security
Connects with the Beagle Security backend using a user token to manage applications, run automated security tests, track vulnerabilities across environments, and gain intelligence from Application and API vulnerability data.
[View on Docker Hub](https://hub.docker.com/mcp/server/beagle-security/overview)
## Bitrefill
A Model Context Protocol Server connector for Bitrefill public API, to enable AI agents to search and shop on Bitrefill.
[View on Docker Hub](https://hub.docker.com/mcp/server/bitrefill/overview)
## Box
An MCP server capable of interacting with the Box API.
[View on Docker Hub](https://hub.docker.com/mcp/server/box/overview)
## Brave Search
Search the Web for pages, images, news, videos, and more using the Brave Search API.
[View on Docker Hub](https://hub.docker.com/mcp/server/brave/overview)
## Browserbase
Allow LLMs to control a browser with Browserbase and Stagehand for AI-powered web automation, intelligent data extraction, and screenshot capture.
[View on Docker Hub](https://hub.docker.com/mcp/server/browserbase/overview)
## Buildkite
Buildkite MCP lets agents interact with Buildkite Builds, Jobs, Logs, Packages and Test Suites.
[View on Docker Hub](https://hub.docker.com/mcp/server/buildkite/overview)
## Camunda BPM process engine
Tools to interact with the Camunda 7 Community Edition Engine using the Model Context Protocol (MCP). Whether you're automating workflows, querying process instances, or integrating with external systems, Camunda MCP Server is your agentic solution for seamless interaction with Camunda.
[View on Docker Hub](https://hub.docker.com/mcp/server/camunda/overview)
## CData Connect Cloud
This fully functional MCP Server allows you to connect to any data source in Connect Cloud from Claude Desktop.
[View on Docker Hub](https://hub.docker.com/mcp/server/cdata-connectcloud/overview)
## CharmHealth
An MCP server for CharmHealth EHR that allows LLMs and MCP clients to interact with patient records, encounters, and practice information.
[View on Docker Hub](https://hub.docker.com/mcp/server/charmhealth-mcp-server/overview)
## Chroma
A Model Context Protocol (MCP) server implementation that provides database capabilities for Chroma.
[View on Docker Hub](https://hub.docker.com/mcp/server/chroma/overview)
## CircleCI
A specialized server implementation for the Model Context Protocol (MCP) designed to integrate with CircleCI's development workflow. This project serves as a bridge between CircleCI's infrastructure and the Model Context Protocol, enabling enhanced AI-powered development experiences.
[View on Docker Hub](https://hub.docker.com/mcp/server/circleci/overview)
## Official ClickHouse
Official ClickHouse MCP Server.
[View on Docker Hub](https://hub.docker.com/mcp/server/clickhouse/overview)
## Close
Streamline sales processes with integrated calling, email, SMS, and automated workflows for small and scaling businesses.
[View on Docker Hub](https://hub.docker.com/mcp/server/close/overview)
## Cloudflare Docs
Access the latest documentation on Cloudflare products such as Workers, Pages, R2, D1, KV.
[View on Docker Hub](https://hub.docker.com/mcp/server/cloudflare-docs/overview)
## Cloud Run MCP
MCP server to deploy apps to Cloud Run.
[View on Docker Hub](https://hub.docker.com/mcp/server/cloud-run-mcp/overview)
path to application-default credentials (eg \$HOME/.config/gcloud/application\_default\_credentials.json )
## CockroachDB
Enable AI agents to manage, monitor, and query CockroachDB using natural language. Perform complex database operations, cluster management, and query execution seamlessly through AI-driven workflows. Integrate effortlessly with MCP clients for scalable and high-performance data operations.
[View on Docker Hub](https://hub.docker.com/mcp/server/cockroachdb/overview)
## Python Interpreter
A Python-based execution tool that mimics a Jupyter notebook environment. It accepts code snippets, executes them, and maintains state across sessions — preserving variables, imports, and past results. Ideal for iterative development, debugging, or code execution.
[View on Docker Hub](https://hub.docker.com/mcp/server/mcp-code-interpreter/overview)
## Context7
Context7 MCP Server -- Up-to-date code documentation for LLMs and AI code editors.
[View on Docker Hub](https://hub.docker.com/mcp/server/context7/overview)
## Couchbase
Couchbase is a distributed document database with a powerful search engine and in-built operational and analytical capabilities.
[View on Docker Hub](https://hub.docker.com/mcp/server/couchbase/overview)
Bucket in the Couchbase cluster to use for the MCP server.
Connection string for the Couchbase cluster.
Setting to "true" (default) enables read-only query mode while running SQL++ queries.
Username for the Couchbase cluster with access to the bucket.
## The official for Cylera.
Brings context about device inventory, threats, risks and utilization powered by the Cylera Partner API into an LLM.
[View on Docker Hub](https://hub.docker.com/mcp/server/cylera-mcp-server/overview)
## Shodan
A Model Context Protocol server that provides access to Shodan API functionality.
[View on Docker Hub](https://hub.docker.com/mcp/server/cyreslab-ai-shodan/overview)
## Dappier
Enable fast, free real-time web search and access premium data from trusted media brands—news, financial markets, sports, entertainment, weather, and more. Build powerful AI agents with Dappier.
[View on Docker Hub](https://hub.docker.com/mcp/server/dappier/overview)
## Dappier Remote
Enable fast, free real-time web search and access premium data from trusted media brands—news, financial markets, sports, entertainment, weather, and more. Build powerful AI agents with Dappier.
[View on Docker Hub](https://hub.docker.com/mcp/server/dappier-remote/overview)
## Dart AI
Dart AI Model Context Protocol (MCP) server.
[View on Docker Hub](https://hub.docker.com/mcp/server/dart/overview)
## MCP Database Server
Comprehensive database server supporting PostgreSQL, MySQL, and SQLite with natural language SQL query capabilities. Enables AI agents to interact with databases through both direct SQL and natural language queries.
[View on Docker Hub](https://hub.docker.com/mcp/server/database-server/overview)
Connection string for your database. Examples: SQLite: sqlite+aiosqlite:///data/mydb.db, PostgreSQL: postgresql+asyncpg://user:password\@localhost:5432/mydb, MySQL: mysql+aiomysql://user:password\@localhost:3306/mydb
## Databutton
Databutton MCP Server.
[View on Docker Hub](https://hub.docker.com/mcp/server/databutton/overview)
## DeepWiki
Tools for fetching and asking questions about GitHub repositories.
[View on Docker Hub](https://hub.docker.com/mcp/server/deepwiki/overview)
## Descope
The Descope Model Context Protocol (MCP) server provides an interface to interact with Descope's Management APIs, enabling the search and retrieval of project-related information.
[View on Docker Hub](https://hub.docker.com/mcp/server/descope/overview)
## Desktop Commander
Search, update, manage files and run terminal commands with AI.
[View on Docker Hub](https://hub.docker.com/mcp/server/desktop-commander/overview)
List of directories that Desktop Commander can access
## DevHub CMS
DevHub CMS LLM integration through the Model Context Protocol.
[View on Docker Hub](https://hub.docker.com/mcp/server/devhub-cms/overview)
## Discord
Interact with the Discord platform.
[View on Docker Hub](https://hub.docker.com/mcp/server/mcp-discord/overview)
## Docker Hub
Docker Hub official MCP server.
[View on Docker Hub](https://hub.docker.com/mcp/server/dockerhub/overview)
## Dodo Payments
Tools for cross-border payments, taxes, and compliance.
[View on Docker Hub](https://hub.docker.com/mcp/server/dodo-payments/overview)
## DreamFactory
DreamFactory is a REST API generation platform with support for hundreds of data sources, including Microsoft SQL Server, MySQL, PostgreSQL, and MongoDB. The DreamFactory MCP Server makes it easy for users to securely interact with their data sources via an MCP client.
[View on Docker Hub](https://hub.docker.com/mcp/server/dreamfactory-mcp/overview)
## DuckDuckGo
A Model Context Protocol (MCP) server that provides web search capabilities through DuckDuckGo, with additional features for content fetching and parsing.
[View on Docker Hub](https://hub.docker.com/mcp/server/duckduckgo/overview)
## Dynatrace
This MCP Server allows interaction with the Dynatrace observability platform, brining real-time observability data directly into your development workflow.
[View on Docker Hub](https://hub.docker.com/mcp/server/dynatrace-mcp-server/overview)
## E2B
Giving Claude ability to run code with E2B via MCP (Model Context Protocol).
[View on Docker Hub](https://hub.docker.com/mcp/server/e2b/overview)
## EduBase
The EduBase MCP server enables Claude and other LLMs to interact with EduBase's comprehensive e-learning platform through the Model Context Protocol (MCP).
[View on Docker Hub](https://hub.docker.com/mcp/server/edubase/overview)
## Effect MCP
Tools and resources for writing Effect code in Typescript.
[View on Docker Hub](https://hub.docker.com/mcp/server/effect-mcp/overview)
## Elasticsearch
Interact with your Elasticsearch indices through natural language conversations.
[View on Docker Hub](https://hub.docker.com/mcp/server/elasticsearch/overview)
## Elevenlabs MCP
Official ElevenLabs Model Context Protocol (MCP) server that enables interaction with powerful Text to Speech and audio processing APIs.
[View on Docker Hub](https://hub.docker.com/mcp/server/elevenlabs/overview)
## EverArt (Archived)
Image generation server using EverArt's API.
[View on Docker Hub](https://hub.docker.com/mcp/server/everart/overview)
## Exa
Exa MCP for web search and web crawling!.
[View on Docker Hub](https://hub.docker.com/mcp/server/exa/overview)
## Explorium B2B Data
Discover companies, contacts, and business insights—powered by dozens of trusted external data sources.
[View on Docker Hub](https://hub.docker.com/mcp/server/explorium/overview)
## Fetch (Reference)
Fetches a URL from the internet and extracts its contents as markdown.
[View on Docker Hub](https://hub.docker.com/mcp/server/fetch/overview)
## Fibery
Interact with your Fibery workspace.
[View on Docker Hub](https://hub.docker.com/mcp/server/fibery/overview)
## Filesystem (Reference)
Local filesystem access with configurable allowed paths.
[View on Docker Hub](https://hub.docker.com/mcp/server/filesystem/overview)
## Find-A-Domain
Tools for finding domain names.
[View on Docker Hub](https://hub.docker.com/mcp/server/find-a-domain/overview)
## Firecrawl
🔥 Official Firecrawl MCP Server - Adds powerful web scraping and search to Cursor, Claude and any other LLM clients.
[View on Docker Hub](https://hub.docker.com/mcp/server/firecrawl/overview)
## Firewalla
Real-time network monitoring, security analysis, and firewall management through 28 specialized tools. Access security alerts, network flows, device status, and firewall rules directly from your Firewalla device.
[View on Docker Hub](https://hub.docker.com/mcp/server/firewalla-mcp-server/overview)
Your Firewalla Box Global ID
Your Firewalla MSP domain (e.g., yourdomain.firewalla.net)
## FlexPrice
Official flexprice MCP Server.
[View on Docker Hub](https://hub.docker.com/mcp/server/flexprice/overview)
## Git (Reference)
Git repository interaction and automation.
[View on Docker Hub](https://hub.docker.com/mcp/server/git/overview)
## GitHub (Archived)
Tools for interacting with the GitHub API, enabling file operations, repository management, search functionality, and more.
[View on Docker Hub](https://hub.docker.com/mcp/server/github/overview)
## GitHub Chat
A Model Context Protocol (MCP) for analyzing and querying GitHub repositories using the GitHub Chat API.
[View on Docker Hub](https://hub.docker.com/mcp/server/github-chat/overview)
## GitHub Official
Official GitHub MCP Server, by GitHub. Provides seamless integration with GitHub APIs, enabling advanced automation and interaction capabilities for developers and tools.
[View on Docker Hub](https://hub.docker.com/mcp/server/github-official/overview)
## GitLab (Archived)
MCP Server for the GitLab API, enabling project management, file operations, and more.
[View on Docker Hub](https://hub.docker.com/mcp/server/gitlab/overview)
api url - optional for self-hosted instances
## GitMCP
Tools for interacting with Git repositories.
[View on Docker Hub](https://hub.docker.com/mcp/server/gitmcp/overview)
## glif.app
Easily run glif.app AI workflows inside your LLM: image generators, memes, selfies, and more. Glif supports all major multimedia AI models inside one app.
[View on Docker Hub](https://hub.docker.com/mcp/server/glif/overview)
## Gmail
A Model Context Protocol server for Gmail operations using IMAP/SMTP with app password authentication. Supports listing messages, searching emails, and sending messages. To create your app password, visit your Google Account settings under Security > App Passwords. Or visit the link [https://myaccount.google.com/apppasswords](https://myaccount.google.com/apppasswords).
[View on Docker Hub](https://hub.docker.com/mcp/server/gmail-mcp/overview)
Your Gmail email address
## Google Maps (Archived)
Tools for interacting with the Google Maps API.
[View on Docker Hub](https://hub.docker.com/mcp/server/google-maps/overview)
## Google Maps Comprehensive MCP
Complete Google Maps integration with 8 tools including geocoding, places search, directions, elevation data, and more using Google's latest APIs.
[View on Docker Hub](https://hub.docker.com/mcp/server/google-maps-comprehensive/overview)
## Grafana
MCP server for Grafana.
[View on Docker Hub](https://hub.docker.com/mcp/server/grafana/overview)
## Gyazo
Official Model Context Protocol server for Gyazo.
[View on Docker Hub](https://hub.docker.com/mcp/server/gyazo/overview)
## Hackernews mcp
A Model Context Protocol (MCP) server that provides access to Hacker News stories, comments, and user data, with support for search and content retrieval.
[View on Docker Hub](https://hub.docker.com/mcp/server/mcp-hackernews/overview)
## Hackle
Model Context Protocol server for Hackle.
[View on Docker Hub](https://hub.docker.com/mcp/server/hackle/overview)
## Handwriting OCR
Model Context Protocol (MCP) Server for Handwriting OCR.
[View on Docker Hub](https://hub.docker.com/mcp/server/handwriting-ocr/overview)
## Humanitarian Data Exchange
HDX MCP Server provides access to humanitarian data through the Humanitarian Data Exchange (HDX) API - [https://data.humdata.org/hapi](https://data.humdata.org/hapi). This server offers 33 specialized tools for retrieving humanitarian information including affected populations (refugees, IDPs, returnees), baseline demographics, food security indicators, conflict data, funding information, and operational presence across hundreds of countries and territories. See repository for instructions on getting a free HDX\_APP\_INDENTIFIER for access.
[View on Docker Hub](https://hub.docker.com/mcp/server/hdx/overview)
## Heroku
Heroku Platform MCP Server using the Heroku CLI.
[View on Docker Hub](https://hub.docker.com/mcp/server/heroku/overview)
## Hostinger API
Interact with Hostinger services over the Hostinger API.
[View on Docker Hub](https://hub.docker.com/mcp/server/hostinger-mcp-server/overview)
## Hoverfly
A Model Context Protocol (MCP) server that exposes Hoverfly as a programmable tool for AI assistants like Cursor, Claude, GitHub Copilot, and others supporting MCP. It enables dynamic mocking of third-party APIs to unblock development, automate testing, and simulate unavailable services during integration.
[View on Docker Hub](https://hub.docker.com/mcp/server/hoverfly-mcp-server/overview)
## HubSpot
Unite marketing, sales, and customer service with AI-powered automation, lead management, and comprehensive analytics.
[View on Docker Hub](https://hub.docker.com/mcp/server/hubspot/overview)
## Hugging Face
Tools for interacting with Hugging Face models, datasets, research papers, and more.
[View on Docker Hub](https://hub.docker.com/mcp/server/hugging-face/overview)
## Hummingbot MCP: Trading Agent
Hummingbot MCP is an open-source toolset that lets you control and monitor your Hummingbot trading bots through AI-powered commands and automation.
[View on Docker Hub](https://hub.docker.com/mcp/server/hummingbot-mcp/overview)
## Husqvarna Automower
MCP Server for huqsvarna automower.
[View on Docker Hub](https://hub.docker.com/mcp/server/husqvarna-automower/overview)
## Hyperbrowser
A MCP server implementation for hyperbrowser.
[View on Docker Hub](https://hub.docker.com/mcp/server/hyperbrowser/overview)
## Hyperspell
Hyperspell MCP Server.
[View on Docker Hub](https://hub.docker.com/mcp/server/hyperspell/overview)
## Iaptic
Model Context Protocol server for interacting with iaptic.
[View on Docker Hub](https://hub.docker.com/mcp/server/iaptic/overview)
## Inspektor Gadget
AI interface to troubleshoot and observe Kubernetes/Container workloads.
[View on Docker Hub](https://hub.docker.com/mcp/server/inspektor-gadget/overview)
Comma-separated list of gadget images (trace\_dns, trace\_tcp, etc) to use, allowing control over which gadgets are available as MCP tools
Path to the kubeconfig file for accessing Kubernetes clusters
## Javadocs
Access to Java, Kotlin, and Scala library documentation.
[View on Docker Hub](https://hub.docker.com/mcp/server/javadocs/overview)
## JetBrains
A model context protocol server to work with JetBrains IDEs: IntelliJ, PyCharm, WebStorm, etc. Also, works with Android Studio.
[View on Docker Hub](https://hub.docker.com/mcp/server/jetbrains/overview)
## Kafka Schema Registry MCP
Comprehensive MCP server for Kafka Schema Registry operations. Features multi-registry support, schema contexts, migration tools, OAuth authentication, and 57+ tools for complete schema management. Supports SLIM\_MODE for optimal performance.
[View on Docker Hub](https://hub.docker.com/mcp/server/kafka-schema-reg-mcp/overview)
Schema Registry URL
Enable SLIM\_MODE for better performance
Enable read-only mode
## Kagi search
The Official Model Context Protocol (MCP) server for Kagi search & other tools.
[View on Docker Hub](https://hub.docker.com/mcp/server/kagisearch/overview)
## Keboola
Keboola MCP Server is an open-source bridge between your Keboola project and modern AI tools.
[View on Docker Hub](https://hub.docker.com/mcp/server/keboola-mcp/overview)
## Kong Konnect
A Model Context Protocol (MCP) server for interacting with Kong Konnect APIs, allowing AI assistants to query and analyze Kong Gateway configurations, traffic, and analytics.
[View on Docker Hub](https://hub.docker.com/mcp/server/kong/overview)
## Kubectl
MCP Server that enables AI assistants to interact with Kubernetes clusters via kubectl operations.
[View on Docker Hub](https://hub.docker.com/mcp/server/kubectl-mcp-server/overview)
## Kubernetes
Connect to a Kubernetes cluster and manage it.
[View on Docker Hub](https://hub.docker.com/mcp/server/kubernetes/overview)
the path to the host .kube/config
## Lara Translate
Connect to Lara Translate API, enabling powerful translation capabilities with support for language detection and context-aware translations.
[View on Docker Hub](https://hub.docker.com/mcp/server/lara/overview)
## LINE
MCP server that integrates the LINE Messaging API to connect an AI Agent to the LINE Official Account.
[View on Docker Hub](https://hub.docker.com/mcp/server/line/overview)
## LinkedIn
This MCP server allows Claude and other AI assistants to access your LinkedIn. Scrape LinkedIn profiles and companies, get your recommended jobs, and perform job searches. Set your li\_at LinkedIn cookie to use this server.
[View on Docker Hub](https://hub.docker.com/mcp/server/linkedin-mcp-server/overview)
Custom user agent string (optional, helps avoid detection and cookie login issues)
## LLM Text
Discovers and retrieves llms.txt from websites.
[View on Docker Hub](https://hub.docker.com/mcp/server/llmtxt/overview)
## Maestro
A Model Context Protocol (MCP) server exposing Bitcoin blockchain data through the Maestro API platform. Provides tools to explore blocks, transactions, addresses, inscriptions, runes, and other metaprotocol data.
[View on Docker Hub](https://hub.docker.com/mcp/server/maestro-mcp-server/overview)
## Manifold
Tools for accessing the Manifold Markets online prediction market platform.
[View on Docker Hub](https://hub.docker.com/mcp/server/manifold/overview)
## Mapbox
Transform any AI agent into a geospatially-aware system with Mapbox APIs. Provides geocoding, POI search, routing, travel time matrices, isochrones, and static map generation.
[View on Docker Hub](https://hub.docker.com/mcp/server/mapbox/overview)
## Mapbox Developer
Direct access to Mapbox developer APIs for AI assistants. Enables style management, token management, GeoJSON preview, and other developer tools for building Mapbox applications.
[View on Docker Hub](https://hub.docker.com/mcp/server/mapbox-devkit/overview)
## Markdownify
A Model Context Protocol server for converting almost anything to Markdown.
[View on Docker Hub](https://hub.docker.com/mcp/server/markdownify/overview)
## Markitdown
A lightweight MCP server for calling MarkItDown.
[View on Docker Hub](https://hub.docker.com/mcp/server/markitdown/overview)
## Maven Tools
JVM dependency intelligence for any build tool using Maven Central Repository. Includes Context7 integration for upgrade documentation and guidance.
[View on Docker Hub](https://hub.docker.com/mcp/server/maven-tools-mcp/overview)
## Memory (Reference)
Knowledge graph-based persistent memory system.
[View on Docker Hub](https://hub.docker.com/mcp/server/memory/overview)
## Mercado Libre
Provides access to Mercado Libre E-Commerce API.
[View on Docker Hub](https://hub.docker.com/mcp/server/mercado-libre/overview)
## Mercado Pago
Provides access to Mercado Pago Marketplace API.
[View on Docker Hub](https://hub.docker.com/mcp/server/mercado-pago/overview)
## Metabase MCP
A comprehensive MCP server for Metabase with 70+ tools.
[View on Docker Hub](https://hub.docker.com/mcp/server/metabase/overview)
## Minecraft Wiki
A MCP Server for browsing the official Minecraft Wiki!.
[View on Docker Hub](https://hub.docker.com/mcp/server/minecraft-wiki/overview)
## MongoDB
A Model Context Protocol server to connect to MongoDB databases and MongoDB Atlas Clusters.
[View on Docker Hub](https://hub.docker.com/mcp/server/mongodb/overview)
## MultiversX
MCP Server for MultiversX.
[View on Docker Hub](https://hub.docker.com/mcp/server/multiversx-mx/overview)
## Nasdaq Data Link
MCP server to interact with the data feeds provided by the Nasdaq Data Link. Developed by the community and maintained by Stefano Amorelli.
[View on Docker Hub](https://hub.docker.com/mcp/server/nasdaq-data-link/overview)
## Needle
Production-ready RAG service to search and retrieve data from your documents.
[View on Docker Hub](https://hub.docker.com/mcp/server/needle-mcp/overview)
## Neo4j Cloud Aura Api
Manage Neo4j Aura database instances through the Neo4j Aura API.
[View on Docker Hub](https://hub.docker.com/mcp/server/neo4j-cloud-aura-api/overview)
## Neo4j Cypher
Interact with Neo4j using Cypher graph queries.
[View on Docker Hub](https://hub.docker.com/mcp/server/neo4j-cypher/overview)
## Neo4j Data Modeling
MCP server that assists in creating, validating and visualizing graph data models.
[View on Docker Hub](https://hub.docker.com/mcp/server/neo4j-data-modeling/overview)
## Neo4j Memory
Provide persistent memory capabilities through Neo4j graph database integration.
[View on Docker Hub](https://hub.docker.com/mcp/server/neo4j-memory/overview)
## Neon
MCP server for interacting with Neon Management API and databases.
[View on Docker Hub](https://hub.docker.com/mcp/server/neon/overview)
## Node.js Sandbox
A Node.js–based Model Context Protocol server that spins up disposable Docker containers to execute arbitrary JavaScript.
[View on Docker Hub](https://hub.docker.com/mcp/server/node-code-sandbox/overview)
## Notion
Official Notion MCP Server.
[View on Docker Hub](https://hub.docker.com/mcp/server/notion/overview)
## Novita
Seamless interaction with Novita AI platform resources.
[View on Docker Hub](https://hub.docker.com/mcp/server/novita/overview)
## NPM Sentinel
MCP server that enables intelligent NPM package analysis powered by AI.
[View on Docker Hub](https://hub.docker.com/mcp/server/npm-sentinel/overview)
## Obsidian
MCP server that interacts with Obsidian via the Obsidian rest API community plugin.
[View on Docker Hub](https://hub.docker.com/mcp/server/obsidian/overview)
## Okta
Secure Okta identity and access management via Model Context Protocol (MCP). Access Okta users, groups, applications, logs, and policies through AI assistants with enterprise-grade security.
[View on Docker Hub](https://hub.docker.com/mcp/server/okta-mcp-fctr/overview)
Okta organization URL (e.g., [https://dev-123456.okta.com](https://dev-123456.okta.com))
Maximum concurrent requests to Okta API
Logging level for server output
## omi-mcp
A Model Context Protocol server for Omi interaction and automation. This server provides tools to read, search, and manipulate Memories and Conversations.
[View on Docker Hub](https://hub.docker.com/mcp/server/omi/overview)
## ONLYOFFICE DocSpace
ONLYOFFICE DocSpace is a room-based collaborative platform which allows organizing a clear file structure depending on users' needs or project goals.
[View on Docker Hub](https://hub.docker.com/mcp/server/onlyoffice-docspace/overview)
## OpenAPI Toolkit for MCP
Fetch, validate, and generate code or curl from any OpenAPI or Swagger spec - all from a single URL.
[View on Docker Hub](https://hub.docker.com/mcp/server/openapi/overview)
## OpenAPI Schema
OpenAPI Schema Model Context Protocol Server.
[View on Docker Hub](https://hub.docker.com/mcp/server/openapi-schema/overview)
## Airbnb Search
MCP Server for searching Airbnb and get listing details.
[View on Docker Hub](https://hub.docker.com/mcp/server/openbnb-airbnb/overview)
## OpenMesh
Discover and connect to a curated marketplace of MCP servers for extending AI agent capabilities.
[View on Docker Hub](https://hub.docker.com/mcp/server/openmesh/overview)
## Openweather
A simple MCP service that provides current weather and 5-day forecast using the free OpenWeatherMap API.
[View on Docker Hub](https://hub.docker.com/mcp/server/openweather/overview)
## OpenZeppelin Cairo Contracts
Access to OpenZeppelin Cairo Contracts.
[View on Docker Hub](https://hub.docker.com/mcp/server/openzeppelin-cairo/overview)
## OpenZeppelin Solidity Contracts
Access to OpenZeppelin Solidity Contracts.
[View on Docker Hub](https://hub.docker.com/mcp/server/openzeppelin-solidity/overview)
## OpenZeppelin Stellar Contracts
Access to OpenZeppelin Stellar Contracts.
[View on Docker Hub](https://hub.docker.com/mcp/server/openzeppelin-stellar/overview)
## OpenZeppelin Stylus Contracts
Access to OpenZeppelin Stylus Contracts.
[View on Docker Hub](https://hub.docker.com/mcp/server/openzeppelin-stylus/overview)
## Opik
Model Context Protocol (MCP) implementation for Opik enabling seamless IDE integration and unified access to prompts, projects, traces, and metrics.
[View on Docker Hub](https://hub.docker.com/mcp/server/opik/overview)
## Opine
A Model Context Protocol (MCP) server for querying deals and evaluations from the Opine CRM API.
[View on Docker Hub](https://hub.docker.com/mcp/server/opine-mcp-server/overview)
## Oracle Database
Connect to Oracle databases via MCP, providing secure read-only access with support for schema exploration, query execution, and metadata inspection.
[View on Docker Hub](https://hub.docker.com/mcp/server/oracle/overview)
## OSP Marketing Tools
A Model Context Protocol (MCP) server that empowers LLMs to use some of Open Srategy Partners' core writing and product marketing techniques.
[View on Docker Hub](https://hub.docker.com/mcp/server/osp_marketing_tools/overview)
## Oxylabs
A Model Context Protocol (MCP) server that enables AI assistants like Claude to seamlessly access web data through Oxylabs' powerful web scraping technology.
[View on Docker Hub](https://hub.docker.com/mcp/server/oxylabs/overview)
## Paper Search
A MCP for searching and downloading academic papers from multiple sources like arXiv, PubMed, bioRxiv, etc.
[View on Docker Hub](https://hub.docker.com/mcp/server/paper-search/overview)
## Perplexity
Connector for Perplexity API, to enable real-time, web-wide research.
[View on Docker Hub](https://hub.docker.com/mcp/server/perplexity-ask/overview)
## Program Integrity Alliance
An MCP server to help make U.S. Government open datasets AI-friendly.
[View on Docker Hub](https://hub.docker.com/mcp/server/pia/overview)
## Pinecone Assistant
Pinecone Assistant MCP server.
[View on Docker Hub](https://hub.docker.com/mcp/server/pinecone/overview)
## ExecuteAutomation Playwright MCP
Playwright Model Context Protocol Server - Tool to automate Browsers and APIs in Claude Desktop, Cline, Cursor IDE and More 🔌.
[View on Docker Hub](https://hub.docker.com/mcp/server/playwright-mcp-server/overview)
## Plugged.in MCP Proxy
A unified MCP proxy that aggregates multiple MCP servers into one interface, enabling seamless tool discovery and management across all your AI interactions. Manage all your MCP servers from a single connection point with RAG capabilities and real-time notifications.
[View on Docker Hub](https://hub.docker.com/mcp/server/pluggedin-mcp-proxy/overview)
Base URL for the Plugged.in API (optional, defaults to [https://plugged.in](https://plugged.in) for cloud or [http://localhost:12005](http://localhost:12005) for self-hosted)
## Polar Signals
MCP server for Polar Signals Cloud continuous profiling platform, enabling AI assistants to analyze CPU performance, memory usage, and identify optimization opportunities in production systems.
[View on Docker Hub](https://hub.docker.com/mcp/server/polar-signals/overview)
## PomoDash
Connect your AI assistant to PomoDash for seamless task and project management.
[View on Docker Hub](https://hub.docker.com/mcp/server/pomodash/overview)
## PostgreSQL readonly (Archived)
Connect with read-only access to PostgreSQL databases. This server enables LLMs to inspect database schemas and execute read-only queries.
[View on Docker Hub](https://hub.docker.com/mcp/server/postgres/overview)
## Postman
Postman's MCP server connects AI agents, assistants, and chatbots directly to your APIs on Postman. Use natural language to prompt AI to automate work across your Postman collections, environments, workspaces, and more.
[View on Docker Hub](https://hub.docker.com/mcp/server/postman/overview)
## Pref Editor
Pref Editor is a tool for viewing and editing Android app preferences during development.
[View on Docker Hub](https://hub.docker.com/mcp/server/pref-editor/overview)
## Prometheus
A Model Context Protocol (MCP) server that enables AI assistants to query and analyze Prometheus metrics through standardized interfaces. Connect to your Prometheus instance to retrieve metrics, perform queries, and gain insights into your system's performance and health.
[View on Docker Hub](https://hub.docker.com/mcp/server/prometheus/overview)
The URL of your Prometheus server
## Puppeteer (Archived)
Browser automation and web scraping using Puppeteer.
[View on Docker Hub](https://hub.docker.com/mcp/server/puppeteer/overview)
## Python Refactoring Assistant
Educational Python refactoring assistant that provides guided suggestions for AI assistants. Features: • Step-by-step refactoring instructions without modifying code • Comprehensive code analysis using professional tools (Rope, Radon, Vulture, Jedi, LibCST, Pyrefly) • Educational approach teaching refactoring patterns through guided practice • Support for both guide-only and apply-changes modes • Identifies long functions, high complexity, dead code, and type issues • Provides precise line numbers and specific refactoring instructions • Compatible with all AI assistants (Claude, GPT, Cursor, Continue, etc.) Perfect for developers learning refactoring patterns while maintaining full control over code changes. Acts as a refactoring mentor rather than an automated code modifier.
[View on Docker Hub](https://hub.docker.com/mcp/server/mcp-python-refactoring/overview)
## QuantConnect
The QuantConnect MCP Server is a bridge for AIs (such as Claude and OpenAI o3 Pro) to interact with our cloud platform. When equipped with our MCP, the AI can perform tasks on your behalf through our API such as updating projects, writing strategies, backtesting, and deploying strategies to production live-trading.
[View on Docker Hub](https://hub.docker.com/mcp/server/quantconnect/overview)
## Ramparts MCP Security Scanner
A comprehensive security scanner for MCP servers with YARA rules and static analysis capabilities.
[View on Docker Hub](https://hub.docker.com/mcp/server/ramparts/overview)
## Razorpay
Razorpay's Official MCP Server.
[View on Docker Hub](https://hub.docker.com/mcp/server/razorpay/overview)
## Mcp reddit
A comprehensive Model Context Protocol (MCP) server for Reddit integration. This server enables AI agents to interact with Reddit programmatically through a standardized interface.
[View on Docker Hub](https://hub.docker.com/mcp/server/mcp-reddit/overview)
## Redis
Access to Redis database operations.
[View on Docker Hub](https://hub.docker.com/mcp/server/redis/overview)
## Redis Cloud
MCP Server for Redis Cloud's API, allowing you to manage your Redis Cloud resources using natural language.
[View on Docker Hub](https://hub.docker.com/mcp/server/redis-cloud/overview)
## Ref - up-to-date docs
Ref powerful search tool connets your coding tools with documentation context. It includes an up-to-date index of public documentation and it can ingest your private documentation (eg. GitHub repos, PDFs) as well.
[View on Docker Hub](https://hub.docker.com/mcp/server/ref/overview)
## Remote MCP
Tools for finding remote MCP servers.
[View on Docker Hub](https://hub.docker.com/mcp/server/remote-mcp/overview)
## Render
Interact with your Render resources via LLMs.
[View on Docker Hub](https://hub.docker.com/mcp/server/render/overview)
## Send emails
Send emails directly from Cursor with this email sending MCP server.
[View on Docker Hub](https://hub.docker.com/mcp/server/resend/overview)
comma separated list of reply to email addresses
sender email address
## RISKEN
RISKEN's official MCP Server.
[View on Docker Hub](https://hub.docker.com/mcp/server/risken/overview)
## Root.io Vulnerability Remediation MCP
MCP server that provides container image vulnerability scanning and remediation capabilities through Root.io.
[View on Docker Hub](https://hub.docker.com/mcp/server/root/overview)
## WiseVision ROS2
Python server implementing Model Context Protocol (MCP) for ROS2.
[View on Docker Hub](https://hub.docker.com/mcp/server/ros2/overview)
## Rube
Access to Rube's catalog of remote MCP servers.
[View on Docker Hub](https://hub.docker.com/mcp/server/rube/overview)
## Blazing-fast, asynchronous for seamless filesystem operations.
The Rust MCP Filesystem is a high-performance, asynchronous, and lightweight Model Context Protocol (MCP) server built in Rust for secure and efficient filesystem operations. Designed with security in mind, it operates in read-only mode by default and restricts clients from updating allowed directories via MCP Roots unless explicitly enabled, ensuring robust protection against unauthorized access. Leveraging asynchronous I/O, it delivers blazingly fast performance with a minimal resource footprint. Optimized for token efficiency, the Rust MCP Filesystem enables large language models (LLMs) to precisely target searches and edits within specific sections of large files and restrict operations by file size range, making it ideal for efficient file exploration, automation, and system integration.
[View on Docker Hub](https://hub.docker.com/mcp/server/rust-mcp-filesystem/overview)
Enable read/write mode. If false, the app operates in read-only mode.
List of directories that rust-mcp-filesystem can access.
Enable dynamic directory access control via MCP client-side Roots.
## SchemaCrawler AI
The SchemaCrawler AI MCP Server enables natural language interaction with your database schema using an MCP client in "Agent" mode. It allows users to explore tables, columns, foreign keys, triggers, stored procedures and more simply by asking questions like "Explain the code for the interest calculation stored procedure". You can also ask it to help with SQL, since it knows your schema. This is ideal for developers, DBAs, and data analysts who want to streamline schema comprehension and query development without diving into dense documentation.
[View on Docker Hub](https://hub.docker.com/mcp/server/schemacrawler-ai/overview)
\--info-level How much database metadata to retrieve
\--database Database to connect to (optional)
\--host Database host (optional)
\--port Database port (optional)
\--server SchemaCrawler database plugin
\--url JDBC URL for database connection
Host volume to map within the Docker container
## Schogini MCP Image Border
This adds a border to an image and returns base64 encoded image.
[View on Docker Hub](https://hub.docker.com/mcp/server/schogini-mcp-image-border/overview)
## ScrapeGraph
ScapeGraph MCP Server.
[View on Docker Hub](https://hub.docker.com/mcp/server/scrapegraph/overview)
## Scrapezy
A Model Context Protocol server for Scrapezy that enables AI models to extract structured data from websites.
[View on Docker Hub](https://hub.docker.com/mcp/server/scrapezy/overview)
## Securenote.link
SecureNote.link MCP Server - allowing AI agents to securely share sensitive information through end-to-end encrypted notes.
[View on Docker Hub](https://hub.docker.com/mcp/server/securenote-link-mcp-server/overview)
## Semgrep
MCP server for using Semgrep to scan code for security vulnerabilities.
[View on Docker Hub](https://hub.docker.com/mcp/server/semgrep/overview)
## Sentry (Archived)
A Model Context Protocol server for retrieving and analyzing issues from Sentry.io. This server provides tools to inspect error reports, stacktraces, and other debugging information from your Sentry account.
[View on Docker Hub](https://hub.docker.com/mcp/server/sentry/overview)
## Sequa.AI
Stop stitching context for Copilot and Cursor. With Sequa MCP, your AI tools know your entire codebase and docs out of the box.
[View on Docker Hub](https://hub.docker.com/mcp/server/sequa/overview)
## Sequential Thinking (Reference)
Dynamic and reflective problem-solving through thought sequences.
[View on Docker Hub](https://hub.docker.com/mcp/server/sequentialthinking/overview)
## Short.io
Access to Short.io's link shortener and analytics tools.
[View on Docker Hub](https://hub.docker.com/mcp/server/short-io/overview)
## SimpleCheckList
Advanced SimpleCheckList with MCP server and SQLite database for comprehensive task management. Features: • Complete project and task management system • Hierarchical organization (Projects → Groups → Task Lists → Tasks → Subtasks) • SQLite database for data persistence • RESTful API with comprehensive endpoints • MCP protocol compliance for AI assistant integration • Docker-optimized deployment with stability improvements **v1.0.1 Update**: Enhanced Docker stability with improved container lifecycle management. Default mode optimized for containerized deployment with reliable startup and shutdown processes. Perfect for AI assistants managing complex project workflows and task hierarchies.
[View on Docker Hub](https://hub.docker.com/mcp/server/simplechecklist/overview)
## Singlestore
MCP server for interacting with SingleStore Management API and services.
[View on Docker Hub](https://hub.docker.com/mcp/server/singlestore/overview)
## Slack (Archived)
Interact with Slack Workspaces over the Slack API.
[View on Docker Hub](https://hub.docker.com/mcp/server/slack/overview)
## SmartBear
MCP server for AI access to SmartBear tools, including BugSnag, Reflect, API Hub, PactFlow.
[View on Docker Hub](https://hub.docker.com/mcp/server/smartbear/overview)
## SonarQube
Interact with SonarQube Cloud, Server and Community build over the web API. Analyze code to identify quality and security issues.
[View on Docker Hub](https://hub.docker.com/mcp/server/sonarqube/overview)
Organization key for SonarQube Cloud, not required for SonarQube Server or Community Build
URL of the SonarQube instance, to provide only for SonarQube Server or Community Build
## SQLite (Archived)
Database interaction and business intelligence capabilities.
[View on Docker Hub](https://hub.docker.com/mcp/server/SQLite/overview)
## StackGen
AI-powered DevOps assistant for managing cloud infrastructure and applications.
[View on Docker Hub](https://hub.docker.com/mcp/server/stackgen/overview)
URL of your StackGen instance
## StackHawk
A Model Context Protocol (MCP) server for integrating with StackHawk's security scanning platform. Provides security analytics, YAML configuration management, sensitive data/threat surface analysis, and anti-hallucination tools for LLMs.
[View on Docker Hub](https://hub.docker.com/mcp/server/stackhawk/overview)
## Stripe
Interact with Stripe services over the Stripe API.
[View on Docker Hub](https://hub.docker.com/mcp/server/stripe/overview)
## Supadata
Official Supadata MCP Server - Adds powerful video & web scraping to Cursor, Claude and any other LLM clients.
[View on Docker Hub](https://hub.docker.com/mcp/server/supadata/overview)
## Suzieq MCP
MCP Server to interact with a SuzieQ network observability instance via its REST API.
[View on Docker Hub](https://hub.docker.com/mcp/server/suzieq/overview)
## Task orchestrator
Model Context Protocol (MCP) server for comprehensive task and feature management, providing AI assistants with a structured, context-efficient way to interact with project data.
[View on Docker Hub](https://hub.docker.com/mcp/server/task-orchestrator/overview)
## Tavily
The Tavily MCP server provides seamless interaction with the tavily-search and tavily-extract tools, real-time web search capabilities through the tavily-search tool and Intelligent data extraction from web pages via the tavily-extract tool.
[View on Docker Hub](https://hub.docker.com/mcp/server/tavily/overview)
## Teamwork
Tools for Teamwork.com products.
[View on Docker Hub](https://hub.docker.com/mcp/server/teamwork/overview)
## Telnyx
Enables interaction with powerful telephony, messaging, and AI assistant APIs.
[View on Docker Hub](https://hub.docker.com/mcp/server/telnyx/overview)
## Tembo
MCP server for Tembo Cloud's platform API.
[View on Docker Hub](https://hub.docker.com/mcp/server/tembo/overview)
## Hashicorp Terraform
The Terraform MCP Server provides seamless integration with Terraform ecosystem, enabling advanced automation and interaction capabilities for Infrastructure as Code (IaC) development.
[View on Docker Hub](https://hub.docker.com/mcp/server/terraform/overview)
## Text-to-GraphQL
Transform natural language queries into GraphQL queries using an AI agent. Provides schema management, query validation, execution, and history tracking.
[View on Docker Hub](https://hub.docker.com/mcp/server/text-to-graphql/overview)
Authentication method for GraphQL API
OpenAI model to use
Model temperature for responses
## Tigris Data
Tigris is a globally distributed S3-compatible object storage service that provides low latency anywhere in the world, enabling developers to store and access any amount of data for a wide range of use cases.
[View on Docker Hub](https://hub.docker.com/mcp/server/tigris/overview)
## Time (Reference)
Time and timezone conversion capabilities.
[View on Docker Hub](https://hub.docker.com/mcp/server/time/overview)
## Triplewhale
Triplewhale MCP Server.
[View on Docker Hub](https://hub.docker.com/mcp/server/triplewhale/overview)
## Unreal Engine
A comprehensive Model Context Protocol (MCP) server that enables AI assistants to control Unreal Engine via Remote Control API. Built with TypeScript and designed for game development automation.
[View on Docker Hub](https://hub.docker.com/mcp/server/unreal-engine-mcp-server/overview)
Logging level
Unreal Engine host address. Use: host.docker.internal for local UE on Windows/Mac Docker, 127.0.0.1 for Linux without Docker, or actual IP address (e.g., 192.168.1.100) for remote UE
Remote Control HTTP port
Remote Control WebSocket port
## VeyraX
VeyraX MCP is the only connection you need to access all your tools in any MCP-compatible environment.
[View on Docker Hub](https://hub.docker.com/mcp/server/veyrax/overview)
## Vizro
provides tools and templates to create a functioning Vizro chart or dashboard step by step.
[View on Docker Hub](https://hub.docker.com/mcp/server/vizro/overview)
## Vuln nist
This MCP server exposes tools to query the NVD/CVE REST API and return formatted text results suitable for LLM consumption via the MCP protocol. It includes automatic query chunking for large date ranges and parallel processing for improved performance.
[View on Docker Hub](https://hub.docker.com/mcp/server/vuln-nist-mcp-server/overview)
## Wayfound MCP
Wayfound’s MCP server allows business users to govern, supervise, and improve AI Agents.
[View on Docker Hub](https://hub.docker.com/mcp/server/wayfound/overview)
## Webflow
Model Context Protocol (MCP) server for the Webflow Data API.
[View on Docker Hub](https://hub.docker.com/mcp/server/webflow/overview)
## Wikipedia
A Model Context Protocol (MCP) server that retrieves information from Wikipedia to provide context to LLMs.
[View on Docker Hub](https://hub.docker.com/mcp/server/wikipedia-mcp/overview)
## WolframAlpha
Connect your chat repl to wolfram alpha computational intelligence.
[View on Docker Hub](https://hub.docker.com/mcp/server/wolfram-alpha/overview)
## YouTube transcripts
Retrieves transcripts for given YouTube video URLs.
[View on Docker Hub](https://hub.docker.com/mcp/server/youtube_transcript/overview)
## Zerodha Kite Connect
MCP server for Zerodha Kite Connect API - India's leading stock broker trading platform. Execute trades, manage portfolios, and access real-time market data for NSE, BSE, and other Indian exchanges.
[View on Docker Hub](https://hub.docker.com/mcp/server/zerodha-kite/overview)
Access token obtained after OAuth authentication (optional - can be generated at runtime)
Your Kite Connect API key from the developer console
OAuth redirect URL configured in your Kite Connect app
# Custom servers
Source: https://e2b.mintlify.app/docs/mcp/custom-servers
Use custom MCP servers from GitHub repositories
In addition to the 200+ pre-built MCP servers from the [Docker MCP Catalog](https://hub.docker.com/mcp), you can run custom MCP servers directly from public GitHub repositories.
## How it works
When you specify a GitHub repository, E2B will:
1. Clone the repository into the sandbox
2. Run the `installCmd` (optional) to install dependencies
3. Run the `runCmd` to start the MCP server with stdio transport
The `runCmd` must start an MCP server that follows the [MCP specification](https://modelcontextprotocol.io/specification/2025-06-18) and communicates via stdio (standard input/output).
## Using a custom MCP server
```typescript TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import Sandbox from 'e2b'
const sandbox = await Sandbox.create({
mcp: {
'github/modelcontextprotocol/servers': {
installCmd: 'npm install',
runCmd: 'sudo npx -y @modelcontextprotocol/server-filesystem /root',
},
},
});
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
import os
sbx = Sandbox.create(
mcp={
"github/modelcontextprotocol/servers": {
"install_cmd": "npm install",
"run_cmd": "sudo npx -y @modelcontextprotocol/server-filesystem /root",
},
}
)
```
## Configuration
Optional command to run before starting the MCP server. Use this to install dependencies (e.g., `npm install`, `pip install -r requirements.txt`).
Command to start the MCP server. Must launch a stdio-enabled MCP server.
**Important for npx-based servers:** Always include `installCmd: 'npm install'` (or equivalent) when using `npx` in your `runCmd`. Without installing dependencies first, npx will try to use the local repository and fail.
## Troubleshooting
If your custom MCP server doesn't work as expected:
1. Explore the sandbox either via the [dashboard](https://e2b.dev/dashboard) or by connecting to it via `e2b connect `
2. Check the gateway log file with `sudo cat /var/log/mcp-gateway/gateway.log`.
# Custom templates
Source: https://e2b.mintlify.app/docs/mcp/custom-templates
Use MCP servers with custom sandbox templates
You can prepull MCP server Docker images during template build time to significantly improve runtime performance.
## How it works
When you build a template with prepulled MCP servers, the Docker images for those servers are downloaded and cached during the build process. This means when you create a sandbox from that template, the MCP servers are ready to use immediately without waiting for image downloads.
You must use the MCP gateway enabled template (`mcp-gateway`) as your base template to use this feature.
## Building a template with MCP servers
Use the `addMcpServer()` method (TypeScript) or `add_mcp_server()` method (Python) to prepull MCP server images during template build. You can pass a single server or an array of servers.
The server names (like `"browserbase"` and `"exa"`) correspond to the keys defined in the [Available Servers](/docs/mcp/available-servers) documentation.
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import "dotenv/config";
import { Template, defaultBuildLogger } from 'e2b';
export const template = Template()
.fromTemplate("mcp-gateway")
.addMcpServer(["browserbase", "exa"]);
await Template.build(template, 'my-mcp-gateway', {
cpuCount: 8,
memoryMB: 8192,
onBuildLogs: defaultBuildLogger(),
});
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from dotenv import load_dotenv
from e2b import Template, default_build_logger
load_dotenv()
template = (
Template()
.from_template("mcp-gateway")
.add_mcp_server(["browserbase", "exa"])
)
Template.build(
template,
'my-mcp-gateway',
cpu_count=8,
memory_mb=8192,
on_build_logs=default_build_logger(),
)
```
## Using the template
Once built, create sandboxes from your template. You still need to provide the configuration for each MCP server.
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b';
const sandbox = await Sandbox.create({
template: "my-mcp-gateway",
mcp: {
browserbase: {
apiKey: process.env.BROWSERBASE_API_KEY!,
geminiApiKey: process.env.GEMINI_API_KEY!,
projectId: process.env.BROWSERBASE_PROJECT_ID!,
},
exa: {
apiKey: process.env.EXA_API_KEY!,
},
},
});
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
import os
sbx = Sandbox.create(
template="my-mcp-gateway",
mcp={
"browserbase": {
"apiKey": os.getenv("BROWSERBASE_API_KEY"),
"geminiApiKey": os.getenv("GEMINI_API_KEY"),
"projectId": os.getenv("BROWSERBASE_PROJECT_ID"),
},
"exa": {
"apiKey": os.getenv("EXA_API_KEY"),
},
}
)
```
## Learn more
For more information about working with templates, see:
* [Template Quickstart](/docs/template/quickstart) - Get started with custom templates
* [Defining Templates](/docs/template/defining-template) - Learn all template configuration options
* [Template Build](/docs/template/build) - Understand the build process
# Examples
Source: https://e2b.mintlify.app/docs/mcp/examples
Example projects using MCP servers in E2B sandboxes
Claude Code with MCP integration
Web automation agent with Browserbase
AI research using Groq and Exa
Research Agent using the OpenAI Agents framework
Basic MCP client connecting to an E2B Sandbox
Use custom MCP servers installed from GitHub
Create a custom E2B Sandbox with pre-installed MCP servers
# Quickstart
Source: https://e2b.mintlify.app/docs/mcp/quickstart
Get started with MCP integration
You can connect to the MCPs running inside the sandbox both from outside and inside the sandbox.
## From outside the sandbox
To connect to the MCPs running inside the sandbox, use the `sandbox.getMcpUrl()` in JavaScript and `sandbox.get_mcp_url()` in Python.
```typescript TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import Sandbox from 'e2b'
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
const sandbox = await Sandbox.create({
mcp: {
browserbase: {
apiKey: process.env.BROWSERBASE_API_KEY!,
geminiApiKey: process.env.GEMINI_API_KEY!,
projectId: process.env.BROWSERBASE_PROJECT_ID!,
},
exa: {
apiKey: process.env.EXA_API_KEY!,
},
notion: {
internalIntegrationToken: process.env.NOTION_API_KEY!,
},
},
});
const client = new Client({
name: 'e2b-mcp-client',
version: '1.0.0'
});
const transport = new StreamableHTTPClientTransport(
new URL(sandbox.getMcpUrl()),
{
requestInit: {
headers: {
'Authorization': `Bearer ${await sandbox.getMcpToken()}`
}
}
}
);
await client.connect(transport);
const tools = await client.listTools();
console.log('Available tools:', tools.tools.map(t => t.name));
await client.close();
await sandbox.kill();
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import os
import dotenv
dotenv.load_dotenv()
from e2b import AsyncSandbox
import asyncio
from datetime import timedelta
from mcp.client.session import ClientSession
from mcp.client.streamable_http import streamablehttp_client
async def main():
sandbox = await AsyncSandbox.create(
mcp={
"browserbase": {
"apiKey": os.environ["BROWSERBASE_API_KEY"],
"geminiApiKey": os.environ["GEMINI_API_KEY"],
"projectId": os.environ["BROWSERBASE_PROJECT_ID"],
},
"exa": {
"apiKey": os.environ["EXA_API_KEY"],
},
"notion": {
"internalIntegrationToken": os.environ["NOTION_API_KEY"],
},
}
)
async with streamablehttp_client(
url=sandbox.get_mcp_url(),
headers={"Authorization": f"Bearer {await sandbox.get_mcp_token()}"},
timeout=timedelta(seconds=600)
) as (read_stream, write_stream, _):
async with ClientSession(read_stream, write_stream) as session:
await session.initialize()
tools = await session.list_tools()
print(f"Available tools: {[tool.name for tool in tools.tools]}")
await sandbox.kill()
if __name__ == "__main__":
asyncio.run(main())
```
## From inside the sandbox
If you need to access the MCP gateway from within the sandbox itself, it's available at:
```
http://localhost:50005/mcp
```
You'll need to include the Authorization header with the MCP token when making requests from inside the sandbox. How that is added depends on the MCP client you use:
## Claude
```
claude mcp add --transport http e2b-mcp-gateway --header "Authorization: Bearer "
```
## OpenAI Agents
```typescript TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { MCPServerStreamableHttp } from '@openai/agents';
const mcp = new MCPServerStreamableHttp({
url: mcpUrl,
name: 'E2B MCP Gateway',
requestInit: {
headers: {
'Authorization': `Bearer ${await sandbox.getMcpToken()}`
}
},
});
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import asyncio
import os
from e2b import AsyncSandbox
from agents.mcp import MCPServerStreamableHttp
async def main():
async with MCPServerStreamableHttp(
name="e2b-mcp-client",
params={
"url": sandbox.get_mcp_url(),
"headers": {"Authorization": f"Bearer {await sandbox.get_mcp_token()}"},
},
) as server:
tools = await server.list_tools()
print("Available tools:", [t.name for t in tools])
# Clean up
await sandbox.kill()
asyncio.run(main())
```
## Official MCP client
```typescript Typescript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
const client = new Client({
name: 'e2b-mcp-client',
version: '1.0.0'
});
const transport = new StreamableHTTPClientTransport(
new URL(sandbox.getMcpUrl()),
{
requestInit: {
headers: {
'Authorization': `Bearer ${await sandbox.getMcpToken()}`
}
}
}
);
await client.connect(transport);
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import asyncio
from datetime import timedelta
from mcp.client.session import ClientSession
from mcp.client.streamable_http import streamablehttp_client
async def main():
async with streamablehttp_client(
url=sandbox.get_mcp_url(),
headers={"Authorization": f"Bearer {await sandbox.get_mcp_token()}"},
timeout=timedelta(seconds=600)
) as (read_stream, write_stream, _):
async with ClientSession(read_stream, write_stream) as session:
await session.initialize()
tools = await session.list_tools()
print(f"Available tools: {[tool.name for tool in tools.tools]}")
await sandbox.kill()
if __name__ == "__main__":
asyncio.run(main())
```
This list is not exhaustive. You can find more examples in the [E2B Cookbook](https://github.com/e2b-dev/e2b-cookbook).
## Debugging with MCP Inspector
The [MCP Inspector](https://github.com/modelcontextprotocol/inspector) is a useful tool for debugging and testing your MCP server setup. Get the command to run:
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
npx @modelcontextprotocol/inspector --transport http --url --header "Authorization: Bearer ${mcpToken}"
```
Run the command in your terminal. This will open a web interface where you can:
* Browse available tools
* Test tool calls with different parameters
* Inspect request/response payloads
* Debug connection issues
# Running your first Sandbox
Source: https://e2b.mintlify.app/docs/quickstart
This guide will show you how to start your first E2B Sandbox.
Every new E2B account get \$100 in credits. You can sign up [here](https://e2b.dev/auth/sign-up).
1. Navigate to the E2B Dashboard.
2. Copy your [API key](https://e2b.dev/dashboard?tab=keys).
3. Paste your E2B API key into your .env file.
```bash .env theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
E2B_API_KEY=e2b_***
```
Install the E2B SDK to your project by running the following command in your terminal.
```javascript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
npm i @e2b/code-interpreter dotenv
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
pip install e2b-code-interpreter python-dotenv
```
We'll write the minimal code for starting Sandbox, executing Python inside it and listing all files inside the root directory.
```javascript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
// index.ts
import 'dotenv/config'
import { Sandbox } from '@e2b/code-interpreter'
const sbx = await Sandbox.create() // Creates a persistent sandbox session
const execution = await sbx.runCode('print("hello world")') // Execute Python inside the sandbox
console.log(execution.logs)
const files = await sbx.files.list('/')
console.log(files)
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
# main.py
from dotenv import load_dotenv
load_dotenv()
from e2b_code_interpreter import Sandbox
sbx = Sandbox.create() # Creates a persistent sandbox session
execution = sbx.run_code("print('hello world')") # Execute Python inside the sandbox
print(execution.logs)
files = sbx.files.list("/")
print(files)
```
Run the code with the following command:
```bash JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
npx tsx ./index.ts
```
```bash Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
python main.py
```
# Connect LLMs to E2B
Source: https://e2b.mintlify.app/docs/quickstart/connect-llms
E2B can work with any LLM and AI framework. The easiest way to connect an LLM to E2B is to use the tool use capabilities of the LLM (sometimes known as function calling).
If the LLM doesn't support tool use, you can, for example, prompt the LLM to output code snippets and then manually extract the code snippets with [RegEx](https://en.wikipedia.org/wiki/Regular_expression).
## Contents
* [OpenAI](#openai)
* [Anthropic](#anthropic)
* [Mistral](#mistral)
* [Groq](#groq)
* [Vercel AI SDK](#vercel-ai-sdk)
* [CrewAI](#crewai)
* [LangChain](#langchain)
* [LlamaIndex](#llamaindex)
* [Ollama](#ollama)
* [Hugging Face](#hugging-face)
***
## OpenAI
### Simple
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
# pip install openai e2b-code-interpreter
from openai import OpenAI
from e2b_code_interpreter import Sandbox
# Create OpenAI client
client = OpenAI()
system = "You are a helpful assistant that can execute python code in a Jupyter notebook. Only respond with the code to be executed and nothing else. Strip backticks in code blocks."
prompt = "Calculate how many r's are in the word 'strawberry'"
# Send messages to OpenAI API
response = client.chat.completions.create(
model="gpt-4o",
messages=[
{"role": "system", "content": system},
{"role": "user", "content": prompt}
]
)
# Extract the code from the response
code = response.choices[0].message.content
# Execute code in E2B Sandbox
if code:
with Sandbox.create() as sandbox:
execution = sandbox.run_code(code)
result = execution.text
print(result)
```
### Function calling
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
# pip install openai e2b-code-interpreter
import json
from openai import OpenAI
from e2b_code_interpreter import Sandbox
# Create OpenAI client
client = OpenAI()
model = "gpt-4o"
# Define the messages
messages = [
{
"role": "user",
"content": "Calculate how many r's are in the word 'strawberry'"
}
]
# Define the tools
tools = [{
"type": "function",
"function": {
"name": "execute_python",
"description": "Execute python code in a Jupyter notebook cell and return result",
"parameters": {
"type": "object",
"properties": {
"code": {
"type": "string",
"description": "The python code to execute in a single cell"
}
},
"required": ["code"]
}
}
}]
# Generate text with OpenAI
response = client.chat.completions.create(
model=model,
messages=messages,
tools=tools,
)
# Append the response message to the messages list
response_message = response.choices[0].message
messages.append(response_message)
# Execute the tool if it's called by the model
if response_message.tool_calls:
for tool_call in response_message.tool_calls:
if tool_call.function.name == "execute_python":
# Create a sandbox and execute the code
with Sandbox.create() as sandbox:
code = json.loads(tool_call.function.arguments)['code']
execution = sandbox.run_code(code)
result = execution.text
# Send the result back to the model
messages.append({
"role": "tool",
"name": "execute_python",
"content": result,
"tool_call_id": tool_call.id,
})
# Generate the final response
final_response = client.chat.completions.create(
model=model,
messages=messages
)
print(final_response.choices[0].message.content)
```
***
## Anthropic
### Simple
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
# pip install anthropic e2b-code-interpreter
from anthropic import Anthropic
from e2b_code_interpreter import Sandbox
# Create Anthropic client
anthropic = Anthropic()
system_prompt = "You are a helpful assistant that can execute python code in a Jupyter notebook. Only respond with the code to be executed and nothing else. Strip backticks in code blocks."
prompt = "Calculate how many r's are in the word 'strawberry'"
# Send messages to Anthropic API
response = anthropic.messages.create(
model="claude-3-5-sonnet-20240620",
max_tokens=1024,
messages=[
{"role": "assistant", "content": system_prompt},
{"role": "user", "content": prompt}
]
)
# Extract code from response
code = response.content[0].text
# Execute code in E2B Sandbox
with Sandbox.create() as sandbox:
execution = sandbox.run_code(code)
result = execution.logs.stdout
print(result)
```
### Function calling
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
# pip install anthropic e2b-code-interpreter
from anthropic import Anthropic
from e2b_code_interpreter import Sandbox
# Create Anthropic client
client = Anthropic()
model = "claude-3-5-sonnet-20240620"
# Define the messages
messages = [
{
"role": "user",
"content": "Calculate how many r's are in the word 'strawberry'"
}
]
# Define the tools
tools = [{
"name": "execute_python",
"description": "Execute python code in a Jupyter notebook cell and return (not print) the result",
"input_schema": {
"type": "object",
"properties": {
"code": {
"type": "string",
"description": "The python code to execute in a single cell"
}
},
"required": ["code"]
}
}]
# Generate text with Anthropic
message = client.messages.create(
model=model,
max_tokens=1024,
messages=messages,
tools=tools
)
# Append the response message to the messages list
messages.append({
"role": "assistant",
"content": message.content
})
# Execute the tool if it's called by the model
if message.stop_reason == "tool_use":
tool_use = next(block for block in message.content if block.type == "tool_use")
tool_name = tool_use.name
tool_input = tool_use.input
if tool_name == "execute_python":
with Sandbox.create() as sandbox:
code = tool_input['code']
execution = sandbox.run_code(code)
result = execution.text
# Append the tool result to the messages list
messages.append({
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": tool_use.id,
"content": result,
}
],
})
# Generate the final response
final_response = client.messages.create(
model=model,
max_tokens=1024,
messages=messages,
tools=tools
)
print(final_response.content[0].text)
```
***
## Mistral
### Simple
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
# pip install mistralai e2b-code-interpreter
import os
from mistralai import Mistral
from e2b_code_interpreter import Sandbox
# Create Mistral client
client = Mistral(api_key=os.environ["MISTRAL_API_KEY"])
system_prompt = "You are a helpful assistant that can execute python code in a Jupyter notebook. Only respond with the code to be executed and nothing else. Strip backticks in code blocks."
prompt = "Calculate how many r's are in the word 'strawberry'"
# Send the prompt to the model
response = client.chat.complete(
model="codestral-latest",
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": prompt}
]
)
# Extract the code from the response
code = response.choices[0].message.content
# Execute code in E2B Sandbox
with Sandbox.create() as sandbox:
execution = sandbox.run_code(code)
result = execution.text
print(result)
```
### Function calling
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
# pip install mistralai e2b-code-interpreter
import os
import json
from mistralai import Mistral
from e2b_code_interpreter import Sandbox
# Create Mistral client
client = Mistral(api_key=os.environ["MISTRAL_API_KEY"])
model = "mistral-large-latest"
messages = [
{
"role": "user",
"content": "Calculate how many r's are in the word 'strawberry'"
}
]
# Define the tools
tools = [{
"type": "function",
"function": {
"name": "execute_python",
"description": "Execute python code in a Jupyter notebook cell and return result",
"parameters": {
"type": "object",
"properties": {
"code": {
"type": "string",
"description": "The python code to execute in a single cell"
}
},
"required": ["code"]
}
}
}]
# Send the prompt to the model
response = client.chat.complete(
model=model,
messages=messages,
tools=tools
)
# Append the response message to the messages list
response_message = response.choices[0].message
messages.append(response_message)
# Execute the tool if it's called by the model
if response_message.tool_calls:
for tool_call in response_message.tool_calls:
if tool_call.function.name == "execute_python":
# Create a sandbox and execute the code
with Sandbox.create() as sandbox:
code = json.loads(tool_call.function.arguments)['code']
execution = sandbox.run_code(code)
result = execution.text
# Send the result back to the model
messages.append({
"role": "tool",
"name": "execute_python",
"content": result,
"tool_call_id": tool_call.id,
})
# Generate the final response
final_response = client.chat.complete(
model=model,
messages=messages,
)
print(final_response.choices[0].message.content)
```
***
## Groq
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
# pip install groq e2b-code-interpreter
import os
from groq import Groq
from e2b_code_interpreter import Sandbox
api_key = os.environ["GROQ_API_KEY"]
# Create Groq client
client = Groq(api_key=api_key)
system_prompt = "You are a helpful assistant that can execute python code in a Jupyter notebook. Only respond with the code to be executed and nothing else. Strip backticks in code blocks."
prompt = "Calculate how many r's are in the word 'strawberry.'"
# Send the prompt to the model
response = client.chat.completions.create(
model="llama3-70b-8192",
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": prompt},
]
)
# Extract the code from the response
code = response.choices[0].message.content
# Execute code in E2B Sandbox
with Sandbox.create() as sandbox:
execution = sandbox.run_code(code)
result = execution.text
print(result)
```
***
## Vercel AI SDK
Vercel's [AI SDK](https://sdk.vercel.ai) offers support for multiple different LLM providers through a unified JavaScript interface that's easy to use.
### Simple
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
// npm install ai @ai-sdk/openai @e2b/code-interpreter
import { openai } from '@ai-sdk/openai'
import { generateText } from 'ai'
import { Sandbox } from '@e2b/code-interpreter'
// Create OpenAI client
const model = openai('gpt-4o')
const system = "You are a helpful assistant that can execute python code in a Jupyter notebook. Only respond with the code to be executed and nothing else. Strip backticks in code blocks."
const prompt = "Calculate how many r's are in the word 'strawberry'"
// Generate code with OpenAI
const { text: code } = await generateText({
model,
system,
prompt
})
// Run the code in E2B Sandbox
const sandbox = await Sandbox.create()
const { text, results, logs, error } = await sandbox.runCode(code)
console.log(text)
```
### Function calling
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
// npm install ai @ai-sdk/openai zod @e2b/code-interpreter
import { openai } from '@ai-sdk/openai'
import { generateText } from 'ai'
import z from 'zod'
import { Sandbox } from '@e2b/code-interpreter'
// Create OpenAI client
const model = openai('gpt-4o')
const prompt = "Calculate how many r's are in the word 'strawberry'"
// Generate text with OpenAI
const { text } = await generateText({
model,
prompt,
tools: {
// Define a tool that runs code in a sandbox
execute_python: {
description: 'Execute python code in a Jupyter notebook cell and return result',
parameters: z.object({
code: z.string().describe('The python code to execute in a single cell'),
}),
execute: async ({ code }) => {
// Create a sandbox, execute LLM-generated code, and return the result
const sandbox = await Sandbox.create()
const { text, results, logs, error } = await sandbox.runCode(code)
return results
},
},
},
// This is required to feed the tool call result back to the LLM
maxSteps: 2
})
console.log(text)
```
***
## CrewAI
[CrewAI](https://crewai.com/) is a platform for building AI agents.
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
# pip install crewai e2b-code-interpreter
from crewai.tools import tool
from crewai import Agent, Task, Crew, LLM
from e2b_code_interpreter import Sandbox
# Update tool definition using the decorator
@tool("Python Interpreter")
def execute_python(code: str) -> str:
"""
Execute Python code and return the results.
"""
with Sandbox.create() as sandbox:
execution = sandbox.run_code(code)
return execution.text
# Define the agent
python_executor = Agent(
role='Python Executor',
goal='Execute Python code and return the results',
backstory='You are an expert Python programmer capable of executing code and returning results.',
tools=[execute_python],
llm=LLM(model="gpt-4o")
)
# Define the task
execute_task = Task(
description="Calculate how many r's are in the word 'strawberry'",
agent=python_executor,
expected_output="The number of r's in the word 'strawberry'"
)
# Create the crew
code_execution_crew = Crew(
agents=[python_executor],
tasks=[execute_task],
verbose=True,
)
# Run the crew
result = code_execution_crew.kickoff()
print(result)
```
***
## LangChain
[LangChain](https://langchain.com/) offers support multiple different LLM providers.
### Simple
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
# pip install langchain langchain-openai e2b-code-interpreter
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from e2b_code_interpreter import Sandbox
system_prompt = "You are a helpful assistant that can execute python code in a Jupyter notebook. Only respond with the code to be executed and nothing else. Strip backticks in code blocks."
prompt = "Calculate how many r's are in the word 'strawberry'"
# Create LangChain components
llm = ChatOpenAI(model="gpt-4o")
prompt_template = ChatPromptTemplate.from_messages([
("system", system_prompt),
("human", "{input}")
])
output_parser = StrOutputParser()
# Create the chain
chain = prompt_template | llm | output_parser
# Run the chain
code = chain.invoke({"input": prompt})
# Execute code in E2B Sandbox
with Sandbox.create() as sandbox:
execution = sandbox.run_code(code)
result = execution.text
print(result)
```
### Agent
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
# pip install langchain langchain-openai e2b-code-interpreter
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.tools import tool
from langchain.agents import create_tool_calling_agent, AgentExecutor
from langchain_openai import ChatOpenAI
from e2b_code_interpreter import Sandbox
system_prompt = "You are a helpful assistant that can execute python code in a Jupyter notebook. Only respond with the code to be executed and nothing else. Strip backticks in code blocks."
prompt = "Calculate how many r's are in the word 'strawberry'"
# Define the tool
@tool
def execute_python(code: str):
"""
Execute python code in a Jupyter notebook.
"""
with Sandbox.create() as sandbox:
execution = sandbox.run_code(code)
return execution.text
# Define LangChain components
prompt_template = ChatPromptTemplate.from_messages([
("system", system_prompt),
("human", "{input}"),
("placeholder", "{agent_scratchpad}"),
])
tools = [execute_python]
llm = ChatOpenAI(model="gpt-4o", temperature=0)
agent = create_tool_calling_agent(llm, tools, prompt_template)
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
# Run the agent
agent_executor.invoke({"input": prompt})
```
### Function calling
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
# pip install langchain langchain-openai e2b-code-interpreter
from langchain_openai import ChatOpenAI
from langchain.tools import Tool
from langchain.schema import HumanMessage, AIMessage, FunctionMessage
from e2b_code_interpreter import Sandbox
def execute_python(code: str):
with Sandbox.create() as sandbox:
execution = sandbox.run_code(code)
return execution.text
# Define a tool that uses the E2B Sandbox
e2b_sandbox_tool = Tool(
name="execute_python",
func=execute_python,
description="Execute python code in a Jupyter notebook cell and return result"
)
# Initialize the language model and bind the tool
llm = ChatOpenAI(model="gpt-4o").bind_tools([e2b_sandbox_tool])
# Define the messages
messages = [
HumanMessage(content="Calculate how many 'r's are in the word 'strawberry'.")
]
# Run the model with a prompt
result = llm.invoke(messages)
messages.append(AIMessage(content=result.content))
# Check if the model called the tool
if result.additional_kwargs.get('tool_calls'):
tool_call = result.additional_kwargs['tool_calls'][0]
if tool_call['function']['name'] == "execute_python":
code = tool_call['function']['arguments']
execution_result = execute_python(code)
# Send the result back to the model
messages.append(
FunctionMessage(name="execute_python", content=execution_result)
)
final_result = llm.invoke(messages)
print(final_result.content)
```
***
## LlamaIndex
[LlamaIndex](https://www.llamaindex.ai/) offers support multiple different LLM providers.
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
# pip install llama-index e2b-code-interpreter
from llama_index.core.tools import FunctionTool
from llama_index.llms.openai import OpenAI
from llama_index.core.agent import ReActAgent
from e2b_code_interpreter import Sandbox
# Define the tool
def execute_python(code: str):
with Sandbox.create() as sandbox:
execution = sandbox.run_code(code)
return execution.text
e2b_sandbox_tool = FunctionTool.from_defaults(
name="execute_python",
description="Execute python code in a Jupyter notebook cell and return result",
fn=execute_python
)
# Initialize LLM
llm = OpenAI(model="gpt-4o")
# Initialize ReAct agent
agent = ReActAgent.from_tools([e2b_sandbox_tool], llm=llm, verbose=True)
agent.chat("Calculate how many r's are in the word 'strawberry'")
```
## Ollama
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
# pip install ollama
import ollama
from e2b_code_interpreter import Sandbox
# Send the prompt to the model
response = ollama.chat(
model="llama3.2",
messages=[{
"role": "system",
"content": "You are a helpful assistant that can execute python code in a Jupyter notebook. Only respond with the code to be executed and nothing else. Strip backticks in code blocks."
},
{
"role": "user",
"content": "Calculate how many r's are in the word 'strawberry'"
}
])
# Extract the code from the response
code = response['message']['content']
# Execute code in E2B Sandbox
with Sandbox.create() as sandbox:
execution = sandbox.run_code(code)
result = execution.logs.stdout
print(result)
```
***
## Hugging Face
Hugging Face offers support for serverless inference for models on their model hub with [Hugging Face's Inference API](https://huggingface.co/docs/inference-providers/en/index).
Note that not every model on Hugging Face has native support for tool use and function calling.
````python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from huggingface_hub import InferenceClient
from e2b_code_interpreter import Sandbox
import re
# Not all models are capable of direct tools use - we need to extract the code block manually and prompting the LLM to generate the code.
def match_code_block(llm_response):
pattern = re.compile(r'```python Python\n(.*?)\n```', re.DOTALL) # Match everything in between ```python and ```
match = pattern.search(llm_response)
if match:
code = match.group(1)
print(code)
return code
return ""
system_prompt = """You are a helpful coding assistant that can execute python code in a Jupyter notebook. You are given tasks to complete and you run Python code to solve them.
Generally, you follow these rules:
- ALWAYS FORMAT YOUR RESPONSE IN MARKDOWN
- ALWAYS RESPOND ONLY WITH CODE IN CODE BLOCK LIKE THIS:
\`\`\`python
{code}
\`\`\`
"""
prompt = "Calculate how many r's are in the word 'strawberry.'"
# Initialize the client
client = InferenceClient(
provider="hf-inference",
api_key="HF_INFERENCE_API_KEY"
)
completion = client.chat.completions.create(
model="Qwen/Qwen3-235B-A22B", # Or use any other model from Hugging Face
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": prompt},
]
)
content = completion.choices[0].message.content
code = match_code_block(content)
with Sandbox.create() as sandbox:
execution = sandbox.run_code(code)
print(execution)
````
# Install custom packages
Source: https://e2b.mintlify.app/docs/quickstart/install-custom-packages
There are two ways to install custom packages in the E2B Sandbox.
1. [Create custom sandbox with preinstalled packages](#create-a-custom-sandbox).
2. [Install packages during the sandbox runtime](#install-packages-during-the-sandbox-runtime).
***
## Create a custom sandbox
Use this option if you know beforehand what packages you need in the sandbox.
Sandbox templates allow you to define custom sandboxes with preinstalled packages and configurations.
### 1. Install the E2B SDK
```bash JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
npm install e2b dotenv
```
```bash Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
pip install e2b python-dotenv
```
Create a `.env` file with your API key:
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
E2B_API_KEY=e2b_***
```
### 2. Create a template file
Define your custom template with the packages you need.
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
// template.ts
import { Template } from 'e2b';
export const template = Template()
.fromTemplate("code-interpreter-v1")
.pipInstall(['cowsay']) // Install Python packages
.npmInstall(['cowsay']); // Install Node.js packages
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
# template.py
from e2b import Template
template = (
Template()
.from_template("code-interpreter-v1")
.pip_install(['cowsay']) # Install Python packages
.npm_install(['cowsay']) # Install Node.js packages
)
```
### 3. Create a build script
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
// build.prod.ts
import "dotenv/config";
import { Template, defaultBuildLogger } from 'e2b';
import { template } from './template';
async function main() {
await Template.build(template, 'custom-packages', {
cpuCount: 2,
memoryMB: 2048,
onBuildLogs: defaultBuildLogger(),
});
}
main().catch(console.error);
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
# build.prod.py
from dotenv import load_dotenv
from e2b import Template, default_build_logger
from template import template
load_dotenv()
if __name__ == '__main__':
Template.build(
template,
'custom-packages',
cpu_count=2,
memory_mb=2048,
on_build_logs=default_build_logger(),
)
```
### 4. Build the template
Run the build script to create your custom template:
```bash JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
npx tsx build.prod.ts
```
```bash Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
python build.prod.py
```
This will build your template and you'll see build logs in the console.
### 5. Use your custom sandbox
Now you can create sandboxes from your custom template:
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const sbx = await Sandbox.create("custom-packages")
// Your packages are already installed and ready to use
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
sbx = Sandbox.create("custom-packages")
# Your packages are already installed and ready to use
```
***
## Install packages during the sandbox runtime
Use this option if don't know beforehand what packages you need in the sandbox. You can install packages with the package manager of your choice.
The packages installed during the runtime are available only in the running sandbox instance.
When you start a new sandbox instance, the packages are not be available.
### 1. Install Python packages with PIP
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from '@e2b/code-interpreter'
const sbx = await Sandbox.create()
sbx.commands.run('pip install cowsay') // This will install the cowsay package
sbx.runCode(`
import cowsay
cowsay.cow("Hello, world!")
`)
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b_code_interpreter import Sandbox
sbx = Sandbox.create()
sbx.commands.run("pip install cowsay") # This will install the cowsay package
sbx.run_code("""
import cowsay
cowsay.cow("Hello, world!")
""")
```
### 2. Install Node.js packages with NPM
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from '@e2b/code-interpreter'
const sbx = await Sandbox.create()
sbx.commands.run('npm install cowsay') // This will install the cowsay package
sbx.runCode(`
const cowsay = require('cowsay')
console.log(cowsay.say({ text: 'Hello, world!' }))
`, { language: 'javascript' })
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b_code_interpreter import Sandbox
sbx = Sandbox.create()
sbx.commands.run("npm install cowsay") # This will install the cowsay package
sbx.run_code("""
import { say } from 'cowsay'
console.log(say('Hello, world!'))
""", language="javascript")
```
### 3. Install packages with package manager of your choice
Since E2B Sandboxes are Debian based machines, you can use any package manager supported by Debian.
You just need to make sure that the package manager is already installed in the sandbox.
For example, to install `curl` and `git`, you can use the following commands:
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from '@e2b/code-interpreter'
const sbx = await Sandbox.create()
await sbx.commands.run('apt-get update && apt-get install -y curl git')
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b_code_interpreter import Sandbox
sbx = Sandbox.create()
sbx.commands.run("apt-get update && apt-get install -y curl git")
```
# Upload & downloads files
Source: https://e2b.mintlify.app/docs/quickstart/upload-download-files
E2B Sandbox allows you to upload and downloads file to and from the Sandbox.
An alternative way to get your data to the sandbox is to create a [custom sandbox template](/docs/template/quickstart).
## Upload file
```ts JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
// Read local file relative to the current working directory
const content = fs.readFileSync('local/file')
const sbx = await Sandbox.create()
// Upload file to the sandbox to absolute path '/home/user/my-file'
await sbx.files.write('/home/user/my-file', content)
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
sbx = Sandbox.create()
# Read local file relative to the current working directory
with open("local/file", "rb") as file:
# Upload file to the sandbox to absolute path '/home/user/my-file'
sbx.files.write("/home/user/my-file", file)
```
## Upload multiple files
Currently, if you want to upload multiple files, you need to upload each one of the separately.
We're working on a better solution.
```ts JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
// Read local file relative to the current working directory
const fileA = fs.readFileSync('local/file/a')
const fileB = fs.readFileSync('local/file/b')
const sbx = await Sandbox.create()
// Upload file A to the sandbox to absolute path '/home/user/my-file-a'
await sbx.files.write('/home/user/my-file-a', fileA)
// Upload file B to the sandbox to absolute path '/home/user/my-file-b'
await sbx.files.write('/home/user/my-file-b', fileB)
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
sbx = Sandbox.create()
# Read local file relative to the current working directory
with open("local/file/a", "rb") as file:
# Upload file to the sandbox to absolute path '/home/user/my-file-a'
sbx.files.write("/home/user/my-file-a", file)
with open("local/file/b", "rb") as file:
# Upload file to the sandbox to absolute path '/home/user/my-file-b'
sbx.files.write("/home/user/my-file-b", file)
```
## Upload directory
We currently don't support an easy way to upload a whole directory.
You need to upload each file separately.
We're working on a better solution.
***
## Download file
To download a file, you need to first get the file's content and then write it to a local file.
```ts JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const sbx = await Sandbox.create()
// Download file from the sandbox to absolute path '/home/user/my-file'
const content = await sbx.files.read('/home/user/my-file')
// Write file to local path relative to the current working directory
fs.writeFileSync('local/file', content)
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
sbx = Sandbox.create()
# Download file from the sandbox to absolute path '/home/user/my-file'
content = sbx.files.read('/home/user/my-file')
# Write file to local path relative to the current working directory
with open('local/file', 'w') as file:
file.write(content)
```
## Download multiple files
To download multiple files, you need to download each one of them separately from the sandbox.
We're working on a better solution.
```ts JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const sbx = await Sandbox.create()
// Download file A from the sandbox by absolute path '/home/user/my-file-a'
const contentA = await sbx.files.read('/home/user/my-file-a')
// Write file A to local path relative to the current working directory
fs.writeFileSync('local/file/a', contentA)
// Download file B from the sandbox by absolute path '/home/user/my-file-b'
const contentB = await sbx.files.read('/home/user/my-file-b')
// Write file B to local path relative to the current working directory
fs.writeFileSync('local/file/b', contentB)
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
sbx = Sandbox.create()
# Download file A from the sandbox by absolute path '/home/user/my-file-a'
contentA = sbx.files.read('/home/user/my-file-a')
# Write file A to local path relative to the current working directory
with open('local/file/a', 'w') as file:
file.write(contentA)
# Download file B from the sandbox by absolute path '/home/user/my-file-b'
contentB = sbx.files.read('/home/user/my-file-b')
# Write file B to local path relative to the current working directory
with open('local/file/b', 'w') as file:
file.write(contentB)
```
## Download directory
We currently don't support an easy way to download a whole directory.
You need to download each file separately.
We're working on a better solution.
# Sandbox lifecycle
Source: https://e2b.mintlify.app/docs/sandbox
Sandboxes stay running as long as you need them. When their timeout expires, they can automatically pause to save resources — preserving their full state so you can resume at any time. You can also configure an explicit timeout or shut down a sandbox manually.
Sandboxes can run continuously for up to 24 hours (Pro) or 1 hour (Base). For longer workloads, use [pause and resume](/docs/sandbox/persistence) — pausing resets the runtime window, and your sandbox's full state is preserved indefinitely.
```js JavaScript & TypeScript highlight={6} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
// Create sandbox with and keep it running for 60 seconds.
// 🚨 Note: The units are milliseconds.
const sandbox = await Sandbox.create({
timeoutMs: 60_000,
})
```
```python Python highlight={6} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
# Create sandbox with and keep it running for 60 seconds.
# 🚨 Note: The units are seconds.
sandbox = Sandbox.create(
timeout=60,
)
```
## Change sandbox timeout during runtime
You can change the sandbox timeout when it's running by calling the `setTimeout` method in JavaScript or `set_timeout` method in Python.
When you call the set timeout method, the sandbox timeout will be reset to the new value that you specified.
This can be useful if you want to extend the sandbox lifetime when it's already running.
You can for example start with a sandbox with 1 minute timeout and then periodically call set timeout every time user interacts with it in your app.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
// Create sandbox with and keep it running for 60 seconds.
const sandbox = await Sandbox.create({ timeoutMs: 60_000 })
// Change the sandbox timeout to 30 seconds.
// 🚨 The new timeout will be 30 seconds from now.
await sandbox.setTimeout(30_000)
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
# Create sandbox with and keep it running for 60 seconds.
sandbox = Sandbox.create(timeout=60)
# Change the sandbox timeout to 30 seconds.
# 🚨 The new timeout will be 30 seconds from now.
sandbox.set_timeout(30)
```
## Retrieve sandbox information
You can retrieve sandbox information like sandbox ID, template, metadata, started at/end at date by calling the `getInfo` method in JavaScript or `get_info` method in Python.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
// Create sandbox with and keep it running for 60 seconds.
const sandbox = await Sandbox.create({ timeoutMs: 60_000 })
// Retrieve sandbox information.
const info = await sandbox.getInfo()
console.log(info)
// {
// "sandboxId": "iiny0783cype8gmoawzmx-ce30bc46",
// "templateId": "rki5dems9wqfm4r03t7g",
// "name": "base",
// "metadata": {},
// "startedAt": "2025-03-24T15:37:58.076Z",
// "endAt": "2025-03-24T15:42:58.076Z"
// }
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
# Create sandbox with and keep it running for 60 seconds.
sandbox = Sandbox.create(timeout=60)
# Retrieve sandbox information.
info = sandbox.get_info()
print(info)
# SandboxInfo(sandbox_id='ig6f1yt6idvxkxl562scj-419ff533',
# template_id='u7nqkmpn3jjf1tvftlsu',
# name='base',
# metadata={},
# started_at=datetime.datetime(2025, 3, 24, 15, 42, 59, 255612, tzinfo=tzutc()),
# end_at=datetime.datetime(2025, 3, 24, 15, 47, 59, 255612, tzinfo=tzutc())
# )
```
## Shutdown sandbox
You can shutdown the sandbox any time even before the timeout is up by calling the `kill` method.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
// Create sandbox with and keep it running for 60 seconds.
const sandbox = await Sandbox.create({ timeoutMs: 60_000 })
// Shutdown the sandbox immediately.
await sandbox.kill()
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
# Create sandbox with and keep it running for 60 seconds.
sandbox = Sandbox.create(timeout=60)
# Shutdown the sandbox immediately.
sandbox.kill()
```
# AutoResume
Source: https://e2b.mintlify.app/docs/sandbox/auto-resume
Many workloads don't need a sandbox running all the time, but when they do need it, it should just work, whether it was paused or not.
`AutoResume` handles this automatically: a paused sandbox wakes up when activity arrives, so your code does not have to check or manage sandbox state.
Configure it through the `lifecycle` object when creating a sandbox.
## Configure lifecycle on create
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const sandbox = await Sandbox.create({
timeoutMs: 10 * 60 * 1000,
lifecycle: {
onTimeout: 'pause',
autoResume: true, // resume when activity arrives
},
})
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
sandbox = Sandbox.create(
timeout=10 * 60,
lifecycle={
"on_timeout": "pause",
"auto_resume": True, # resume when activity arrives
},
)
```
## Lifecycle options
* `onTimeout` / `on_timeout`
* `kill` (default): sandbox is terminated when timeout is reached
* `pause`: sandbox is paused when timeout is reached
* `autoResume` / `auto_resume`
* `false` (default): paused sandboxes do not auto-resume
* `true`: paused sandboxes auto-resume on activity
* `true` is valid only when `onTimeout`/`on_timeout` is `pause`
## Behavior summary
* Default behavior is equivalent to `onTimeout: "kill"` with `autoResume: false`.
* `onTimeout: "pause"` with `autoResume: false` gives auto-pause without auto-resume.
* `onTimeout: "pause"` with `autoResume: true` gives auto-pause with auto-resume.
* [`Sandbox.connect()`](/docs/sandbox/connect) can still be used to resume a paused sandbox manually.
If you use `autoResume: false`, resume explicitly with [`Sandbox.connect()`](/docs/sandbox/connect).
## Timeout after auto-resume
When a sandbox auto-resumes, it restarts with a **5-minute minimum timeout**. If you originally created the sandbox with a longer timeout, that value carries over.
The countdown begins from the moment the sandbox resumes, not from when it was first created.
For example, if you create a sandbox with a 2-minute timeout:
1. The sandbox runs for 2 minutes, then pauses.
2. Activity arrives and the sandbox auto-resumes.
3. A 5-minute timeout starts (the 5-minute minimum applies because the original timeout was shorter).
4. If no further activity resets the timeout, the sandbox pauses again after 5 minutes.
If you create a sandbox with a 1-hour timeout, the auto-resume timeout will also be 1 hour, since it exceeds the 5-minute minimum.
This cycle repeats every time the sandbox auto-resumes — the lifecycle configuration (`onTimeout: "pause"` + `autoResume: true`) is persistent across pause/resume cycles.
You can change the timeout after the sandbox resumes by calling `setTimeout`/`set_timeout`. See [Change sandbox timeout during runtime](/docs/sandbox#change-sandbox-timeout-during-runtime).
## What counts as activity
Auto-resume is triggered by the sandbox activity - that's both HTTP traffic and controlling the sandbox from the SDK.
That includes SDK operations like:
* `sandbox.commands.run(...)`
* `sandbox.files.read(...)`
* `sandbox.files.write(...)`
* opening a tunneled app URL or sending requests to a service running inside the sandbox
If a sandbox is paused and `autoResume` is enabled, the next supported operation resumes it automatically. You do not need to call [`Sandbox.connect()`](/docs/sandbox/connect) first.
### SDK example: pause, then read a file
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const sandbox = await Sandbox.create({
timeoutMs: 10 * 60 * 1000,
lifecycle: {
onTimeout: 'pause',
autoResume: true,
},
})
await sandbox.files.write('/home/user/hello.txt', 'hello from a paused sandbox')
await sandbox.pause()
const content = await sandbox.files.read('/home/user/hello.txt')
console.log(content)
console.log(`State after read: ${(await sandbox.getInfo()).state}`)
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
sandbox = Sandbox.create(
timeout=10 * 60,
lifecycle={
"on_timeout": "pause",
"auto_resume": True,
},
)
sandbox.files.write("/home/user/hello.txt", "hello from a paused sandbox")
sandbox.pause()
content = sandbox.files.read("/home/user/hello.txt")
print(content)
print(f"State after read: {sandbox.get_info().state}")
```
## Use cases
### Web and dev/preview servers
Use `onTimeout: "pause"` + `autoResume: true` so inbound traffic can wake a paused sandbox automatically.
This works for both:
* Basic web/API servers
* Dev or preview servers you open occasionally
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const sandbox = await Sandbox.create({
timeoutMs: 10 * 60 * 1000,
lifecycle: {
onTimeout: 'pause',
autoResume: true,
},
})
await sandbox.commands.run(
`python3 -m pip -q install 'flask>=2.2'`
)
await sandbox.files.write(
'/home/user/app.py',
[
'from flask import Flask',
'app = Flask(__name__)',
'@app.route("/")',
'def hello():',
' return "Hello, World!"',
'app.run(host="0.0.0.0", port=3000)',
'',
].join('\n')
)
await sandbox.commands.run(
'python3 -u /home/user/app.py > /home/user/flask.log 2>&1',
{ background: true }
)
await new Promise((resolve) => setTimeout(resolve, 1000))
const previewHost = sandbox.getHost(3000)
console.log(`Preview URL: https://${previewHost}`)
console.log(`Status before pause: ${(await sandbox.getInfo()).state}`)
await sandbox.pause()
console.log(`Status after pause: ${(await sandbox.getInfo()).state}`)
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import time
from e2b import Sandbox
sandbox = Sandbox.create(
timeout=10 * 60,
lifecycle={
"on_timeout": "pause",
"auto_resume": True,
},
)
sandbox.commands.run("python3 -m pip -q install 'flask>=2.2'")
sandbox.files.write(
"/home/user/app.py",
'from flask import Flask\n'
'app = Flask(__name__)\n'
'@app.route("/")\n'
'def hello():\n'
' return "Hello, World!"\n'
'app.run(host="0.0.0.0", port=3000)\n'
)
sandbox.commands.run(
"python3 -u /home/user/app.py > /home/user/flask.log 2>&1",
background=True,
)
time.sleep(1)
preview_host = sandbox.get_host(3000)
print(f"Preview URL: https://{preview_host}")
print(f"Status before pause: {sandbox.get_info().state}")
sandbox.pause()
print(f"Status after pause: {sandbox.get_info().state}")
```
### Agent/tool execution
For queued tasks or tool calls, create once and keep using the same sandbox handle. If it is paused, it will auto-resume when you run the next command.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
// One-time setup
const sandbox = await Sandbox.create({
timeoutMs: 5 * 60 * 1000,
lifecycle: {
onTimeout: 'pause',
autoResume: true,
},
})
// Later: called for each agent/tool task
async function runToolTask(command) {
const result = await sandbox.commands.run(command)
return result.stdout
}
console.log(await runToolTask('python -c "print(2 + 2)"'))
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
# One-time setup
sandbox = Sandbox.create(
timeout=5 * 60,
lifecycle={
"on_timeout": "pause",
"auto_resume": True,
},
)
# Later: called for each agent/tool task
def run_tool_task(command: str) -> str:
result = sandbox.commands.run(command)
return result.stdout
print(run_tool_task('python -c "print(2 + 2)"'))
```
### Per-user sandboxes
For multi-tenant apps, keep a map of sandbox IDs by user. On each request, connect to the user's existing sandbox (which auto-resumes if paused) or create a new one.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const userSandboxes = new Map() // userId → Sandbox
async function getSandbox(userId) {
let sandbox = userSandboxes.get(userId)
if (!sandbox) {
sandbox = await Sandbox.create({
timeoutMs: 5 * 60 * 1000,
lifecycle: {
onTimeout: 'pause',
autoResume: true,
},
})
userSandboxes.set(userId, sandbox)
}
return sandbox
}
// On each user request (auto-resumes if paused)
const sandbox = await getSandbox('user-123')
const result = await sandbox.commands.run('echo "Hello from your sandbox"')
console.log(result.stdout)
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
user_sandboxes: dict[str, Sandbox] = {} # user_id → Sandbox
def get_sandbox(user_id: str) -> Sandbox:
if user_id not in user_sandboxes:
user_sandboxes[user_id] = Sandbox.create(
timeout=5 * 60,
lifecycle={
"on_timeout": "pause",
"auto_resume": True,
},
)
return user_sandboxes[user_id]
# On each user request (auto-resumes if paused)
sandbox = get_sandbox("user-123")
result = sandbox.commands.run('echo "Hello from your sandbox"')
print(result.stdout)
```
## Cleanup
Auto-resume is persistent, meaning if your sandbox resumes and later times out again, it will pause again.
Each time the sandbox resumes, it gets a fresh timeout (at least 5 minutes, or longer if the original creation timeout exceeds that) — so the sandbox keeps cycling between running and paused as long as activity arrives.
If you call `.kill()`, the sandbox is permanently deleted and cannot be resumed.
# Connect to running sandbox
Source: https://e2b.mintlify.app/docs/sandbox/connect
If you have a running sandbox, you can connect to it using the `Sandbox.connect()` method and then start controlling it with our SDK.
This is useful if you want to, for example, reuse the same sandbox instance for the same user after a short period of inactivity.
## 1. Get the sandbox ID
To connect to a running sandbox, you first need to retrieve its ID. You can do this by calling the `Sandbox.list()` method.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const sbx = await Sandbox.create()
// Get all running sandboxes
const paginator = await Sandbox.list({
query: { state: ['running'] },
})
const runningSandboxes = await paginator.nextItems()
if (runningSandboxes.length === 0) {
throw new Error('No running sandboxes found')
}
// Get the ID of the sandbox you want to connect to
const sandboxId = runningSandboxes[0].sandboxId
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
sbx = Sandbox.create()
# Get all running sandboxes
paginator = Sandbox.list()
# Get the ID of the sandbox you want to connect to
running_sandboxes = paginator.next_items()
if len(running_sandboxes) == 0:
raise Exception("No running sandboxes found")
# Get the ID of the sandbox you want to connect to
sandbox_id = running_sandboxes[0].sandbox_id
```
## 2. Connect to the sandbox
Now that you have the sandbox ID, you can connect to the sandbox using the `Sandbox.connect()` method.
```js JavaScript & TypeScript highlight={3} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const sandbox = await Sandbox.connect(sandboxId)
// Now you can use the sandbox as usual
// ...
const result = await sandbox.commands.run("whoami")
console.log(`Running in sandbox ${sandbox.sandboxId} as "${result.stdout.trim()}"`)
```
```python Python highlight={3} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
sandbox = Sandbox.connect(sandbox_id)
# Now you can use the sandbox as usual
# ...
r = sandbox.commands.run("whoami")
print(f"Running in sandbox {sandbox.sandbox_id} as \"{r.stdout.strip()}\"")
```
# Connecting storage bucket to the sandbox
Source: https://e2b.mintlify.app/docs/sandbox/connect-bucket
To connect a bucket for storing data from the sandbox, we will use the FUSE file system to mount the bucket to the sandbox.
You will need to create a custom sandbox template with the FUSE file system installed. The guide for building a custom sandbox template can be found [here](/docs/template/quickstart).
## Google Cloud Storage
### Prerequisites
To use Google Cloud Storage, you'll need a bucket and a service account. You can create a service account [here](https://console.cloud.google.com/iam-admin/serviceaccounts) and a bucket [here](https://console.cloud.google.com/storage).
If you want to write to the bucket, make sure the service account has the `Storage Object User` role for this bucket.
You can find a guide on creating a service account key [here](https://cloud.google.com/iam/docs/keys-create-delete#iam-service-account-keys-create-console).
### Mounting the bucket
To use the Google Cloud Storage we need to install the `gcsfuse` package. There's simple [template](/docs/template/quickstart) that can be used to create a container with the `gcsfuse` installed.
```ts JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
const template = Template()
.fromTemplate("code-interpreter-v1")
.aptInstall(["gnupg", "lsb-release"])
.runCmd("lsb_release -c -s > /tmp/lsb_release")
.runCmd(
'GCSFUSE_REPO=$(cat /tmp/lsb_release) && echo "deb [signed-by=/usr/share/keyrings/cloud.google.asc] https://packages.cloud.google.com/apt gcsfuse-$GCSFUSE_REPO main" | sudo tee /etc/apt/sources.list.d/gcsfuse.list',
)
.runCmd(
"curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo tee /usr/share/keyrings/cloud.google.asc",
)
.aptInstall(["gcsfuse"])
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
template = (
Template()
.from_template("code-interpreter-v1")
.apt_install(["gnupg", "lsb-release"])
.run_cmd("lsb_release -c -s > /tmp/lsb_release")
.run_cmd(
'GCSFUSE_REPO=$(cat /tmp/lsb_release) && echo "deb [signed-by=/usr/share/keyrings/cloud.google.asc] https://packages.cloud.google.com/apt gcsfuse-$GCSFUSE_REPO main" | sudo tee /etc/apt/sources.list.d/gcsfuse.list'
)
.run_cmd(
"curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo tee /usr/share/keyrings/cloud.google.asc"
)
.apt_install(["gcsfuse"])
)
```
The bucket is mounted during the sandbox runtime using the `gcsfuse` command.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const sandbox = await Sandbox.create('')
await sandbox.files.makeDir('/home/user/bucket')
await sandbox.files.write('key.json', '')
await sandbox.commands.run('sudo gcsfuse --key-file /home/user/key.json /home/user/bucket')
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
sandbox = Sandbox.create("")
sandbox.files.make_dir("/home/user/bucket")
sandbox.files.write("key.json", "")
output = sandbox.commands.run(
"sudo gcsfuse --key-file /home/user/key.json /home/user/bucket"
)
```
### Flags
The complete list of flags is available [here](https://cloud.google.com/storage/docs/gcsfuse-cli#options).
### Allow the default user to access the files
To allow the default user to access the files, we can use the following flags:
```
-o allow_other -file-mode=777 -dir-mode=777
```
## Amazon S3
To use Amazon S3, we can use the `s3fs` package. The [template](/docs/template/quickstart) setup is similar to that of Google Cloud Storage.
```ts JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
const template = Template()
.fromImage("ubuntu:latest")
.aptInstall(["s3fs"])
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
template = (
Template()
.from_image("ubuntu:latest")
.apt_install(["s3fs"])
)
```
Similar to Google Cloud Storage, the bucket is mounted during the runtime of the sandbox. The `s3fs` command is used to mount the bucket to the sandbox.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const sandbox = await Sandbox.create('')
await sandbox.files.makeDir('/home/user/bucket')
// Create a file with the credentials
// If you use another path for the credentials you need to add the path in the command s3fs command
await sandbox.files.write('/root/.passwd-s3fs', ':')
await sandbox.commands.run('sudo chmod 600 /root/.passwd-s3fs')
await sandbox.commands.run('sudo s3fs /home/user/bucket')
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
sandbox = Sandbox.create("")
sandbox.files.make_dir("/home/user/bucket")
# Create a file with the credentials
# If you use another path for the credentials you need to add the path in the command s3fs command
sandbox.files.write("/root/.passwd-s3fs", ":")
sandbox.commands.run("sudo chmod 600 /root/.passwd-s3fs")
sandbox.commands.run("sudo s3fs /home/user/bucket")
```
### Flags
The complete list of flags is available [here](https://manpages.ubuntu.com/manpages/xenial/man1/s3fs.1.html).
### Allow the default user to access the files
To allow the default user to access the files, add the following flag:
```
-o allow_other
```
## Cloudflare R2
For Cloudflare R2, we can use a setup very similar to S3. The template remains the same as for S3. However, the mounting differs slightly; we need to specify the endpoint for R2.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const sandbox = await Sandbox.create({ template: '' })
await sandbox.files.makeDir('/home/user/bucket')
// Create a file with the R2 credentials
// If you use another path for the credentials you need to add the path in the command s3fs command
await sandbox.files.write('/root/.passwd-s3fs', ':')
await sandbox.commands.run('sudo chmod 600 /root/.passwd-s3fs')
await sandbox.commands.run('sudo s3fs -o url=https://.r2.cloudflarestorage.com /home/user/bucket')
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
sandbox = Sandbox.create("")
sandbox.files.make_dir("/home/user/bucket")
# Create a file with the R2 credentials
# If you use another path for the credentials you need to add the path in the command s3fs command
sandbox.files.write("/root/.passwd-s3fs", ":")
sandbox.commands.run("sudo chmod 600 /root/.passwd-s3fs")
sandbox.commands.run(
"sudo s3fs -o url=https://.r2.cloudflarestorage.com /home/user/bucket"
)
```
### Flags
It's the same as for S3.
# Custom domain
Source: https://e2b.mintlify.app/docs/sandbox/custom-domain
How to set up a custom domain for Sandboxes hosted on E2B.
We will set up a GCP VM running Caddy server with Docker and Cloudflare DNS to proxy the requests to the Sandboxes.
Example: `8080-sandboxid.mydomain.com` -> `8080-sandboxid.e2b.app`
### Prerequisites
* Domain name registered and configured with Cloudflare DNS.
* Cloudflare API Token that allows you to manage DNS records.
### GCP VM setup
1. Create a VM instance by running the following command:
Replace `your-project-id` with your actual project ID.
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
gcloud compute instances create e2b-custom-domain-proxy \
--project=your-project-id \
--zone=us-west1-a \
--machine-type=n2-standard-2 \
--can-ip-forward \
--tags=http-server,https-server \
--image-project=debian-cloud \
--image-family=debian-12 \
--boot-disk-size=20GB
```
2. After the VM is created, you can connect to it using the following command:
Replace `your-project-id` with your actual project ID.
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
# May take a few seconds until the instance is ready to accept SSH connections
gcloud compute ssh e2b-custom-domain-proxy \
--project=your-project-id \
--zone=us-west1-a
```
### Server setup
1. Install the latest stable version of Docker:
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
curl -fsSL https://get.docker.com | sudo sh
```
2. Create a Dockerfile that will be used to build the Caddy server image with Cloudflare DNS:
```Dockerfile Dockerfile theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
FROM caddy:builder AS builder
RUN xcaddy build \
--with github.com/caddy-dns/cloudflare
FROM caddy:latest
COPY --from=builder /usr/bin/caddy /usr/bin/caddy
```
3. Create a Docker Compose file that will be used to start the Caddy server:
```yaml docker-compose.yml theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
services:
caddy:
build:
context: .
dockerfile: Dockerfile
container_name: caddy-proxy
restart: unless-stopped
ports:
- "80:80"
- "443:443"
- "443:443/udp" # Optional: HTTP/3
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile
- caddy_data:/data
- caddy_config:/config
environment:
- CLOUDFLARE_API_TOKEN=${CLOUDFLARE_API_TOKEN}
networks:
- caddy_network
volumes:
caddy_data:
caddy_config:
networks:
caddy_network:
driver: bridge
```
4. Create a Caddyfile for proxying the requests to the Sandboxes:
Replace `*.mydomain.com` with your actual wildcard domain name.
```Caddyfile Caddyfile theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
*.mydomain.com {
# Use Cloudflare DNS for ACME challenge
tls {
dns cloudflare {env.CLOUDFLARE_API_TOKEN}
}
# Capture sandboxId for reuse
# {labels.N} splits the host by "." and indexes from right to left:
# e.g., for "abc123.mydomain.com":
# {labels.0} = "com" (TLD)
# {labels.1} = "mydomain" (domain)
# {labels.2} = "abc123" (subdomain)
vars sandboxId {labels.2}
# Reverse proxy to corresponding e2b.app Sandbox
reverse_proxy {vars.sandboxId}.e2b.app:443 {
# Set the Host header to the e2b.app domain
header_up Host {vars.sandboxId}.e2b.app
# Forward real IP
header_up X-Real-IP {remote_host}
header_up X-Forwarded-For {remote_host}
header_up X-Forwarded-Proto {scheme}
header_up X-Forwarded-Host {host}
# Use HTTPS to upstream
transport http {
tls
tls_server_name {vars.sandboxId}.e2b.app
}
}
# Optional: Add logging
log {
output file /var/log/caddy/access.log
format json
}
}
```
5. Create a .env file that will be used to store the Cloudflare API Token:
```env .env theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
CLOUDFLARE_API_TOKEN=your-cloudflare-api-token
```
6. Build and start the Caddy server:
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
docker compose build
docker compose up -d
```
### Domain setup
Log into the Cloudflare dashboard and create a new A wildcard DNS record pointing to the IP address of the GCP VM.
Replace `GCP_VM_IP` with the IP address of the GCP VM.
It may take a few minutes for the DNS record to propagate and for the certificate to be issued.
If you have existing AAAA (IPv6) records for this domain name, make sure they are either removed or updated to point to the GCP VM.
```txt DNS theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
*.mydomain.com A GCP_VM_IP
```
### Testing the setup
We will create a new Sandbox on E2B and install a simple HTTP server in it.
1. Create a new Sandbox using [E2B CLI](/docs/cli):
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
e2b sandbox create base
```
2. Install and run a simple HTTP server in the sandbox:
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
sudo apt install nginx
sudo systemctl start nginx
```
3. Visit the sandbox URL in your browser: `https://80-sandboxid.mydomain.com`. You should see the default nginx welcome page.
# Environment variables
Source: https://e2b.mintlify.app/docs/sandbox/environment-variables
This page covers how to set and use environment variables in a sandbox, and default environment variables inside the sandbox.
## Default environment variables
### Knowing if you are inside a sandbox
Sometimes it's useful to know if the code is running inside a sandbox. Upon creating a sandbox, useful sandbox metadata is set as environment variables for commands:
* `E2B_SANDBOX` is set to `true` for processes to know if they are inside our VM.
* `E2B_SANDBOX_ID` to know the ID of the sandbox.
* `E2B_TEAM_ID` to know the team ID that created the sandbox.
* `E2B_TEMPLATE_ID` to know what template was used for the current sandbox.
You can try it out by running the following code in the sandbox:
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
const sandbox = await Sandbox.create()
const result = await sandbox.commands.run('echo $E2B_SANDBOX_ID')
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
sandbox = Sandbox.create()
result = sandbox.commands.run("echo $E2B_SANDBOX_ID")
```
These default environment variables are only accessible via the SDK, when using the CLI you can find them in the form of dot files in the `/run/e2b/` dir:
```sh theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
user@e2b:~$ ls -a /run/e2b/
.E2B_SANDBOX .E2B_SANDBOX_ID .E2B_TEAM_ID .E2B_TEMPLATE_ID
```
***
## Setting environment variables
There are 3 ways to set environment variables in a sandbox:
1. [Global environment variables when creating the sandbox](/docs/sandbox/environment-variables#1-global-environment-variables).
2. [When running code in the sandbox](/docs/sandbox/environment-variables#2-setting-environment-variables-when-running-code).
3. [When running commands in the sandbox](/docs/sandbox/environment-variables#3-setting-environment-variables-when-running-commands).
### 1. Global environment variables
You can set global environment variables when creating a sandbox.
```js JavaScript & TypeScript highlight={2-4} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
const sandbox = await Sandbox.create({
envs: {
MY_VAR: 'my_value',
},
})
```
```python Python highlight={2-4} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
sandbox = Sandbox.create(
envs={
'MY_VAR': 'my_value',
},
)
```
### 2. Setting environment variables when running code
You can set environment variables for specific code execution call in the sandbox.
* These environment variables are scoped to the command but are not private in the OS.
* If you had a global environment variable with the same name, it will be overridden only for the command.
```js JavaScript & TypeScript highlight={3-5} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
const sandbox = await Sandbox.create()
const result = await sandbox.runCode('import os; print(os.environ.get("MY_VAR"))', {
envs: {
MY_VAR: 'my_value',
},
})
```
```python Python highlight={4-6} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
sandbox = Sandbox.create()
result = sandbox.run_code(
'import os; print(os.environ.get("MY_VAR"))',
envs={
'MY_VAR': 'my_value'
}
)
```
### 3. Setting environment variables when running commands
You can set environment variables for specific command execution in the sandbox.
* These environment variables are scoped to the command but are not private in the OS.
* If you had a global environment variable with the same name, it will be overridden only for the command.
```js JavaScript & TypeScript highlight={3-5} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
const sandbox = await Sandbox.create()
sandbox.commands.run('echo $MY_VAR', {
envs: {
MY_VAR: '123',
},
})
```
```python Python highlight={4-6} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
sandbox = Sandbox.create()
sandbox.commands.run(
'echo $MY_VAR',
envs={
'MY_VAR': '123'
}
)
```
# Git integration
Source: https://e2b.mintlify.app/docs/sandbox/git-integration
Clone repositories, manage branches, and push changes using the sandbox.git methods.
Use the `sandbox.git` methods to run common git operations inside a sandbox.
### Authentication and Identity
#### Passing credentials inline
For private repositories over HTTP(S), pass `username` and `password` (token) directly to commands that need authentication. A username is required whenever you pass a password/token.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
const repoPath = '/home/user/repo'
await sandbox.git.push(repoPath, {
username: process.env.GIT_USERNAME,
password: process.env.GIT_TOKEN,
})
await sandbox.git.pull(repoPath, {
username: process.env.GIT_USERNAME,
password: process.env.GIT_TOKEN,
})
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import os
repo_path = "/home/user/repo"
sandbox.git.push(
repo_path,
username=os.environ.get("GIT_USERNAME"),
password=os.environ.get("GIT_TOKEN"),
)
sandbox.git.pull(
repo_path,
username=os.environ.get("GIT_USERNAME"),
password=os.environ.get("GIT_TOKEN"),
)
```
#### Credential helper (authenticate once)
To avoid passing credentials on each command, store them in the git credential helper inside the sandbox using `dangerouslyAuthenticate()` / `dangerously_authenticate()`.
Stores credentials on disk inside the sandbox. Any process or agent with access to the sandbox can read them. Use only when you understand the risk.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
// Default (GitHub)
await sandbox.git.dangerouslyAuthenticate({
username: process.env.GIT_USERNAME,
password: process.env.GIT_TOKEN,
})
// Custom host (self-hosted)
await sandbox.git.dangerouslyAuthenticate({
username: process.env.GIT_USERNAME,
password: process.env.GIT_TOKEN,
host: 'git.example.com',
protocol: 'https',
})
// After this, HTTPS git operations use the stored credentials
await sandbox.git.clone('https://git.example.com/org/repo.git', { path: '/home/user/repo' })
await sandbox.git.push('/home/user/repo')
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import os
# Default (GitHub)
sandbox.git.dangerously_authenticate(
username=os.environ.get("GIT_USERNAME"),
password=os.environ.get("GIT_TOKEN"),
)
# Custom host (self-hosted)
sandbox.git.dangerously_authenticate(
username=os.environ.get("GIT_USERNAME"),
password=os.environ.get("GIT_TOKEN"),
host="git.example.com",
protocol="https",
)
# After this, HTTPS git operations use the stored credentials
sandbox.git.clone("https://git.example.com/org/repo.git", path="/home/user/repo")
sandbox.git.push("/home/user/repo")
```
#### Keep credentials in the remote URL
By default, credentials are stripped from the remote URL after cloning. To keep credentials in the remote URL (stored in `.git/config`), set `dangerouslyStoreCredentials` / `dangerously_store_credentials`.
Storing credentials in the remote URL persists them in the repo config. Any process or agent with access to the sandbox can read them. Only use this when required.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
// Default: credentials are stripped from the remote URL
await sandbox.git.clone('https://git.example.com/org/repo.git', {
path: '/home/user/repo',
username: process.env.GIT_USERNAME,
password: process.env.GIT_TOKEN,
})
// Keep credentials in the remote URL
await sandbox.git.clone('https://git.example.com/org/repo.git', {
path: '/home/user/repo',
username: process.env.GIT_USERNAME,
password: process.env.GIT_TOKEN,
dangerouslyStoreCredentials: true,
})
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import os
# Default: credentials are stripped from the remote URL
sandbox.git.clone(
"https://git.example.com/org/repo.git",
path="/home/user/repo",
username=os.environ.get("GIT_USERNAME"),
password=os.environ.get("GIT_TOKEN"),
)
# Keep credentials in the remote URL
sandbox.git.clone(
"https://git.example.com/org/repo.git",
path="/home/user/repo",
username=os.environ.get("GIT_USERNAME"),
password=os.environ.get("GIT_TOKEN"),
dangerously_store_credentials=True,
)
```
#### Configure git identity
Set the git author name and email for commits. Configure globally or per-repository.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
const repoPath = '/home/user/repo'
// Global config
await sandbox.git.configureUser('E2B Bot', 'bot@example.com')
// Repo-local config
await sandbox.git.configureUser('E2B Bot', 'bot@example.com', {
scope: 'local',
path: repoPath
})
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
repo_path = "/home/user/repo"
# Global config
sandbox.git.configure_user("E2B Bot", "bot@example.com")
# Repo-local config
sandbox.git.configure_user(
"E2B Bot",
"bot@example.com",
scope="local",
path=repo_path
)
```
### Clone a repository
See [Authentication and Identity](#authentication-and-identity) for how to authenticate with private repositories.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
const repoUrl = 'https://git.example.com/org/repo.git'
const repoPath = '/home/user/repo'
// Default clone
await sandbox.git.clone(repoUrl, { path: repoPath })
// Clone a specific branch
await sandbox.git.clone(repoUrl, { path: repoPath, branch: 'main' })
// Shallow clone
await sandbox.git.clone(repoUrl, { path: repoPath, depth: 1 })
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
repo_url = "https://git.example.com/org/repo.git"
repo_path = "/home/user/repo"
# Default clone
sandbox.git.clone(repo_url, path=repo_path)
# Clone a specific branch
sandbox.git.clone(repo_url, path=repo_path, branch="main")
# Shallow clone
sandbox.git.clone(repo_url, path=repo_path, depth=1)
```
### Check status and branches
`status()` returns a structured object with branch, ahead/behind, and file status details. `branches()` returns the branch list and the current branch.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
const repoPath = '/home/user/repo'
const status = await sandbox.git.status(repoPath)
console.log(status.currentBranch, status.ahead, status.behind)
console.log(status.fileStatus)
const branches = await sandbox.git.branches(repoPath)
console.log(branches.currentBranch)
console.log(branches.branches)
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
repo_path = "/home/user/repo"
status = sandbox.git.status(repo_path)
print(status.current_branch, status.ahead, status.behind)
print(status.file_status)
branches = sandbox.git.branches(repo_path)
print(branches.current_branch)
print(branches.branches)
```
### Create and manage branches
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
const repoPath = '/home/user/repo'
// Create and switch to a new branch
await sandbox.git.createBranch(repoPath, 'feature/new-docs')
// Check out an existing branch
await sandbox.git.checkoutBranch(repoPath, 'main')
// Delete a branch
await sandbox.git.deleteBranch(repoPath, 'feature/old-docs')
// Force delete a branch
await sandbox.git.deleteBranch(repoPath, 'feature/stale-docs', { force: true })
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
repo_path = "/home/user/repo"
# Create and switch to a new branch
sandbox.git.create_branch(repo_path, "feature/new-docs")
# Check out an existing branch
sandbox.git.checkout_branch(repo_path, "main")
# Delete a branch
sandbox.git.delete_branch(repo_path, "feature/old-docs")
# Force delete a branch
sandbox.git.delete_branch(repo_path, "feature/stale-docs", force=True)
```
### Stage and commit
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
const repoPath = '/home/user/repo'
// Default: stage all changes, commit with repo config
await sandbox.git.add(repoPath)
await sandbox.git.commit(repoPath, 'Initial commit')
// Stage specific files
await sandbox.git.add(repoPath, { files: ['README.md', 'src/index.ts'] })
// Allow empty commit and override author
await sandbox.git.commit(repoPath, 'Docs sync', {
authorName: 'E2B Bot',
authorEmail: 'bot@example.com',
allowEmpty: true,
})
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
repo_path = "/home/user/repo"
# Default: stage all changes, commit with repo config
sandbox.git.add(repo_path)
sandbox.git.commit(repo_path, "Initial commit")
# Stage specific files
sandbox.git.add(repo_path, files=["README.md", "src/index.ts"])
# Allow empty commit and override author
sandbox.git.commit(
repo_path,
"Docs sync",
author_name="E2B Bot",
author_email="bot@example.com",
allow_empty=True,
)
```
### Pull and push
See [Authentication and Identity](#authentication-and-identity) for how to authenticate with private repositories.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
const repoPath = '/home/user/repo'
// Default (uses upstream when set)
await sandbox.git.push(repoPath)
await sandbox.git.pull(repoPath)
// Target a specific remote/branch and set upstream
await sandbox.git.push(repoPath, {
remote: 'origin',
branch: 'main',
setUpstream: true,
})
await sandbox.git.pull(repoPath, {
remote: 'origin',
branch: 'main',
})
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
repo_path = "/home/user/repo"
# Default (uses upstream when set)
sandbox.git.push(repo_path)
sandbox.git.pull(repo_path)
# Target a specific remote/branch and set upstream
sandbox.git.push(
repo_path,
remote="origin",
branch="main",
set_upstream=True,
)
sandbox.git.pull(
repo_path,
remote="origin",
branch="main",
)
```
### Manage remotes
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
const repoPath = '/home/user/repo'
const repoUrl = 'https://git.example.com/org/repo.git'
// Default
await sandbox.git.remoteAdd(repoPath, 'origin', repoUrl)
// Fetch after adding the remote
await sandbox.git.remoteAdd(repoPath, 'origin', repoUrl, { fetch: true })
// Overwrite the remote URL if it already exists
await sandbox.git.remoteAdd(repoPath, 'origin', repoUrl, { overwrite: true })
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
repo_path = "/home/user/repo"
repo_url = "https://git.example.com/org/repo.git"
# Default
sandbox.git.remote_add(repo_path, "origin", repo_url)
# Fetch after adding the remote
sandbox.git.remote_add(repo_path, "origin", repo_url, fetch=True)
# Overwrite the remote URL if it already exists
sandbox.git.remote_add(repo_path, "origin", repo_url, overwrite=True)
```
### Git config
Set and get git configuration values. See [Configure git identity](#configure-git-identity) for configuring the commit author.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
const repoPath = '/home/user/repo'
// Global config
await sandbox.git.setConfig('pull.rebase', 'false')
const rebase = await sandbox.git.getConfig('pull.rebase')
// Repo-local config
await sandbox.git.setConfig('pull.rebase', 'false', { scope: 'local', path: repoPath })
const localRebase = await sandbox.git.getConfig('pull.rebase', { scope: 'local', path: repoPath })
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
repo_path = "/home/user/repo"
# Global config
sandbox.git.set_config("pull.rebase", "false")
rebase = sandbox.git.get_config("pull.rebase")
# Repo-local config
sandbox.git.set_config("pull.rebase", "false", scope="local", path=repo_path)
local_rebase = sandbox.git.get_config("pull.rebase", scope="local", path=repo_path)
```
# Internet access
Source: https://e2b.mintlify.app/docs/sandbox/internet-access
Every sandbox has access to the internet and can be reached by a public URL.
## Controlling internet access
You can control whether a sandbox has access to the internet by using the `allowInternetAccess` parameter when creating a sandbox. By default, internet access is enabled (`true`), but you can disable it for security-sensitive workloads.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
// Create sandbox with internet access enabled (default)
const sandbox = await Sandbox.create({ allowInternetAccess: true })
// Create sandbox without internet access
const isolatedSandbox = await Sandbox.create({ allowInternetAccess: false })
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
# Create sandbox with internet access enabled (default)
sandbox = Sandbox.create(allow_internet_access=True)
# Create sandbox without internet access
isolated_sandbox = Sandbox.create(allow_internet_access=False)
```
When internet access is disabled, the sandbox cannot make outbound network connections, which provides an additional layer of security for sensitive code execution.
Setting `allowInternetAccess` to `false` is equivalent to setting `network.denyOut` to `['0.0.0.0/0']` (denying all traffic).
## Fine-grained network control
For more granular control over network access, you can use the `network` configuration option to specify allow and deny lists for outbound traffic.
### Allow and deny lists
You can specify IP addresses, CIDR blocks, or domain names that the sandbox is allowed to use:
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox, ALL_TRAFFIC } from 'e2b'
// Deny all traffic except specific IPs
const sandbox = await Sandbox.create({
network: {
denyOut: [ALL_TRAFFIC],
allowOut: ['1.1.1.1', '8.8.8.0/24']
}
})
// Deny specific IPs only
const restrictedSandbox = await Sandbox.create({
network: {
denyOut: ['8.8.8.8']
}
})
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox, ALL_TRAFFIC
# Deny all traffic except specific IPs
sandbox = Sandbox.create(
network={
"deny_out": [ALL_TRAFFIC],
"allow_out": ["1.1.1.1", "8.8.8.0/24"]
}
)
# Deny specific IPs only
restricted_sandbox = Sandbox.create(
network={
"deny_out": ["8.8.8.8"]
}
)
```
### Domain-based filtering
You can allow traffic to specific domains by specifying hostnames in `allow out`. When using domain-based filtering, you must include `ALL_TRAFFIC` in `deny out` to block all other traffic. Domains are not supported in the `deny out` list.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox, ALL_TRAFFIC } from 'e2b'
// Allow only traffic to google.com
const sandbox = await Sandbox.create({
network: {
allowOut: ['google.com'],
denyOut: [ALL_TRAFFIC]
}
})
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox, ALL_TRAFFIC
# Allow only traffic to google.com
sandbox = Sandbox.create(
network={
"allow_out": ["google.com"],
"deny_out": [ALL_TRAFFIC]
}
)
```
When any domain is used, the default nameserver `8.8.8.8` is automatically allowed to ensure proper DNS resolution.
You can also use wildcards to allow all subdomains of a domain:
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox, ALL_TRAFFIC } from 'e2b'
// Allow traffic to any subdomain of mydomain.com
const sandbox = await Sandbox.create({
network: {
allowOut: ['*.mydomain.com'],
denyOut: [ALL_TRAFFIC]
}
})
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox, ALL_TRAFFIC
# Allow traffic to any subdomain of mydomain.com
sandbox = Sandbox.create(
network={
"allow_out": ["*.mydomain.com"],
"deny_out": [ALL_TRAFFIC]
}
)
```
You can combine domain names with IP addresses and CIDR blocks:
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox, ALL_TRAFFIC } from 'e2b'
// Allow traffic to specific domains and IPs
const sandbox = await Sandbox.create({
network: {
allowOut: ['api.example.com', '*.github.com', '8.8.8.8'],
denyOut: [ALL_TRAFFIC]
}
})
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox, ALL_TRAFFIC
# Allow traffic to specific domains and IPs
sandbox = Sandbox.create(
network={
"allow_out": ["api.example.com", "*.github.com", "8.8.8.8"],
"deny_out": [ALL_TRAFFIC]
}
)
```
Domain-based filtering only works for HTTP traffic on port 80 (via Host header inspection) and TLS traffic on port 443 (via SNI inspection). Traffic on other ports uses CIDR-based filtering only. UDP-based protocols like QUIC/HTTP3 are not supported for domain filtering.
### Priority rules
When both `allow out` and `deny out` are specified, **allow rules always take precedence** over deny rules. This means if an IP address is in both lists, it will be allowed.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox, ALL_TRAFFIC } from 'e2b'
// Even though ALL_TRAFFIC is denied, 1.1.1.1 and 8.8.8.8 are explicitly allowed
const sandbox = await Sandbox.create({
network: {
denyOut: [ALL_TRAFFIC],
allowOut: ['1.1.1.1', '8.8.8.8']
}
})
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox, ALL_TRAFFIC
# Even though ALL_TRAFFIC is denied, 1.1.1.1 and 8.8.8.8 are explicitly allowed
sandbox = Sandbox.create(
network={
"deny_out": [ALL_TRAFFIC],
"allow_out": ["1.1.1.1", "8.8.8.8"]
}
)
```
### ALL\_TRAFFIC helper
The `ALL_TRAFFIC` constant represents the CIDR range `0.0.0.0/0`, which matches all IP addresses. Use it to easily deny or allow all network traffic:
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox, ALL_TRAFFIC } from 'e2b'
// Deny all outbound traffic
const sandbox = await Sandbox.create({
network: {
denyOut: [ALL_TRAFFIC]
}
})
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox, ALL_TRAFFIC
# Deny all outbound traffic
sandbox = Sandbox.create(
network={
"deny_out": [ALL_TRAFFIC]
}
)
```
## Sandbox public URL
Every sandbox has a public URL that can be used to access running services inside the sandbox.
```js JavaScript & TypeScript highlight={6} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const sandbox = await Sandbox.create()
// You need to always pass a port number to get the host
const host = sandbox.getHost(3000)
console.log(`https://${host}`)
```
```python Python highlight={6} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
sandbox = Sandbox.create()
# You need to always pass a port number to get the host
host = sandbox.get_host(3000)
print(f'https://{host}')
```
The code above will print something like this:
```bash JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
https://3000-i62mff4ahtrdfdkyn2esc.e2b.app
```
```bash Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
https://3000-i62mff4ahtrdfdkyn2esc.e2b.app
```
The first leftmost part of the host is the port number we passed to the method.
## Restricting public access to sandbox URLs
By default, sandbox URLs are publicly accessible. You can restrict access to require authentication using the `allowPublicTraffic` option:
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
// Create sandbox with restricted public access
const sandbox = await Sandbox.create({
network: {
allowPublicTraffic: false
}
})
// The sandbox has a traffic access token
console.log(sandbox.trafficAccessToken)
// Start a server inside the sandbox
await sandbox.commands.run('python -m http.server 8080', { background: true })
const host = sandbox.getHost(8080)
const url = `https://${host}`
// Request without token will fail with 403
const response1 = await fetch(url)
console.log(response1.status) // 403
// Request with token will succeed
const response2 = await fetch(url, {
headers: {
'e2b-traffic-access-token': sandbox.trafficAccessToken
}
})
console.log(response2.status) // 200
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import requests
from e2b import Sandbox
# Create sandbox with restricted public access
sandbox = Sandbox.create(
network={
"allow_public_traffic": False
}
)
# The sandbox has a traffic access token
print(sandbox.traffic_access_token)
# Start a server inside the sandbox
sandbox.commands.run("python -m http.server 8080", background=True)
host = sandbox.get_host(8080)
url = f"https://{host}"
# Request without token will fail with 403
response1 = requests.get(url)
print(response1.status_code) # 403
# Request with token will succeed
response2 = requests.get(url, headers={
'e2b-traffic-access-token': sandbox.traffic_access_token
})
print(response2.status_code) # 200
```
When `allowPublicTraffic` is set to `false`, all requests to the sandbox's public URLs must include the `e2b-traffic-access-token` header with the value from `sandbox.trafficAccessToken`.
## Connecting to a server running inside the sandbox
You can start a server inside the sandbox and connect to it using the approach above.
In this example we will start a simple HTTP server that listens on port 3000 and responds with the content of the directory where the server is started.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const sandbox = await Sandbox.create()
// Start a simple HTTP server inside the sandbox.
const process = await sandbox.commands.run('python -m http.server 3000', { background: true })
const host = sandbox.getHost(3000)
const url = `https://${host}`
console.log('Server started at:', url)
// Fetch data from the server inside the sandbox.
const response = await fetch(url);
const data = await response.text();
console.log('Response from server inside sandbox:', data);
// Kill the server process inside the sandbox.
await process.kill()
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import requests
from e2b import Sandbox
sandbox = Sandbox.create()
# Start a simple HTTP server inside the sandbox.
process = sandbox.commands.run("python -m http.server 3000", background=True)
host = sandbox.get_host(3000)
url = f"https://{host}"
print('Server started at:', url)
# Fetch data from the server inside the sandbox.
response = requests.get(url)
data = response.text
print('Response from server inside sandbox:', data)
# Kill the server process inside the sandbox.
process.kill()
```
This output will look like this:
```bash JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
Server started at: https://3000-ip3nfrvajtqu5ktoxugc7.e2b.app
Response from server inside sandbox:
Directory listing for /
```
```bash Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
Server started at: https://3000-ip3nfrvajtqu5ktoxugc7.e2b.app
Response from server inside sandbox:
Directory listing for /
```
## Masking request host headers
You can customize the `Host` header that gets sent to services running inside the sandbox using the `maskRequestHost` option. This is useful when your application expects a specific host format.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
// Create sandbox with custom host masking
const sandbox = await Sandbox.create({
network: {
maskRequestHost: 'localhost:${PORT}'
}
})
// The ${PORT} variable will be replaced with the actual port number
// Requests to the sandbox will have Host header set to for example: localhost:8080
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
# Create sandbox with custom host masking
sandbox = Sandbox.create(
network={
"mask_request_host": "localhost:${PORT}"
}
)
# The ${PORT} variable will be replaced with the actual port number
# Requests to the sandbox will have Host header set to for example: localhost:8080
```
The `${PORT}` variable in the mask will be automatically replaced with the actual port number of the requested service.
# Proxy tunneling
Source: https://e2b.mintlify.app/docs/sandbox/ip-tunneling
How to tunnel Sandbox network traffic through a proxy server
## Setting up proxy tunneling
We will set up a proxy server on a GCP VM instance running [Shadowsocks](https://shadowsocks.org) and use it to tunnel the Sandbox network traffic.
This will allow you to use a dedicated IP address for outgoing requests.
### GCP VM setup
1. Create a firewall rule to allow all tcp/udp traffic to port 8388.
Replace `your-project-id` with your actual project ID.
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
gcloud compute firewall-rules create shadowsocks \
--project=your-project-id \
--direction=INGRESS \
--priority=1000 \
--network=default \
--action=ALLOW \
--rules=tcp:8388,udp:8388 \
--source-ranges=0.0.0.0/0 \
--target-tags=allow-shadowsocks
```
2. Create a VM instance with the following tags: `allow-shadowsocks`.
Replace `your-project-id` with your actual project ID.
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
gcloud compute instances create shadowsocks-vm \
--project=your-project-id \
--zone=us-west1-a \
--machine-type=n2-standard-2 \
--can-ip-forward \
--tags=allow-shadowsocks \
--image-project=debian-cloud \
--image-family=debian-12 \
--boot-disk-size=20GB
```
3. After the VM is created, you can connect to it using the following command:
Replace `your-project-id` with your actual project ID.
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
# May take a few seconds until the instance is ready to accept SSH connections
gcloud compute ssh shadowsocks-vm \
--project=your-project-id \
--zone=us-west1-a
```
### Shadowsocks server setup (VM)
SSH into the VM and follow the instructions below to install and configure Shadowsocks.
1. Install the necessary packages, if missing:
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
sudo apt update
sudo apt install -y wget tar
```
2. Download and install Shadowsocks (v1.24.0):
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
wget https://github.com/shadowsocks/shadowsocks-rust/releases/latest/download/shadowsocks-v1.24.0.x86_64-unknown-linux-gnu.tar.xz
tar -xf shadowsocks-*.tar.xz
sudo mv ssserver ssmanager ssurl /usr/local/bin/
```
3. Create a shadowsocks configuration file:
Replace `STRONG_PASSWORD_HERE` with your own password.
```json /etc/shadowsocks/server.json theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
{
"server": "0.0.0.0",
"server_port": 8388,
"password": "STRONG_PASSWORD_HERE",
"method": "aes-256-gcm",
"mode": "tcp_and_udp",
"timeout": 300,
"fast_open": true
}
```
4. Enable IP forwarding:
```txt /etc/sysctl.d/99-shadowsocks.conf theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
net.core.default_qdisc=fq
net.ipv4.tcp_congestion_control=bbr
net.ipv4.ip_forward=1
```
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
sudo sysctl -p /etc/sysctl.d/99-shadowsocks.conf
```
Optional: Update the Ubuntu Firewall rules to allow traffic to port 8388:
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
sudo ufw allow 8388/tcp
sudo ufw allow 8388/udp
```
5. Start the Shadowsocks server:
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
sudo ssserver -c /etc/shadowsocks/server.json -v
```
You should see the following in the console output:
```
shadowsocks tcp server listening on 0.0.0.0:8388
shadowsocks udp server listening on 0.0.0.0:8388
```
6. Optional: Create a systemd service to start the Shadowsocks server on boot:
```ini /etc/systemd/system/ssserver.service theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
[Unit]
Description=Shadowsocks Rust Server
After=network.target
[Service]
ExecStart=/usr/local/bin/ssserver -c /etc/shadowsocks/server.json
Restart=always
LimitNOFILE=51200
User=root
[Install]
WantedBy=multi-user.target
```
Reload the systemd daemon and start the service:
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
sudo systemctl daemon-reload
sudo systemctl enable --now ssserver
```
You can check the status of the service with the following command:
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
sudo systemctl status ssserver
```
### Shadowsocks client setup (sandbox)
Create a custom Sandbox template that uses the shadowsocks client to tunnel TCP traffic through the proxy server we set up above.
Route only designated traffic through the proxy.
1. Create a configuration file for the shadowsocks client:
Replace `SERVER_IP` with the IP address of the proxy server and `STRONG_PASSWORD_HERE` with your own password.
```json config.json theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
{
"server": "SERVER_IP",
"server_port": 8388,
"password": "STRONG_PASSWORD_HERE",
"method": "aes-256-gcm",
"local_address": "0.0.0.0",
"local_port": 1080,
"mode": "tcp"
}
```
2. Create a template file (`template.ts` / `template.py`):
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Template, waitForPort } from "e2b";
const shadowSocksVersion = "1.24.0"
const downloadUrl = `https://github.com/shadowsocks/shadowsocks-rust/releases/latest/download/shadowsocks-v${shadowSocksVersion}.x86_64-unknown-linux-gnu.tar.xz`
export const template = Template()
.fromBaseImage()
.runCmd([
`wget ${downloadUrl}`,
"tar -xf shadowsocks-*.tar.xz",
"sudo mv sslocal /usr/local/bin/"
])
.copy('config.json', 'config.json')
.setStartCmd(
"sudo sslocal -c config.json --daemonize",
waitForPort(1080)
)
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Template, wait_for_port
shadowsocks_version = "1.24.0"
download_url = f"https://github.com/shadowsocks/shadowsocks-rust/releases/latest/download/shadowsocks-v{shadowsocks_version}.x86_64-unknown-linux-gnu.tar.xz"
template = (
Template()
.from_base_image()
.run_cmd([
f"wget {download_url}",
"tar -xf shadowsocks-*.tar.xz",
"sudo mv sslocal /usr/local/bin/"
])
.copy("config.json", "config.json")
.set_start_cmd(
"sudo sslocal -c config.json --daemonize",
wait_for_port(1080)
)
)
```
3. Create a build script (`build.ts` / `build.py`):
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Template, defaultBuildLogger } from "e2b";
import { template as localProxyTemplate } from "./template";
await Template.build(template, {
alias: 'shadowsocks-client',
memoryMB: 1024,
cpuCount: 1,
onBuildLogs: defaultBuildLogger()
})
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Template, default_build_logger
from .template import template
Template.build(
template,
alias="shadowsocks-client",
memory_mb=1024,
cpu_count=1,
on_build_logs=default_build_logger()
)
```
4. Build the template using the build script:
```bash JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
npx tsx build.ts
```
```bash Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
python build.py
```
Route all traffic through the proxy.
1. Create a configuration file for the shadowsocks client:
Replace `SERVER_IP` with the IP address of the proxy server and `STRONG_PASSWORD_HERE` with your own password.
```json config.json theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
{
"server": "SERVER_IP",
"server_port": 8388,
"password": "STRONG_PASSWORD_HERE",
"method": "aes-256-gcm",
"local_address": "0.0.0.0",
"local_port": 1080,
"mode": "tcp"
}
```
2. Create a script for setting up the iptables rules:
Replace `SERVER_IP` with the IP address of the proxy server.
```bash iptables-rules.sh theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
iptables -t nat -N SHADOWSOCKS
iptables -t nat -A SHADOWSOCKS -d SERVER_IP -j RETURN
iptables -t nat -A SHADOWSOCKS -d 0.0.0.0/8 -j RETURN
iptables -t nat -A SHADOWSOCKS -d 127.0.0.0/8 -j RETURN
iptables -t nat -A SHADOWSOCKS -d 169.254.0.0/16 -j RETURN
iptables -t nat -A SHADOWSOCKS -p tcp -j REDIRECT --to-ports 12345
iptables -t nat -A OUTPUT -p tcp -j SHADOWSOCKS
```
3. Create a template file (`template.ts` / `template.py`):
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Template, waitForPort } from "e2b";
const shadowsocksVersion = "1.24.0"
const downloadUrl = `https://github.com/shadowsocks/shadowsocks-rust/releases/latest/download/shadowsocks-v${shadowsocksVersion}.x86_64-unknown-linux-gnu.tar.xz`
export const template = Template()
.fromBaseImage()
.aptInstall("iptables")
.runCmd([
`wget ${downloadUrl}`,
"tar -xf shadowsocks-*.tar.xz",
"sudo mv sslocal /usr/local/bin/"
])
.copy('config.json', 'config.json')
.copy('iptables-rules.sh', 'iptables-rules.sh', { mode: 0o755 })
.setStartCmd(
"sudo sslocal -c config.json --protocol redir -b 0.0.0.0:12345 --daemonize && sudo iptables-rules.sh",
waitForPort(1080)
)
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Template, wait_for_port
shadowsocks_version = "1.24.0"
download_url = f"https://github.com/shadowsocks/shadowsocks-rust/releases/latest/download/shadowsocks-v{shadowsocks_version}.x86_64-unknown-linux-gnu.tar.xz"
template = (
Template()
.from_base_image()
.apt_install("iptables")
.run_cmd([
f"wget {download_url}",
"tar -xf shadowsocks-*.tar.xz",
"sudo mv sslocal /usr/local/bin/"
])
.copy("config.json", "config.json")
.copy("iptables-rules.sh", "iptables-rules.sh", mode=0o755)
.set_start_cmd(
"sudo sslocal -c config.json --protocol redir -b 0.0.0.0:12345 --daemonize && sudo iptables-rules.sh",
wait_for_port(1080)
)
)
```
4. Create a build script (`build.ts` / `build.py`):
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Template, defaultBuildLogger } from "e2b";
import { template as localProxyTemplate } from "./template";
await Template.build(template, {
alias: 'shadowsocks-client',
memoryMB: 1024,
cpuCount: 1,
onBuildLogs: defaultBuildLogger()
})
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Template, default_build_logger
from .template import template
Template.build(
template,
alias="shadowsocks-client",
memory_mb=1024,
cpu_count=1,
on_build_logs=default_build_logger()
)
```
5. Build the template using the build script:
```bash JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
npx tsx build.ts
```
```bash Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
python build.py
```
## Using the proxies
Create a new Sandbox from the built template and run a curl command to verify that the traffic is routed through the proxy:
Only designated traffic should be routed through the proxy.
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from "e2b";
// create a new Sandbox from the built template
const sbx = await Sandbox.create('shadowsocks-client')
// run a command to curl the IP address of the proxy server
const curl = await sbx.commands.run('curl --socks5 127.0.0.1:1080 https://ifconfig.me')
console.log(curl.stdout)
await sbx.kill()
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
# create a new Sandbox from the built template
sbx = Sandbox.create('shadowsocks-client')
# run a command to curl the IP address of the proxy server
curl = sbx.commands.run('curl --socks5 127.0.0.1:1080 https://ifconfig.me')
print(curl.stdout)
sbx.kill()
```
```bash JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
npx tsx sandbox.ts
```
```bash Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
python sandbox.py
```
You should see the IP address of the proxy server.
All traffic should be routed through the proxy.
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from "e2b";
// create a new Sandbox from the built template
const sbx = await Sandbox.create('shadowsocks-client')
// run a command to curl the IP address of the proxy server
const curl = await sbx.commands.run('curl https://ifconfig.me')
console.log(curl.stdout)
await sbx.kill()
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
# create a new Sandbox from the built template
sbx = Sandbox.create('shadowsocks-client')
# run a command to curl the IP address of the proxy server
curl = sbx.commands.run('curl https://ifconfig.me')
print(curl.stdout)
sbx.kill()
```
```bash JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
npx tsx sandbox.ts
```
```bash Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
python sandbox.py
```
You should see the IP address of the proxy server.
# Monitor sandbox lifecycle events
Source: https://e2b.mintlify.app/docs/sandbox/lifecycle-events-api
The lifecycle API provides RESTful endpoints to request the latest sandbox lifecycle events. This allows you to track when sandboxes are created, paused, resumed, updated, snapshotted, or killed, along with metadata.
All requests require authentication using your team [API key](/docs/api-key#where-to-find-api-key).
Query Parameters:
* `offset` (optional): Number of events to skip (default: 0, min: 0)
* `limit` (optional): Number of events to return (default: 10, min: 1, max: 100)
* `orderAsc` (optional): Sort order - true for ascending, false for descending (default: false)
* `types` (optional): Filter by event type. Can be specified multiple times to match any of the given types.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const sbx = await Sandbox.create()
// Get the latest events for a specific sandbox
const resp1 = await fetch(
`https://api.e2b.app/events/sandboxes/${sbx.id}`,
{
method: 'GET',
headers: {
'X-API-Key': E2B_API_KEY,
},
}
)
const sandboxEvents = await resp1.json()
// Get the latest 10 events for all sandboxes associated with the team
const resp2 = await fetch(
'https://api.e2b.app/events/sandboxes?limit=10',
{
method: 'GET',
headers: {
'X-API-Key': E2B_API_KEY,
},
}
)
const teamSandboxEvents = await resp2.json()
// Filter events by type
const resp3 = await fetch(
'https://api.e2b.app/events/sandboxes?types=sandbox.lifecycle.created&types=sandbox.lifecycle.killed',
{
method: 'GET',
headers: {
'X-API-Key': E2B_API_KEY,
},
}
)
const filteredEvents = await resp3.json()
console.log(teamSandboxEvents)
// [
// {
// "version": "v1",
// "id": "f5911677-cb60-498f-afed-f68143b3cc59",
// "type": "sandbox.lifecycle.killed",
// "eventData": null,
// "sandboxBuildId": "a979a14b-bdcc-49e6-bc04-1189fc9fe7c2",
// "sandboxExecutionId": "1dae9e1c-9957-4ce7-a236-a99d5779aadf",
// "sandboxId": "${SANDBOX_ID}",
// "sandboxTeamId": "460355b3-4f64-48f9-9a16-4442817f79f5",
// "sandboxTemplateId": "rki5dems9wqfm4r03t7g",
// "timestamp": "2025-08-06T20:59:36Z"
// },
// {
// "version": "v1",
// "id": "30b09e11-9ba2-42db-9cf6-d21f0f43a234",
// "type": "sandbox.lifecycle.updated",
// "eventData": {
// "set_timeout": "2025-08-06T20:59:59Z"
// },
// "sandboxBuildId": "a979a14b-bdcc-49e6-bc04-1189fc9fe7c2",
// "sandboxExecutionId": "1dae9e1c-9957-4ce7-a236-a99d5779aadf",
// "sandboxId": "${SANDBOX_ID}",
// "sandboxTeamId": "460355b3-4f64-48f9-9a16-4442817f79f5",
// "sandboxTemplateId": "rki5dems9wqfm4r03t7g",
// "timestamp": "2025-08-06T20:59:29Z"
// },
// [...]
// {
// "version": "v1",
// "id": "0568572b-a2ac-4e5f-85fa-fae90905f556",
// "type": "sandbox.lifecycle.created",
// "eventData": null,
// "sandboxBuildId": "a979a14b-bdcc-49e6-bc04-1189fc9fe7c2",
// "sandboxExecutionId": "1dae9e1c-9957-4ce7-a236-a99d5779aadf",
// "sandboxId": "${SANDBOX_ID}",
// "sandboxTeamId": "460355b3-4f64-48f9-9a16-4442817f79f5",
// "sandboxTemplateId": "rki5dems9wqfm4r03t7g",
// "timestamp": "2025-08-06T20:59:24Z"
// }
// ]
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import requests
from e2b import Sandbox
sbx = Sandbox.create()
# Get the latest events for a specific sandbox
resp1 = requests.get(
f"https://api.e2b.app/events/sandboxes/{sbx.sandbox_id}",
headers={
"X-API-Key": E2B_API_KEY,
}
)
sandbox_events = resp1.json()
# Get the latest 10 events for all sandboxes associated with the team
resp2 = requests.get(
"https://api.e2b.app/events/sandboxes?limit=10",
headers={
"X-API-Key": E2B_API_KEY,
}
)
team_sandbox_events = resp2.json()
# Filter events by type
resp3 = requests.get(
"https://api.e2b.app/events/sandboxes",
params={"types": ["sandbox.lifecycle.created", "sandbox.lifecycle.killed"]},
headers={
"X-API-Key": E2B_API_KEY,
}
)
filtered_events = resp3.json()
print(team_sandbox_events)
# [
# {
# "version": "v1",
# "id": "0568572b-a2ac-4e5f-85fa-fae90905f556",
# "type": "sandbox.lifecycle.killed",
# "eventData": null,
# "sandboxBuildId": "a979a14b-bdcc-49e6-bc04-1189fc9fe7c2",
# "sandboxExecutionId": "1dae9e1c-9957-4ce7-a236-a99d5779aadf",
# "sandboxId": "${SANDBOX_ID}",
# "sandboxTeamId": "460355b3-4f64-48f9-9a16-4442817f79f5",
# "sandboxTemplateId": "rki5dems9wqfm4r03t7g",
# "timestamp": "2025-08-06T20:59:36Z"
# },
# {
# "version": "v1",
# "id": "e7013704-2c51-4dd2-9f6c-388c91460149",
# "type": "sandbox.lifecycle.updated",
# "eventData": {
# "set_timeout": "2025-08-06T20:59:59Z"
# },
# "sandboxBuildId": "a979a14b-bdcc-49e6-bc04-1189fc9fe7c2",
# "sandboxExecutionId": "1dae9e1c-9957-4ce7-a236-a99d5779aadf",
# "sandboxId": "${SANDBOX_ID}",
# "sandboxTeamId": "460355b3-4f64-48f9-9a16-4442817f79f5",
# "sandboxTemplateId": "rki5dems9wqfm4r03t7g",
# "timestamp": "2025-08-06T20:59:29Z"
# },
# [...]
# {
# "version": "v1",
# "id": "f29ef778-2743-4c97-a802-7ba67f84ce24",
# "type": "sandbox.lifecycle.created",
# "eventData": null,
# "sandboxBuildId": "a979a14b-bdcc-49e6-bc04-1189fc9fe7c2",
# "sandboxExecutionId": "1dae9e1c-9957-4ce7-a236-a99d5779aadf",
# "sandboxId": "${SANDBOX_ID}",
# "sandboxTeamId": "460355b3-4f64-48f9-9a16-4442817f79f5",
# "sandboxTemplateId": "rki5dems9wqfm4r03t7g",
# "timestamp": "2025-08-06T20:59:24Z"
# }
# ]
```
# Sandbox lifecycle webhooks
Source: https://e2b.mintlify.app/docs/sandbox/lifecycle-events-webhooks
Webhooks provide a way for notifications to be delivered to an external web server whenever certain sandbox lifecycle events occur.
This allows you to receive real-time updates about sandbox creation, updates, and termination without having to poll the API.
All webhook requests require authentication using your team [API key](/docs/api-key#where-to-find-api-key).
## Webhook management
### Register webhook
Register a new webhook to receive sandbox lifecycle events.
The webhook will be registered for the team ID associated with your API key.
All events specified during webhook creation will be sent to URL provided during registration with [following payload](#webhook-payload).
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
// Register a new webhook
const resp = await fetch(
'https://api.e2b.app/events/webhooks',
{
method: 'POST',
headers: {
'X-API-Key': E2B_API_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({
name: 'My Sandbox Webhook',
url: 'https://your-webhook-endpoint.com/webhook',
enabled: true,
events: ['sandbox.lifecycle.created', 'sandbox.lifecycle.updated', 'sandbox.lifecycle.killed'],
signatureSecret: 'secret-for-event-signature-verification'
}),
}
)
if (resp.status === 201) {
console.log('Webhook registered successfully')
}
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import requests
# Register a new webhook
resp = requests.post(
"https://api.e2b.app/events/webhooks",
headers={
"X-API-Key": E2B_API_KEY,
"Content-Type": "application/json",
},
json={
"name": "My Sandbox Webhook",
"url": "https://your-webhook-endpoint.com/webhook",
"enabled": true,
"events": ["sandbox.lifecycle.created", "sandbox.lifecycle.updated", "sandbox.lifecycle.killed"],
"signatureSecret": "secret-for-event-signature-verification"
}
)
if resp.status_code == 201:
print("Webhook registered successfully")
```
### List webhooks
List all registered webhooks for your team.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
// List webhooks
const resp = await fetch(
'https://api.e2b.app/events/webhooks',
{
method: 'GET',
headers: {
'X-API-Key': E2B_API_KEY
},
},
)
if (resp.status === 200) {
console.log('Webhooks listed successfully')
console.log(await resp.json())
}
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import requests
# List webhooks
resp = requests.get(
"https://api.e2b.app/events/webhooks",
headers={
"X-API-Key": E2B_API_KEY
},
)
if resp.status_code == 200:
print("Webhooks listed successfully")
print(response.json())
```
### Get webhook configuration
Retrieve the current webhook configuration for your team.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
// Get webhook configuration
const resp = await fetch(
`https://api.e2b.app/events/webhooks/${webhookID}`,
{
method: 'GET',
headers: {
'X-API-Key': E2B_API_KEY,
},
}
)
const webhookConfig = await resp.json()
console.log(webhookConfig)
// {
// "id": "",
// "teamID": "",
// "name": "My Sandbox Webhook",
// "createdAt": "2025-08-06T21:00:00Z",
// "enabled": true,
// "url": "https://your-webhook-endpoint.com/webhook",
// "events": ["sandbox.lifecycle.created", "sandbox.lifecycle.killed"]
// }
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import requests
# Get webhook configuration
resp = requests.get(
"https://api.e2b.app/events/webhooks/{webhookID}",
headers={
"X-API-Key": E2B_API_KEY,
}
)
webhook_config = resp.json()
print(webhook_config)
# {
# "id": "",
# "teamID": "",
# "name": "My Sandbox Webhook",
# "createdAt": "2025-08-06T21:00:00Z",
# "enabled": true,
# "url": "https://your-webhook-endpoint.com/webhook",
# "events": ["sandbox.lifecycle.created", "sandbox.lifecycle.killed"]
# }
```
### Update webhook configuration
Update an existing webhook configuration. The update will replace the previous configuration fields with provided fields.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
// Update webhook configuration
const resp = await fetch(
`https://api.e2b.app/events/webhooks/${webhookID}`,
{
method: 'PATCH',
headers: {
'X-API-Key': E2B_API_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({
url: 'https://your-updated-webhook-endpoint.com/webhook',
enabled: false,
events: ['sandbox.lifecycle.created']
}),
}
)
if (resp.status === 200) {
console.log('Webhook updated successfully')
}
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import requests
# Update webhook configuration
resp = requests.patch(
"https://api.e2b.app/events/webhooks/{webhookID}",
headers={
"X-API-Key": E2B_API_KEY,
"Content-Type": "application/json",
},
json={
"url": "https://your-updated-webhook-endpoint.com/webhook",
"enabled": False,
"events": ["sandbox.lifecycle.created"]
}
)
if resp.status_code == 200:
print("Webhook updated successfully")
```
### Delete webhook
Unregister the webhook.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
// Delete webhook configuration
const resp = await fetch(
`https://api.e2b.app/events/webhooks/${webhookID}`,
{
method: 'DELETE',
headers: {
'X-API-Key': E2B_API_KEY,
},
}
)
if (resp.status === 200) {
console.log('Webhook deleted successfully')
}
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import requests
# Delete webhook configuration
resp = requests.delete(
"https://api.e2b.app/events/webhooks/{webhookID}",
headers={
"X-API-Key": E2B_API_KEY,
}
)
if resp.status_code == 200:
print("Webhook deleted successfully")
```
## Webhook payload
When a webhook is triggered, your endpoint will receive a POST request with a JSON payload containing the sandbox event data.
The payload structure matches the event format from the API:
```json theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
{
"version": "v2",
"id": "",
"type": "",
"eventData": {
"sandbox_metadata": {
"": ""
},
"execution": {
"started_at": "2025-08-06T20:58:24Z",
"vcpu_count": 2,
"memory_mb": 512,
"execution_time": 1000,
}
},
"sandboxBuildId": "",
"sandboxExecutionId": "",
"sandboxId": "",
"sandboxTeamId": "",
"sandboxTemplateId": "",
"timestamp": "2025-08-06T20:59:24Z"
}
```
`eventData.execution` contains sandbox execution details and is included on `sandbox.lifecycle.killed` and `sandbox.lifecycle.paused` events:
* `started_at` - UTC RFC3339 timestamp when the current sandbox execution started
* `vcpu_count` - Number of vCPUs assigned to the sandbox
* `memory_mb` - Memory assigned to the sandbox in MB
* `execution_time` - Sandbox runtime in milliseconds
# Webhook verification
To ensure the authenticity of webhook requests, each request includes a signature in the `e2b-signature` header.
You can verify the signature using the signature secret you provided when registering the webhook.
This confirms that the request originated from E2B and has not been tampered with.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
function verifyWebhookSignature(secret : string, payload : string, payloadSignature : string) {
const expectedSignatureRaw = crypto.createHash('sha256').update(secret + payload).digest('base64');
const expectedSignature = expectedSignatureRaw.replace(/=+$/, '');
return expectedSignature == payloadSignature
}
const payloadValid = verifyWebhookSignature(secret, webhookBodyRaw, webhookSignatureHeader)
if (payloadValid) {
console.log("Payload signature is valid")
} else {
console.log("Payload signature is INVALID")
}
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import hashlib
import base64
def verify_webhook_signature(secret: str, payload: str, payload_signature: str) -> bool:
hash_bytes = hashlib.sha256((secret + payload).encode('utf-8')).digest()
expected_signature = base64.b64encode(hash_bytes).decode('utf-8')
expected_signature = expected_signature.rstrip('=')
return expected_signature == payload_signature
if verify_webhook_signature(secret, webhook_body_raw, webhook_signature_header):
print("Payload signature is valid")
else:
print("Payload signature is INVALID")
```
```go Golang theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import (
"crypto/sha256"
"encoding/base64"
"fmt"
"strings"
)
func verifyWebhookSignature(secret, payload, payloadSignature string) bool {
hash := sha256.Sum256([]byte(secret + payload))
expectedSignature := base64.StdEncoding.EncodeToString(hash[:])
expectedSignature = strings.TrimRight(expectedSignature, "=")
return expectedSignature == payloadSignature
}
if verifyWebhookSignature(secret, webhookBodyString, webhookSignatureHeaderString) {
fmt.Println("Payload signature is valid")
} else {
fmt.Println("Payload signature is INVALID")
}
```
## Webhook request headers
When webhooks is send, we are adding headers to help you verify the authenticity of the request and make debugging easier.
* `e2b-webhook-id` - Webhook ID that triggered the event
* `e2b-delivery-id` - Unique ID for the delivery attempt
* `e2b-signature-version` - Currently always `v1`, reserved for future use
* `e2b-signature` - Signature for verifying the request authenticity\`
## Available event types
The following event types can be subscribed to via webhooks, they are used as the `type` field in the [payload](#webhook-payload).
* `sandbox.lifecycle.created` - Sandbox creation
* `sandbox.lifecycle.killed` - Sandbox termination
* `sandbox.lifecycle.updated` - Sandbox configuration updates
* `sandbox.lifecycle.paused` - Sandbox pausing
* `sandbox.lifecycle.resumed` - Sandbox resuming
* `sandbox.lifecycle.checkpointed` - Sandbox [snapshot](/docs/sandbox/snapshots) created
# List sandboxes
Source: https://e2b.mintlify.app/docs/sandbox/list
You can list sandboxes using the `Sandbox.list()` method.
Once you have information about running sandbox, you can [connect](/docs/sandbox/connect) to it using the `Sandbox.connect()` method.
### Listing sandboxes
The `Sandbox.list()` method supports pagination. In the [advanced pagination](/docs/sandbox/list#advanced-pagination) section, you can find more information about pagination techniques using the updated method.
```js JavaScript & TypeScript highlight={6,11,14,24} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox, SandboxInfo } from 'e2b'
const sandbox = await Sandbox.create(
{
metadata: {
name: 'My Sandbox',
},
},
)
const paginator = Sandbox.list()
// Get the first page of sandboxes (running and paused)
const firstPage = await paginator.nextItems()
const runningSandbox = firstPage[0]
console.log('Running sandbox metadata:', runningSandbox.metadata)
console.log('Running sandbox id:', runningSandbox.sandboxId)
console.log('Running sandbox started at:', runningSandbox.startedAt)
console.log('Running sandbox template id:', runningSandbox.templateId)
// Get the next page of sandboxes
const nextPage = await paginator.nextItems()
```
```python Python highlight={5,9,12,22} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox, SandboxInfo
sandbox = Sandbox.create(
metadata={
"name": "My Sandbox",
},
)
paginator = Sandbox.list()
# Get the first page of sandboxes (running and paused)
firstPage = paginator.next_items()
running_sandbox = firstPage[0]
print('Running sandbox metadata:', running_sandbox.metadata)
print('Running sandbox id:', running_sandbox.sandbox_id)
print('Running sandbox started at:', running_sandbox.started_at)
print('Running sandbox template id:', running_sandbox.template_id)
# Get the next page of sandboxes
nextPage = paginator.next_items()
```
The code above will output something like this:
```bash JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
Running sandbox metadata: {name: "My Sandbox"}
Running sandbox id: ixjj3iankaishgcge4jwn-b0b684e9
Running sandbox started at: 2024-10-15T21:13:07.311Z
Running sandbox template id: 3e4rngfa34txe0gxc1zf
```
```bash Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
Running sandbox metadata: {'name': 'My Sandbox'}
Running sandbox id: ixjj3iankaishgcge4jwn-b0b684e9
Running sandbox started at: 2024-10-15 21:13:07.311861+00:00
Running sandbox template id: 3e4rngfa34txe0gxc1zf
```
### Filtering sandboxes
Filter sandboxes by their current state. The state parameter can contain either "**running**" for running sandboxes or "**paused**" for paused sandboxes, or both.
```js JavaScript & TypeScript highlight={9,13} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
// Create a sandbox.
const sandbox = await Sandbox.create()
// List sandboxes that are running or paused.
const paginator = Sandbox.list({
query: {
state: ['running', 'paused'],
},
})
const sandboxes = await paginator.nextItems()
```
```python Python highlight={9,14} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox, SandboxQuery, SandboxState
# Create a sandbox with metadata.
sandbox = Sandbox.create()
# List sandboxes that are running or paused.
paginator = Sandbox.list(
query=SandboxQuery(
state=[SandboxState.RUNNING, SandboxState.PAUSED],
),
)
# Get the first page of sandboxes (running and paused)
sandboxes = paginator.next_items()
```
Filter sandboxes by the metadata key value pairs specified during Sandbox creation.
```js JavaScript & TypeScript highlight={6-8,15,18} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
// Create sandbox with metadata.
const sandbox = await Sandbox.create({
metadata: {
env: 'dev',
app: 'my-app',
userId: '123',
},
})
// List all sandboxes that has `userId` key with value `123` and `env` key with value `dev`.
const paginator = Sandbox.list({
query: {
metadata: { userId: '123', env: 'dev' },
},
})
const sandboxes = await paginator.nextItems()
```
```python Python highlight={6-8,16-17} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox, SandboxQuery, SandboxState
# Create sandbox with metadata.
sandbox = Sandbox.create(
metadata={
"env": "dev",
"app": "my-app",
"user_id": "123",
},
)
# List running sandboxes that has `userId` key with value `123` and `env` key with value `dev`.
paginator = Sandbox.list(
query=SandboxQuery(
metadata={
"userId": "123",
"env": "dev",
}
),
)
# Get the first page of sandboxes (running and paused)
sandboxes = paginator.next_items()
```
### Advanced pagination
For more granular pagination, you can set custom per-page item limit (default and maximum is **100**) and specify an offset parameter (`nextToken` or `next_token`) to start paginating from.
```js JavaScript & TypeScript highlight={4-5,16} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const paginator = Sandbox.list({
limit: 100,
nextToken: '',
})
// Additional paginator properties
// Whether there is a next page
paginator.hasNext
// Next page token
paginator.nextToken
// Fetch the next page
await paginator.nextItems()
```
```python Python highlight={5-6,13} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
# List running sandboxes that has `userId` key with value `123` and `env` key with value `dev`.
paginator = Sandbox.list(
limit=100,
next_token="",
)
paginator.has_next # Whether there is a next page
paginator.next_token # Next page token
# Fetch the next page
paginator.next_items()
```
You can fetch all pages by looping through the paginator while checking if there is a next page (using `hasNext` or `has_next` property) and fetching until there are no more pages left to fetch:
```js JavaScript & TypeScript highlight={7} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const paginator = Sandbox.list()
// Loop through all pages
const sandboxes: SandboxInfo[] = []
while (paginator.hasNext) {
const items = await paginator.nextItems()
sandboxes.push(...items)
}
```
```python Python highlight={7} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox, SandboxQuery
paginator = Sandbox.list()
# Loop through all pages
sandboxes: list[SandboxInfo] = []
while paginator.has_next:
items = paginator.next_items()
sandboxes.extend(items)
```
## Old SDK (v1.x.y)
If you're using SDK with version lower than `2.0.0`, the `Sandbox.list()` method behaves differently.
```js JavaScript & TypeScript highlight={11} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
// Create a sandbox.
const sandbox = await Sandbox.create({
metadata: {
name: 'My Sandbox',
},
})
// List all running sandboxes.
const runningSandboxes = await Sandbox.list()
const runningSandbox = runningSandboxes[0]
console.log('Running sandbox metadata:', runningSandbox.metadata)
console.log('Running sandbox id:', runningSandbox.sandboxId)
console.log('Running sandbox started at:', runningSandbox.startedAt)
console.log('Running sandbox template id:', runningSandbox.templateId)
```
```python Python highlight={11} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
# Create a sandbox.
sandbox = Sandbox.create(
metadata: {
name: 'My Sandbox',
},
)
# List all running sandboxes.
running_sandboxes = Sandbox.list()
running_sandbox = running_sandboxes[0]
print('Running sandbox metadata:', running_sandbox.metadata)
print('Running sandbox id:', running_sandbox.sandbox_id)
print('Running sandbox started at:', running_sandbox.started_at)
print('Running sandbox template id:', running_sandbox.template_id)
```
## Filtering sandboxes
You can filter sandboxes by specifying [Metadata](/docs/sandbox/metadata) key value pairs.
Specifying multiple key value pairs will return sandboxes that match all of them.
This can be useful when you have a large number of sandboxes and want to find only specific ones. The filtering is performed on the server.
```js JavaScript & TypeScript highlight={6-8,15} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
// Create sandbox with metadata.
const sandbox = await Sandbox.create({
metadata: {
env: 'dev',
app: 'my-app',
userId: '123',
},
})
// List running sandboxes that has `userId` key with value `123` and `env` key with value `dev`.
const runningSandboxes = await Sandbox.list({
query: {
metadata: { userId: '123', env: 'dev' },
},
})
```
```python Python highlight={7-9,17-18} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
from e2b.sandbox.sandbox_api import SandboxQuery
# Create sandbox with metadata.
sandbox = Sandbox.create(
metadata={
"env": "dev",
"app": "my-app",
"user_id": "123",
},
)
# List running sandboxes that has `userId` key with value `123` and `env` key with value `dev`.
running_sandboxes = Sandbox.list(
query=SandboxQuery(
metadata={
"userId": "123",
"env": "dev",
}
),
)
```
# Sandbox metadata
Source: https://e2b.mintlify.app/docs/sandbox/metadata
Metadata is a way to attach arbitrary key-value pairs for a sandbox.
This is useful in various scenarios, for example:
* Associate a sandbox with a user session.
* Store custom user data for a sandbox like API keys.
* Associate a sandbox with a user ID and [connect to it later](/docs/sandbox/connect).
You specify metadata when creating a sandbox and can access it later through listing running sandboxes with `Sandbox.list()` method.
```js JavaScript & TypeScript highlight={6} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
// Create sandbox with metadata.
const sandbox = await Sandbox.create({
metadata: {
userId: '123',
},
})
// List running sandboxes and access metadata.
const paginator = await Sandbox.list()
const runningSandboxes = await paginator.nextItems()
// Will print:
// {
// 'userId': '123',
// }
console.log(runningSandboxes[0].metadata)
```
```python Python highlight={6} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
# Create sandbox with metadata.
sandbox = Sandbox.create(
metadata={
'userId': '123',
},
)
# List running sandboxes and access metadata.
paginator = Sandbox.list()
running_sandboxes = paginator.next_items()
# Will print:
# {
# 'userId': '123',
# }
print(running_sandboxes[0].metadata)
```
## Filtering sandboxes by metadata
You can also filter sandboxes by metadata, you can find more about it [here](/docs/sandbox/list#filtering-sandboxes).
# Sandbox metrics
Source: https://e2b.mintlify.app/docs/sandbox/metrics
The sandbox metrics allows you to get information about the sandbox's CPU, memory and disk usage.
## Getting sandbox metrics
Getting the metrics of a sandbox returns an array of timestamped metrics containing CPU, memory and disk usage information.
The metrics are collected every 5 seconds.
### Getting sandbox metrics using the SDKs
```js JavaScript & TypeScript highlight={9} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const sbx = await Sandbox.create()
console.log('Sandbox created', sbx.sandboxId)
// Wait for a few seconds to collect some metrics
await new Promise((resolve) => setTimeout(resolve, 10_000))
const metrics = await sbx.getMetrics()
// You can also get the metrics by sandbox ID:
// const metrics = await Sandbox.getMetrics(sbx.sandboxId)
console.log('Sandbox metrics:', metrics)
// Sandbox metrics:
// [
// {
// timestamp: 2025-07-28T08:04:05.000Z,
// cpuUsedPct: 20.33,
// cpuCount: 2,
// memUsed: 32681984, // in bytes
// memTotal: 507592704, // in bytes
// diskUsed: 1514856448, // in bytes
// diskTotal: 2573185024 // in bytes
// },
// {
// timestamp: 2025-07-28T08:04:10.000Z,
// cpuUsedPct: 0.2,
// cpuCount: 2,
// memUsed: 33316864, // in bytes
// memTotal: 507592704, // in bytes
// diskUsed: 1514856448, // in bytes
// diskTotal: 2573185024 // in bytes
// }
// ]
```
```python Python highlight={10} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from time import sleep
from e2b import Sandbox
sbx = Sandbox.create()
print('Sandbox created', sbx.sandbox_id)
# Wait for a few seconds to collect some metrics
sleep(10)
metrics = sbx.get_metrics()
# You can also get the metrics by sandbox ID:
# metrics = Sandbox.get_metrics(sbx.sandbox_id)
print('Sandbox metrics', metrics)
# Sandbox metrics
# [
# SandboxMetric(
# cpu_count=2,
# cpu_used_pct=13.97,
# disk_total=2573185024, # in bytes
# disk_used=1514856448, # in bytes
# mem_total=507592704, # in bytes
# mem_used=30588928, # in bytes
# timestamp=datetime.datetime(2025, 7, 28, 8, 8, 15, tzinfo=tzutc()),
# ),
# SandboxMetric(
# cpu_count=2,
# cpu_used_pct=0.1,
# disk_total=2573185024, # in bytes
# disk_used=1514856448, # in bytes
# mem_total=507592704, # in bytes
# mem_used=31084544, # in bytes
# timestamp=datetime.datetime(2025, 7, 28, 8, 8, 20, tzinfo=tzutc()),
# ),
# ]
```
### Getting sandbox metrics using the CLI
```bash Terminal highlight={1} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
e2b sandbox metrics
# Metrics for sandbox
#
# [2025-07-25 14:05:55Z] CPU: 8.27% / 2 Cores | Memory: 31 / 484 MiB | Disk: 1445 / 2453 MiB
# [2025-07-25 14:06:00Z] CPU: 0.5% / 2 Cores | Memory: 32 / 484 MiB | Disk: 1445 / 2453 MiB
# [2025-07-25 14:06:05Z] CPU: 0.1% / 2 Cores | Memory: 32 / 484 MiB | Disk: 1445 / 2453 MiB
# [2025-07-25 14:06:10Z] CPU: 0.3% / 2 Cores | Memory: 32 / 484 MiB | Disk: 1445 / 2453 MiB
```
It may take a second or more to get the first metrics after the sandbox is created. Until the first metrics are collected from the sandbox, you will get an empty array.
# Sandbox persistence
Source: https://e2b.mintlify.app/docs/sandbox/persistence
The sandbox persistence allows you to pause your sandbox and resume it later from the same state it was in when you paused it.
This includes not only state of the sandbox's filesystem but also the sandbox's memory. This means all running processes, loaded variables, data, etc.
## Sandbox state transitions
Understanding how sandboxes transition between different states is crucial for managing their lifecycle effectively. Here's a diagram showing the possible state transitions:
```mermaid actions={false} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
flowchart TD
start(( )) -->|Sandbox.create| Running
Running["Running • Active execution • Consumes resources"]
Paused["Paused • Preserves memory and files • Cannot execute code"]
Snapshotting["Snapshotting • Creates persistent snapshot • Briefly pauses execution"]
Killed["Killed • Resources released • Cannot be resumed"]
Running -->|pause| Paused
Running -->|createSnapshot| Snapshotting
Paused -->|connect| Running
Snapshotting -->|snapshot complete| Running
Running -->|kill| Killed
Paused -->|kill| Killed
```
### State descriptions
* **Running**: The sandbox is actively running and can execute code. This is the initial state after creation.
* **Paused**: The sandbox execution is suspended but its state is preserved.
* **Snapshotting**: The sandbox is briefly paused while a persistent snapshot is being created. It automatically returns to Running. See [Snapshots](/docs/sandbox/snapshots).
* **Killed**: The sandbox is terminated and all resources are released. This is a terminal state.
### Changing sandbox's state
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const sandbox = await Sandbox.create() // Starts in Running state
// Pause the sandbox
await sandbox.pause() // Running → Paused
// Resume the sandbox
await sandbox.connect() // Running/Paused → Running
// Kill the sandbox (from any state)
await sandbox.kill() // Running/Paused → Killed
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
sandbox = Sandbox.create() # Starts in Running state
# Pause the sandbox
sandbox.pause() # Running → Paused
# Resume the sandbox
sandbox.connect() # Running/Paused → Running
# Kill the sandbox (from any state)
sandbox.kill() # Running/Paused → Killed
```
## Pausing sandbox
When you pause a sandbox, both the sandbox's filesystem and memory state will be saved. This includes all the files in the sandbox's filesystem and all the running processes, loaded variables, data, etc.
```js JavaScript & TypeScript highlight={8-9} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const sbx = await Sandbox.create()
console.log('Sandbox created', sbx.sandboxId)
// Pause the sandbox
// You can save the sandbox ID in your database to resume the sandbox later
await sbx.pause()
console.log('Sandbox paused', sbx.sandboxId)
```
```python Python highlight={8-9} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
sbx = Sandbox.create()
print('Sandbox created', sbx.sandbox_id)
# Pause the sandbox
# You can save the sandbox ID in your database to resume the sandbox later
sbx.pause()
print('Sandbox paused', sbx.sandbox_id)
```
## Resuming sandbox
When you resume a sandbox, it will be in the same state it was in when you paused it.
This means that all the files in the sandbox's filesystem will be restored and all the running processes, loaded variables, data, etc. will be restored.
```js JavaScript & TypeScript highlight={12-13} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const sbx = await Sandbox.create()
console.log('Sandbox created', sbx.sandboxId)
// Pause the sandbox
// You can save the sandbox ID in your database to resume the sandbox later
await sbx.pause()
console.log('Sandbox paused', sbx.sandboxId)
// Connect to the sandbox (it will automatically resume the sandbox, if paused)
const sameSbx = await sbx.connect()
console.log('Connected to the sandbox', sameSbx.sandboxId)
```
```python Python highlight={12-13} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
sbx = Sandbox.create()
print('Sandbox created', sbx.sandbox_id)
# Pause the sandbox
# You can save the sandbox ID in your database to resume the sandbox later
sbx.pause()
print('Sandbox paused', sbx.sandbox_id)
# Connect to the sandbox (it will automatically resume the sandbox, if paused)
same_sbx = sbx.connect()
print('Connected to the sandbox', same_sbx.sandbox_id)
```
## Listing paused sandboxes
You can list all paused sandboxes by calling the `Sandbox.list` method and supplying the `state` query parameter.
More information about using the method can be found in [List Sandboxes](/docs/sandbox/list).
```js JavaScript & TypeScript highlight={4,7} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox, SandboxInfo } from 'e2b'
// List all paused sandboxes
const paginator = Sandbox.list({ query: { state: ['paused'] } })
// Get the first page of paused sandboxes
const sandboxes = await paginator.nextItems()
// Get all paused sandboxes
while (paginator.hasNext) {
const items = await paginator.nextItems()
sandboxes.push(...items)
}
```
```python Python highlight={4,7} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
# List all paused sandboxes
from e2b import Sandbox, SandboxQuery, SandboxState
paginator = Sandbox.list(SandboxQuery(state=[SandboxState.PAUSED]))
# Get the first page of paused sandboxes
sandboxes = paginator.next_items()
# Get all paused sandboxes
while paginator.has_next:
items = paginator.next_items()
sandboxes.extend(items)
```
## Removing paused sandboxes
You can remove paused sandboxes by calling the `kill` method on the Sandbox instance.
```js JavaScript & TypeScript highlight={11,14} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const sbx = await Sandbox.create()
console.log('Sandbox created', sbx.sandboxId)
// Pause the sandbox
// You can save the sandbox ID in your database to resume the sandbox later
await sbx.pause()
// Remove the sandbox
await sbx.kill()
// Remove sandbox by id
await Sandbox.kill(sbx.sandboxId)
```
```python Python highlight={9,12} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
sbx = Sandbox.create()
# Pause the sandbox
sbx.pause()
# Remove the sandbox
sbx.kill()
# Remove sandbox by id
Sandbox.kill(sbx.sandbox_id)
```
## Sandbox's timeout
When you connect to a sandbox, the timeout resets. The default is 5 minutes, but you can pass a custom timeout to the `Sandbox.connect()` method:
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const sbx = await Sandbox.connect(sandboxId, { timeoutMs: 60 * 1000 }) // 60 seconds
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
sbx = Sandbox.connect(sandbox_id, timeout=60) # 60 seconds
```
### Auto-pause
Auto-pause is configured in the sandbox lifecycle on create. Set `onTimeout`/`on_timeout` to `pause`.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const sandbox = await Sandbox.create({
timeoutMs: 10 * 60 * 1000, // Optional: change default timeout (10 minutes)
lifecycle: {
onTimeout: 'pause',
autoResume: false, // Optional (default is false)
},
})
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
sandbox = Sandbox.create(
timeout=10 * 60, # Optional: change default timeout (10 minutes)
lifecycle={
"on_timeout": "pause", # Auto-pause after the sandbox times out
"auto_resume": False, # Optional (default is False)
},
)
```
Auto-pause is persistent, meaning if your sandbox resumes and later times out again, it will pause again.
If you call `.kill()`, the sandbox is permanently deleted and cannot be resumed.
For auto-resume behavior, see [AutoResume](/docs/sandbox/auto-resume).
## Network
If you have a service (for example a server) running inside your sandbox and you pause the sandbox, the service won't be accessible from the outside and all the clients will be disconnected.
If you resume the sandbox, the service will be accessible again but you need to connect clients again.
## Limitations
### Pause and resume performance
* Pausing a sandbox takes approximately **4 seconds per 1 GiB of RAM**
* Resuming a sandbox takes approximately **1 second**
### Paused sandbox retention
* Paused sandboxes are kept **indefinitely** — there is no automatic deletion or time-to-live limit
* You can resume a paused sandbox at any time
### Continuous runtime limits
* A sandbox can remain running (without being paused) for:
* **24 hours** on the **Pro tier**
* **1 hour** on the **Base tier**
* After a sandbox is paused and resumed, the continuous runtime limit is **reset**
# Interactive terminal (PTY)
Source: https://e2b.mintlify.app/docs/sandbox/pty
The PTY (pseudo-terminal) module allows you to create interactive terminal sessions in the sandbox with real-time, bidirectional communication.
Unlike `commands.run()` which executes a command and returns output after completion, PTY provides:
* **Real-time streaming** - Output is streamed as it happens via callbacks
* **Bidirectional input** - Send input while the terminal is running
* **Interactive shell** - Full terminal support with ANSI colors and escape sequences
* **Session persistence** - Disconnect and reconnect to running sessions
## Create a PTY session
Use `sandbox.pty.create()` to start an interactive bash shell.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const sandbox = await Sandbox.create()
const terminal = await sandbox.pty.create({
cols: 80, // Terminal width in characters
rows: 24, // Terminal height in characters
onData: (data) => {
// Called whenever terminal outputs data
process.stdout.write(data)
},
envs: { MY_VAR: 'hello' }, // Optional environment variables
cwd: '/home/user', // Optional working directory
user: 'root', // Optional user to run as
})
// terminal.pid contains the process ID
console.log('Terminal PID:', terminal.pid)
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
sandbox = Sandbox()
terminal = sandbox.pty.create(
cols=80, # Terminal width in characters
rows=24, # Terminal height in characters
on_data=lambda data: print(data.decode(), end=''), # end='' prevents print from adding extra newline
envs={'MY_VAR': 'hello'}, # Optional environment variables
cwd='/home/user', # Optional working directory
user='root', # Optional user to run as
)
# terminal.pid contains the process ID
print('Terminal PID:', terminal.pid)
```
The PTY runs an interactive bash shell with `TERM=xterm-256color`, which supports ANSI colors and escape sequences.
## Timeout
PTY sessions have a configurable timeout that controls the session duration. The default is 60 seconds. For interactive or long-running sessions, set `timeoutMs: 0` (JavaScript) or `timeout=0` (Python) to keep the session open indefinitely.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const sandbox = await Sandbox.create()
const terminal = await sandbox.pty.create({
cols: 80,
rows: 24,
onData: (data) => process.stdout.write(data),
timeoutMs: 0, // Keep the session open indefinitely
})
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
sandbox = Sandbox()
# end='' prevents print() from adding an extra newline
# (PTY output already contains newlines)
terminal = sandbox.pty.create(
cols=80,
rows=24,
on_data=lambda data: print(data.decode(), end=''),
timeout=0, # Keep the session open indefinitely
)
```
## Send input to PTY
Use `sendInput()` in JavaScript or `send_stdin()` in Python to send data to the terminal. These methods return a Promise (JavaScript) or complete synchronously (Python) - the actual output will be delivered to your `onData` callback.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const sandbox = await Sandbox.create()
const terminal = await sandbox.pty.create({
cols: 80,
rows: 24,
onData: (data) => process.stdout.write(data),
})
// Send a command (don't forget the newline!)
await sandbox.pty.sendInput(
terminal.pid,
new TextEncoder().encode('echo "Hello from PTY"\n')
)
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
sandbox = Sandbox()
terminal = sandbox.pty.create(
cols=80,
rows=24,
on_data=lambda data: print(data.decode(), end=''), # end='' prevents extra newline
)
# Send a command as bytes (b'...' is Python's byte string syntax)
# Don't forget the newline!
sandbox.pty.send_stdin(terminal.pid, b'echo "Hello from PTY"\n')
```
## Resize the terminal
When the user's terminal window changes size, notify the PTY with `resize()`. The `cols` and `rows` parameters are measured in characters, not pixels.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const sandbox = await Sandbox.create()
const terminal = await sandbox.pty.create({
cols: 80,
rows: 24,
onData: (data) => process.stdout.write(data),
})
// Resize to new dimensions (in characters)
await sandbox.pty.resize(terminal.pid, {
cols: 120,
rows: 40,
})
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
sandbox = Sandbox()
terminal = sandbox.pty.create(
cols=80,
rows=24,
on_data=lambda data: print(data.decode(), end=''), # end='' prevents extra newline
)
# Resize to new dimensions (in characters)
sandbox.pty.resize(terminal.pid, cols=120, rows=40)
```
## Disconnect and reconnect
You can disconnect from a PTY session while keeping it running, then reconnect later with a new data handler. This is useful for:
* Resuming terminal sessions after network interruptions
* Sharing terminal access between multiple clients
* Implementing terminal session persistence
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const sandbox = await Sandbox.create()
// Create a PTY session
const terminal = await sandbox.pty.create({
cols: 80,
rows: 24,
onData: (data) => console.log('Handler 1:', new TextDecoder().decode(data)),
})
const pid = terminal.pid
// Send a command
await sandbox.pty.sendInput(pid, new TextEncoder().encode('echo hello\n'))
// Disconnect - PTY keeps running in the background
await terminal.disconnect()
// Later: reconnect with a new data handler
const reconnected = await sandbox.pty.connect(pid, {
onData: (data) => console.log('Handler 2:', new TextDecoder().decode(data)),
})
// Continue using the session
await sandbox.pty.sendInput(pid, new TextEncoder().encode('echo world\n'))
// Wait for the terminal to exit
await reconnected.wait()
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import time
from e2b import Sandbox
sandbox = Sandbox()
# Create a PTY session
terminal = sandbox.pty.create(
cols=80,
rows=24,
on_data=lambda data: print('Handler 1:', data.decode()),
)
pid = terminal.pid
# Send a command
sandbox.pty.send_stdin(pid, b'echo hello\n')
time.sleep(0.5)
# Disconnect - PTY keeps running in the background
terminal.disconnect()
# Later: reconnect with a new data handler
reconnected = sandbox.pty.connect(
pid,
on_data=lambda data: print('Handler 2:', data.decode()),
)
# Continue using the session
sandbox.pty.send_stdin(pid, b'echo world\n')
# Wait for the terminal to exit
reconnected.wait()
```
## Kill the PTY
Terminate the PTY session with `kill()`.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const sandbox = await Sandbox.create()
const terminal = await sandbox.pty.create({
cols: 80,
rows: 24,
onData: (data) => process.stdout.write(data),
})
// Kill the PTY
const killed = await sandbox.pty.kill(terminal.pid)
console.log('Killed:', killed) // true if successful
// Or use the handle method
// await terminal.kill()
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
sandbox = Sandbox()
terminal = sandbox.pty.create(
cols=80,
rows=24,
on_data=lambda data: print(data.decode(), end=''), # end='' prevents extra newline
)
# Kill the PTY
killed = sandbox.pty.kill(terminal.pid)
print('Killed:', killed) # True if successful
# Or use the handle method
# terminal.kill()
```
## Wait for PTY to exit
Use `wait()` to wait for the terminal session to end (e.g., when the user types `exit`).
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const sandbox = await Sandbox.create()
const terminal = await sandbox.pty.create({
cols: 80,
rows: 24,
onData: (data) => process.stdout.write(data),
})
// Send exit command
await sandbox.pty.sendInput(terminal.pid, new TextEncoder().encode('exit\n'))
// Wait for the terminal to exit
const result = await terminal.wait()
console.log('Exit code:', result.exitCode)
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
sandbox = Sandbox()
terminal = sandbox.pty.create(
cols=80,
rows=24,
on_data=lambda data: print(data.decode(), end=''), # end='' prevents extra newline
)
# Send exit command
sandbox.pty.send_stdin(terminal.pid, b'exit\n')
# Wait for the terminal to exit
result = terminal.wait()
print('Exit code:', result.exit_code)
```
## Interactive terminal (SSH-like)
Building a fully interactive terminal (like SSH) requires handling raw mode, stdin forwarding, and terminal resize events. For a production implementation, see the [E2B CLI source code](https://github.com/e2b-dev/E2B/blob/main/packages/cli/src/terminal.ts) which uses the same `sandbox.pty` API documented above.
# Secured access
Source: https://e2b.mintlify.app/docs/sandbox/secured-access
Secure access authenticates communication between SDK and sandbox controller.
Sandbox controller runs in sandbox itself and exposes APIs for work with file system, run commands, and generally control the sandbox via our SDK.
Without secure access, anyone with a sandbox ID can access the controller APIs and control the sandbox from inside.
SDKs version `v2.0.0` and above are using secure access by default when creating sandbox. This may not be compatible with older custom templates and you may need to rebuild them.
## Migration path
When using custom templates created before envd `v0.2.0`, you need to rebuild the templates to enable secure access.
Temporarily, you can disable secure access by setting `secure` to `false` during sandbox creation, but this is not recommended for production use because it increases security risks.
You can check the template envd version using the `e2b template list` command or by viewing the templates list on the dashboard.
## Supported versions
All sandboxes based on templates with envd version at least `v0.2.0` already support secure access without any additional changes.
The secure access flag was introduced in `1.5.0` for JavaScript and Python SDKs to be used optionally.
Starting with SDK version `v2.0.0`, sandboxes are created with secure access enabled by default.
## Access sandbox API directly
In some cases, you might want to access sandbox controller APIs directly through its URL, such as when you are not using SDKs.
When secure access is enabled, you must provide an authentication token that was returned during sandbox creation.
Each call to the sandbox controller must include an additional header `X-Access-Token` with the access token value returned during sandbox creation.
For sandbox [upload](/docs/filesystem/upload#upload-with-pre-signed-url) and [download](/docs/filesystem/download#download-with-pre-signed-url) URLs, you need to generate pre-signed URLs. We are advising to use SDK for generating presigned URLs.
## Disable secure access
Disabling secured access is discouraged because it creates security vulnerabilities.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const sandbox = await Sandbox.create({ secure: false }) // Explicitly disable
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
sandbox = Sandbox.create(secure=False) # Explicitly disable
```
# Sandbox snapshots
Source: https://e2b.mintlify.app/docs/sandbox/snapshots
Snapshots let you create a persistent point-in-time capture of a running sandbox, including both its filesystem and memory state.
You can then use a snapshot to spawn new sandboxes that start from the exact same state.
The original sandbox continues running after the snapshot is created, and a single snapshot can be used to create many new sandboxes.
## Prerequisites
Snapshots require templates with envd version `v0.5.0` or above. If you are using a custom template created before envd `v0.5.0`, you need to rebuild it.
You can check the template envd version using the `e2b template list` command or by viewing the templates list on the dashboard.
## Snapshots vs. Pause/Resume
| | Pause/Resume | Snapshots |
| -------------------------- | --------------------------------------------- | --------------------------------------------------- |
| Effect on original sandbox | Pauses (stops) the sandbox | Sandbox briefly pauses, then continues running |
| Relationship | One-to-one — resume restores the same sandbox | One-to-many — snapshot can spawn many new sandboxes |
| Use case | Suspend and resume a single sandbox | Create a reusable checkpoint |
For pause/resume functionality, see [Persistence](/docs/sandbox/persistence).
## Snapshot flow
```mermaid actions={false} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
graph LR
A[Running Sandbox] -->|createSnapshot| B[Snapshotting]
B --> C[Snapshot Created]
B --> A
C -->|Sandbox.create| D[New Sandbox 1]
C -->|Sandbox.create| E[New Sandbox 2]
C -->|Sandbox.create| F[New Sandbox N]
```
The sandbox is briefly paused during the snapshot process but automatically returns to running state. The sandbox ID stays the same after the snapshot completes.
During the snapshot, the sandbox is temporarily paused and resumed. This causes all active connections (e.g. WebSocket, PTY, command streams) to be dropped. Make sure your client handles reconnection properly.
## Create a snapshot
You can create a snapshot from a running sandbox instance.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const sandbox = await Sandbox.create()
// Create a snapshot from a running sandbox
const snapshot = await sandbox.createSnapshot()
console.log('Snapshot ID:', snapshot.snapshotId)
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
sandbox = Sandbox.create()
# Create a snapshot from a running sandbox
snapshot = sandbox.create_snapshot()
print('Snapshot ID:', snapshot.snapshot_id)
```
You can also create a snapshot by sandbox ID using the static method.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
// Create a snapshot by sandbox ID
const snapshot = await Sandbox.createSnapshot(sandboxId)
console.log('Snapshot ID:', snapshot.snapshotId)
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
# Create a snapshot by sandbox ID
snapshot = Sandbox.create_snapshot(sandbox_id)
print('Snapshot ID:', snapshot.snapshot_id)
```
## Create a sandbox from a snapshot
The snapshot ID can be used directly with `Sandbox.create()` to spawn a new sandbox from the snapshot. The new sandbox starts with the exact filesystem and memory state captured in the snapshot.
```js JavaScript & TypeScript highlight={5} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const snapshot = await sandbox.createSnapshot()
// Create a new sandbox from the snapshot
const newSandbox = await Sandbox.create(snapshot.snapshotId)
```
```python Python highlight={5} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
snapshot = sandbox.create_snapshot()
# Create a new sandbox from the snapshot
new_sandbox = Sandbox.create(snapshot.snapshot_id)
```
## List snapshots
You can list all snapshots. The method returns a paginator for iterating through results.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const paginator = Sandbox.listSnapshots()
const snapshots = []
while (paginator.hasNext) {
const items = await paginator.nextItems()
snapshots.push(...items)
}
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
paginator = Sandbox.list_snapshots()
snapshots = []
while paginator.has_next:
items = paginator.next_items()
snapshots.extend(items)
```
### Filter by sandbox
You can filter snapshots created from a specific sandbox.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const paginator = Sandbox.listSnapshots({ sandboxId: 'your-sandbox-id' })
const snapshots = await paginator.nextItems()
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
paginator = Sandbox.list_snapshots(sandbox_id="your-sandbox-id")
snapshots = paginator.next_items()
```
## Delete a snapshot
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
// Returns true if deleted, false if the snapshot was not found
const deleted = await Sandbox.deleteSnapshot(snapshot.snapshotId)
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
Sandbox.delete_snapshot(snapshot.snapshot_id)
```
## Snapshots vs. Templates
Both snapshots and [templates](/docs/template/quickstart) create reusable starting points for sandboxes, but they solve different problems.
| | Templates | Snapshots |
| --------------- | ---------------------------------------------------- | ---------------------------------------------- |
| Defined by | Declarative code (Template builder) | Capturing a running sandbox |
| Reproducibility | Same definition produces the same sandbox every time | Captures whatever state exists at that moment |
| Best for | Repeatable base environments | Checkpointing, rollback, forking runtime state |
Use templates when every sandbox should start from an identical, known state — pre-installed tools, fixed configurations, consistent environments.
Use snapshots when you need to capture or fork live runtime state that depends on what happened during execution.
## Use cases
* **Checkpointing agent work** — an AI agent has loaded data and produced partial results in memory. Snapshot it so you can resume or fork from that point later.
* **Rollback points** — snapshot before a risky or expensive operation (running untrusted code, applying a migration, refactoring a web app). If it fails, rollback - spawn a fresh sandbox from the snapshot before the operation happened.
* **Forking workflows** — spawn multiple sandboxes from the same snapshot to explore different approaches in parallel.
* **Cached sandboxes** — avoid repeating expensive setup by snapshotting a sandbox that has already loaded a large dataset or started a long-running process.
* **Sharing state** — one user or agent configures an environment interactively, snapshots it, and others start from that exact state.
# SSH access
Source: https://e2b.mintlify.app/docs/sandbox/ssh-access
Connect to your sandbox via SSH using a WebSocket proxy
SSH access enables remote terminal sessions, SCP/SFTP file transfers, and integration with tools that expect SSH connectivity.
## Quickstart
Define a template with OpenSSH server and [websocat](https://github.com/vi/websocat):
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
// template.ts
import { Template, waitForPort } from 'e2b'
export const template = Template()
.fromUbuntuImage('25.04')
.aptInstall(['openssh-server'])
.runCmd([
'curl -fsSL -o /usr/local/bin/websocat https://github.com/vi/websocat/releases/latest/download/websocat.x86_64-unknown-linux-musl',
'chmod a+x /usr/local/bin/websocat',
], { user: 'root' })
.setStartCmd('sudo websocat -b --exit-on-eof ws-l:0.0.0.0:8081 tcp:127.0.0.1:22', waitForPort(8081))
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
# template.py
from e2b import Template, wait_for_port
template = (
Template()
.from_ubuntu_image("25.04")
.apt_install(["openssh-server"])
.run_cmd([
"curl -fsSL -o /usr/local/bin/websocat https://github.com/vi/websocat/releases/latest/download/websocat.x86_64-unknown-linux-musl",
"chmod a+x /usr/local/bin/websocat",
], user="root")
.set_start_cmd("sudo websocat -b --exit-on-eof ws-l:0.0.0.0:8081 tcp:127.0.0.1:22", wait_for_port(8081))
)
```
Build the template:
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
// build.ts
import { Template, defaultBuildLogger } from 'e2b'
import { template as sshTemplate } from './template'
await Template.build(sshTemplate, 'ssh-ready', {
cpuCount: 2,
memoryMB: 2048,
onBuildLogs: defaultBuildLogger(),
})
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
# build.py
from e2b import Template, default_build_logger
from template import template as ssh_template
Template.build(ssh_template, "ssh-ready",
cpu_count=2,
memory_mb=2048,
on_build_logs=default_build_logger(),
)
```
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const sbx = await Sandbox.create('ssh-ready')
console.log(sbx.sandboxId)
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
sbx = Sandbox.create("ssh-ready")
print(sbx.sandbox_id)
```
```bash macOS theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
# Install websocat
brew install websocat
# Connect to your sandbox
ssh -o 'ProxyCommand=websocat --binary -B 65536 - wss://8081-%h.e2b.app' user@
```
```bash Linux theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
# Install websocat
sudo curl -fsSL -o /usr/local/bin/websocat https://github.com/vi/websocat/releases/latest/download/websocat.x86_64-unknown-linux-musl
sudo chmod a+x /usr/local/bin/websocat
# Connect to your sandbox
ssh -o 'ProxyCommand=websocat --binary -B 65536 - wss://8081-%h.e2b.app' user@
```
***
## How it works
This method uses [websocat](https://github.com/vi/websocat) to proxy SSH connections over WebSocket through the sandbox's exposed ports.
```
┌───────────────────────────────────────────────────────────┐
│ Your Machine │
│ ┌──────────┐ ProxyCommand ┌──────────────────┐ │
│ │ SSH │ ────────────────── │ websocat │ │
│ │ Client │ │ (WebSocket) │ │
│ └──────────┘ └─────────┬────────┘ │
└────────────────────────────────────────────┼──────────────┘
│
wss://8081-.e2b.app
│
┌────────────────────────────────────────────┼──────────────┐
│ E2B Sandbox ▼ │
│ ┌──────────────────┐ │
│ │ websocat │ │
│ │ (WS → TCP:22) │ │
│ └─────────┬────────┘ │
│ │ │
│ ┌─────────▼────────┐ │
│ │ SSH Server │ │
│ │ (OpenSSH) │ │
│ └──────────────────┘ │
└───────────────────────────────────────────────────────────┘
```
# Need help?
Source: https://e2b.mintlify.app/docs/support
The fastest way to reach our support team is through the **Contact Support** button in your [E2B Dashboard](https://e2b.dev/dashboard?support=true). You'll find it in the bottom-left corner of the dashboard.
Open the E2B Dashboard to contact our support team directly.
You can also reach us through these channels:
# Base image
Source: https://e2b.mintlify.app/docs/template/base-image
How to define a base image for your template
## Creating template
When creating a template, you can specify options:
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
const template = Template({
fileContextPath: ".", // Custom file context path
fileIgnorePatterns: [".git", "node_modules"], // File patterns to ignore
});
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
template = Template(
file_context_path=".", # Custom file context path
file_ignore_patterns=[".git", "node_modules"], # File patterns to ignore
)
```
**File ignoring**: The SDK automatically reads `.dockerignore` files and combines them with your `fileIgnorePatterns` (TypeScript) or `file_ignore_patterns` (Python). Files matching these patterns are excluded from uploads and hash calculations.
## Defining base image
Every template starts with a base image that provides the foundation for your sandbox environment.
### Predefined base images
Use convenience methods for common base images with Ubuntu, Debian, Python, Node.js, or Bun:
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
template.fromUbuntuImage("22.04"); // ubuntu:22.04
template.fromDebianImage("stable-slim"); // debian:stable-slim
template.fromPythonImage("3.13"); // python:3.13
template.fromNodeImage("lts"); // node:lts
template.fromBunImage("1.3"); // oven/bun:1.3
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
template.from_ubuntu_image("22.04") # ubuntu:22.04
template.from_debian_image("stable-slim") # debian:stable-slim
template.from_python_image("3.13") # python:3.13
template.from_node_image("lts") # node:lts
template.from_bun_image("1.3") # oven/bun:1.3
```
### Custom base image
Use any Docker image from Docker Hub or other registries:
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
template.fromImage("custom-image:latest");
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
template.from_image("custom-image:latest")
```
### Default E2B base image
Use the default E2B base image, which comes pre-configured for sandbox environments:
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
template.fromBaseImage(); // e2bdev/base
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
template.from_base_image() # e2bdev/base
```
### Build from existing template
Extend an existing template from your team or organization:
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
template.fromTemplate("my-template"); // Your team's template
template.fromTemplate("acme/other-template"); // Full namespaced reference
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
template.from_template("my-template") # Your team's template
template.from_template("acme/other-template") # Full namespaced reference
```
You can only call base image methods once per template. Subsequent calls will throw an error.
## Parsing existing Dockerfiles
Convert existing Dockerfiles to template format using `fromDockerfile()`:
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
const dockerfileContent = `
FROM ubuntu:22.04
RUN apt-get update && apt-get install -y curl
WORKDIR /app
COPY . .
ENV NODE_ENV=production
ENV PORT=3000
USER appuser`;
const template = Template()
.fromDockerfile(dockerfileContent)
.setStartCmd("npm start", waitForTimeout(5_000));
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
dockerfile_content = """
FROM ubuntu:22.04
RUN apt-get update && apt-get install -y curl
WORKDIR /app
COPY . .
ENV NODE_ENV=production
ENV PORT=3000
USER appuser
"""
template = (
Template()
.from_dockerfile(dockerfile_content)
.set_start_cmd("npm start", wait_for_timeout(5_000))
)
```
### Dockerfile instructions support
| Instruction | Supported | Behavior |
| :------------------- | :--------------------------: | :------------------------------------------------------------------------------------------------ |
| `FROM` | | Sets base image |
| `RUN` | | Converts to `runCmd()` / `run_cmd()` |
| `COPY` / `ADD` | | Converts to `copy()` |
| `WORKDIR` | | Converts to `setWorkdir()` / `set_workdir()` |
| `USER` | | Converts to `setUser()` / `set_user()` |
| `ENV` | | Converts to `setEnvs()` / `set_envs()`; supports both `ENV key=value` and `ENV key value` formats |
| `CMD` / `ENTRYPOINT` | | Converts to `setStartCmd()` / `set_start_cmd()` with 20 seconds timeout as ready command |
| `EXPOSE` | | Skipped (not supported) |
| `VOLUME` | | Skipped (not supported) |
Multi-stage Dockerfiles are not supported.
# Build
Source: https://e2b.mintlify.app/docs/template/build
How to build the template
## Build and wait for completion
The `build` method builds the template and waits for the build to complete. It returns build information including the template ID and build ID.
```typescript JavaScript & TypeScript wrap theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
const buildInfo = await Template.build(template, 'my-template', {
cpuCount: 2, // CPU cores
memoryMB: 2048, // Memory in MB
skipCache: false, // Configure cache skip (except for files)
onBuildLogs: defaultBuildLogger(), // Log callback receives LogEntry objects
apiKey: 'your-api-key', // Override API key
domain: 'your-domain', // Override domain
})
// buildInfo contains: { name, templateId, buildId }
```
```python Python wrap theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
build_info = Template.build(
template,
'my-template',
cpu_count=2, # CPU cores
memory_mb=2048, # Memory in MB
skip_cache=False, # Configure cache skip (except for files)
on_build_logs=default_build_logger(), # Log callback receives LogEntry objects
api_key="your-api-key", # Override API key
domain="your-domain", # Override domain
)
# build_info contains: BuildInfo(name, template_id, build_id)
```
## Build in background
The `buildInBackground` method starts the build process and returns immediately without waiting for completion. This is useful when you want to trigger a build and check its status later.
```typescript JavaScript & TypeScript wrap theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
const buildInfo = await Template.buildInBackground(template, 'my-template', {
cpuCount: 2,
memoryMB: 2048,
})
// Returns immediately with: { name, templateId, buildId }
```
```python Python wrap theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
build_info = Template.build_in_background(
template,
'my-template',
cpu_count=2,
memory_mb=2048,
)
# Returns immediately with: BuildInfo(name, template_id, build_id)
```
## Check build status
Use `getBuildStatus` to check the status of a build started with `buildInBackground`.
```typescript JavaScript & TypeScript wrap theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
const status = await Template.getBuildStatus(buildInfo, {
logsOffset: 0, // Optional: offset for fetching logs
})
// status contains: { status: 'building' | 'ready' | 'error', logEntries: [...] }
```
```python Python wrap theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
status = Template.get_build_status(
build_info,
logs_offset=0, # Optional: offset for fetching logs
)
# status contains build status and logs
```
## Example: Background build with status polling
```typescript JavaScript & TypeScript wrap theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
// Start build in background
const buildInfo = await Template.buildInBackground(template, 'my-template', {
cpuCount: 2,
memoryMB: 2048,
})
// Poll for build status
let logsOffset = 0
let status = 'building'
while (status === 'building') {
const buildStatus = await Template.getBuildStatus(buildInfo, {
logsOffset,
})
logsOffset += buildStatus.logEntries.length
status = buildStatus.status
buildStatus.logEntries.forEach(
(logEntry) => console.log(logEntry.toString())
)
// Wait for a short period before checking the status again
await new Promise(resolve => setTimeout(resolve, 2000))
}
if (status === 'ready') {
console.log('Build completed successfully')
} else {
console.error('Build failed')
}
```
```python Python wrap theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
# Start build in background
build_info = Template.build_in_background(
template,
'my-template',
cpu_count=2,
memory_mb=2048,
)
# Poll for build status
import time
logs_offset = 0
status = "building"
while status == "building":
build_status = Template.get_build_status(
build_info,
logs_offset=logs_offset,
)
logs_offset += len(build_status.log_entries)
status = build_status.status.value
for log_entry in build_status.log_entries:
print(log_entry)
# Wait for a short period before checking the status again
time.sleep(2)
if status == "ready":
print("Build completed successfully")
else:
print("Build failed")
```
# Caching
Source: https://e2b.mintlify.app/docs/template/caching
How the caching process works
The caching concept is similar to [Docker's layer caching](https://docs.docker.com/build/cache/). For each layer command (`.copy()`, `.runCmd()`, `.setEnvs()`, etc.), we create a new layer on top of the existing.
Each layer is cached based on the command and its inputs (e.g., files copied, command executed, environment variables set).
If a layer command is unchanged and its inputs are the same as in any previous build, we reuse the cached layer instead of rebuilding it.
This significantly speeds up the build process, especially for large templates with many layers.
The cache is scoped to the team, so even if you have multiple templates, they can share the same cache if they have identical layers.
## Invalidation
You can invalidate the caches only partially, or for the whole template.
### Partial
Force rebuild from the next instruction, use the method:
```typescript JavaScript & TypeScript highlight={3} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
const template = Template()
.fromBaseImage()
.skipCache()
.runCmd("echo 'Hello, World!'")
```
```python Python highlight={4} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
template = (
Template()
.from_base_image()
.skip_cache()
.run_cmd("echo 'Hello, World!'")
)
```
This will force rebuild from the next instruction, invalidating the cache for all subsequent instructions in the template.
### Whole template
To force rebuild the whole template, you can use also `skipCache`/`skip_cache` parameter in the `Template.build` method:
```typescript JavaScript & TypeScript wrap highlight={2} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
Template.build(template, 'my-template', {
skipCache: true, // Configure cache skip (except for files)
})
```
```python Python wrap highlight={4} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
Template.build(
template,
'my-template',
skip_cache=True, # Configure cache skip (except for files)
)
```
This will skip the cache usage for the whole template build.
## Files caching
When using the `.copy()` command, we cache the files based on their content. If the files haven't changed since the last build, we reuse them from the files cache.
We differ from Docker here. Because we build the template on our infrastructure, we use improved caching on files level.
Even if you invalidate the layer before `.copy()` (e.g., by changing ENV variables), we'll reuse the already uploaded files.
The `copy()` command will still be re-executed, but the files for the layer will be reused from the files cache, no need to upload them from your computer again.
To invalidate the cache for all subsequent instructions in the template **AND** the layer files cache, use the `forceUpload`/`force_upload` parameter.
```typescript JavaScript & TypeScript highlight={3} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
const template = Template()
.fromBaseImage()
.copy("config.json", "/app/config.json", { forceUpload: true })
```
```python Python highlight={4} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
template = (
Template()
.from_base_image()
.copy("config.json", "/app/config.json", force_upload=True)
)
```
## Use case for caching
You can leverage caching to create templates with multiple variants (e.g., different RAM or CPU) while reusing the common layers.
When building the template, just change the template name to a specific RAM/CPU configuration (e.g., `my-template-2cpu-2gb`, `my-template-1cpu-4gb`), keep the rest of the template definition the same, and the build process will reuse the cached layers.
## Optimize build times
To optimize build times, place frequently changing commands (e.g., copying source code) towards the end of your template definition. This way, earlier layers can be cached and reused more often.
# Defining template
Source: https://e2b.mintlify.app/docs/template/defining-template
How to create your own template
### Method chaining
All template methods return the template instance, allowing for fluent API usage:
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
const template = Template()
.fromUbuntuImage("22.04")
.aptInstall(["curl"]) // necessary for waitForPort
.setWorkdir('/app')
.copy("package.json", "/app/package.json")
.runCmd("npm install")
.setStartCmd("npm start", waitForPort(3000));
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
template = (
Template()
.from_ubuntu_image("22.04")
.set_workdir("/app")
.copy("package.json", "/app/package.json")
.run_cmd("npm install")
.set_start_cmd("npm start", wait_for_timeout(10_000)))
```
### User and workdir
Set the working directory and user for the template:
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
// Set working directory
template.setWorkdir("/app");
// Set user (runs subsequent commands as this user)
template.setUser('node')
template.setUser("1000:1000"); // User ID and group ID
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
# Set working directory
template.set_workdir("/app")
# Set user (runs subsequent commands as this user)
template.set_user("node")
template.set_user("1000:1000") # User ID and group ID
```
### Copying files
Copy files from your local filesystem to the template:
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
// Copy a single file
template.copy("package.json", "/app/package.json");
// Copy multiple files to same destination
template.copy(["file1", "file2"], "/app/file")
// Multiple copy operations using copyItems
template.copyItems([
{ src: "src/", dest: "/app/src/" },
{ src: "package.json", dest: "/app/package.json" },
]);
// Copy with user and mode options
template.copy("config.json", "/app/config.json", {
user: "appuser",
mode: 0o644,
});
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
# Copy a single file
template.copy("package.json", "/app/package.json")
# Copy multiple files to the same destination
template.copy(["file1", "file2"], "/app/file")
# Multiple copy operations using copy_items
template.copy_items([
{"src": "src/", "dest": "/app/src/"},
{"src": "package.json", "dest": "/app/package.json"},
])
# Copy with user and mode options
template.copy("config.json", "/app/config.json", user="appuser", mode=0o644)
```
### File operations
Perform various file operations during template build:
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
// Remove files or directories
template.remove("/tmp/temp-file.txt");
template.remove("/old-directory", { recursive: true });
template.remove("/file.txt", { force: true });
// Rename files or directories
template.rename("/old-name.txt", "/new-name.txt");
template.rename("/old-dir", "/new-dir", { force: true });
// Create directories
template.makeDir("/app/logs");
template.makeDir("/app/data", { mode: 0o755 });
// Create symbolic links
template.makeSymlink("/app/data", "/app/logs/data");
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
# Remove files or directories
template.remove("/tmp/old-file")
template.remove("/tmp/old-dir", recursive=True)
template.remove("/tmp/file", force=True) # Force removal
# Rename files or directories
template.rename("/old/path", "/new/path")
template.rename("/old/path", "/new/path", force=True) # Force rename
# Create directories
template.make_dir("/app/data")
template.make_dir("/app/data", mode=0o755) # Set permissions
# Create symbolic links
template.make_symlink("/path/to/target", "/path/to/link")
```
### Installing packages
Install packages using package managers:
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
// Install Python packages
template.pipInstall(['requests', 'pandas', 'numpy'])
// Install Python packages (user)
template.pipInstall(['requests', 'pandas', 'numpy'], { g: false })
// Install Node.js packages
template.npmInstall(['express', 'lodash'])
// Install Node.js packages (global)
template.npmInstall(['express', 'lodash'], { g: true })
// Install Bun packages
template.bunInstall(['express', 'lodash'])
// Install Bun packages (global)
template.bunInstall(['express', 'lodash'], { g: true })
// Install system packages (automatically runs apt update)
template.aptInstall(['curl', 'wget', 'git'])
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
# Install Python packages
template.pip_install(["requests", "pandas", "numpy"])
# Install Python packages (user)
template.pip_install(["requests", "pandas", "numpy"], g=False)
# Install Node.js packages
template.npm_install(["express", "lodash"])
# Install Node.js packages (global)
template.npm_install(["express", "lodash"], g=True)
# Install Bun packages
template.bun_install(["express", "lodash"])
# Install Bun packages (global)
template.bun_install(["express", "lodash"], g=True)
# Install system packages (Ubuntu/Debian)
template.apt_install(["curl", "wget", "git"])
```
### Git operations
Clone Git repositories during template build (requires `git` to be installed):
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
// Clone a repository
template.gitClone('https://github.com/user/repo.git')
// Clone a repository to a specific path
template.gitClone('https://github.com/user/repo.git', '/app/repo')
// Clone a specific branch
template.gitClone('https://github.com/user/repo.git', '/app/repo', {
branch: 'main',
})
// Shallow clone with depth limit
template.gitClone('https://github.com/user/repo.git', '/app/repo', {
depth: 1,
})
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
# Clone a repository
template.git_clone("https://github.com/user/repo.git")
# Clone a repository to a specific path
template.git_clone("https://github.com/user/repo.git", "/app/repo")
# Clone a specific branch
template.git_clone("https://github.com/user/repo.git", "/app/repo", branch="main")
# Shallow clone with depth limit
template.git_clone("https://github.com/user/repo.git", "/app/repo", depth=1)
```
### Environment variables
Environment variables set in template definition are only available during template build.
[How to setup environment variables in sandbox?](/docs/sandbox/environment-variables)
Set environment variables in the template:
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
template.setEnvs({
NODE_ENV: 'production',
API_KEY: 'your-api-key',
DEBUG: 'true',
})
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
template.set_envs({
"NODE_ENV": "production",
"API_KEY": "your-api-key",
"DEBUG": "true",
})
```
### Running commands
Execute shell commands during template build:
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
// Run a single command
template.runCmd('apt-get update && apt-get install -y curl')
// Run multiple commands
template.runCmd(['apt-get update', 'apt-get install -y curl', 'curl --version'])
// Run commands as a specific user
template.runCmd('npm install', { user: 'node' })
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
# Run a single command
template.run_cmd("apt-get update && apt-get install -y curl")
# Run multiple commands
template.run_cmd(["apt-get update", "apt-get install -y curl", "curl --version"])
# Run command as specific user
template.run_cmd("npm install", user="node")
```
# Error handling
Source: https://e2b.mintlify.app/docs/template/error-handling
Handle errors in your template
The SDK provides specific error types:
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { AuthError, BuildError, FileUploadError } from 'e2b';
try {
await Template.build(template, 'my-template');
} catch (error) {
if (error instanceof AuthError) {
console.error("Authentication failed:", error.message);
} else if (error instanceof FileUploadError) {
console.error("File upload failed:", error.message);
} else if (error instanceof BuildError) {
console.error("Build failed:", error.message);
}
}
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import AuthError, BuildError, FileUploadError
try:
Template.build(template, 'my-template')
except AuthError as error:
print(f"Authentication failed: {error}")
except FileUploadError as error:
print(f"File upload failed: {error}")
except BuildError as error:
print(f"Build failed: {error}")
```
# Claude Code
Source: https://e2b.mintlify.app/docs/template/examples/claude-code
Claude Code Agent available in a sandbox
For a complete guide on running Claude Code in E2B sandboxes — including working with repositories, streaming output, and connecting MCP tools — see the [Agents in Sandbox: Claude Code](/docs/agents/claude-code) guide.
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
// template.ts
import { Template } from 'e2b'
export const template = Template()
.fromNodeImage('24')
.aptInstall(['curl', 'git', 'ripgrep'])
// Claude Code will be available globally as "claude"
.npmInstall('@anthropic-ai/claude-code@latest', { g: true })
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
# template.py
from e2b import Template
template = (
Template()
.from_node_image("24")
.apt_install(["curl", "git", "ripgrep"])
# Claude Code will be available globally as "claude"
.npm_install("@anthropic-ai/claude-code@latest", g=True)
)
```
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
// build.ts
import { Template, defaultBuildLogger } from 'e2b'
import { template as claudeCodeTemplate } from './template'
Template.build(claudeCodeTemplate, 'claude-code', {
cpuCount: 1,
memoryMB: 1024,
onBuildLogs: defaultBuildLogger(),
})
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
# build.py
from e2b import Template, default_build_logger
from .template import template as claudeCodeTemplate
Template.build(claudeCodeTemplate, 'claude-code',
cpu_count=1,
memory_mb=1024,
on_build_logs=default_build_logger(),
)
```
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
// sandbox.ts
import { Sandbox } from 'e2b'
const sbx = await Sandbox.create('claude-code', {
envs: {
ANTHROPIC_API_KEY: '',
},
})
console.log('Sandbox created', sbx.sandboxId)
// Print help for Claude Code
// const result = await sbx.commands.run('claude --help')
// console.log(result.stdout)
// Run a prompt with Claude Code
const result = await sbx.commands.run(
`claude --dangerously-skip-permissions -p 'Create a hello world index.html'`,
{ timeoutMs: 0 }
)
console.log(result.stdout)
sbx.kill()
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
# sandbox.py
from e2b import Sandbox
sbx = Sandbox(
'claude-code',
envs={
'ANTHROPIC_API_KEY': '',
},
)
print("Sandbox created", sbx.sandbox_id)
# Print help for Claude Code
# result = sbx.commands.run('claude --help')
# print(result.stdout)
# Run a prompt with Claude Code
result = sbx.commands.run(
"claude --dangerously-skip-permissions -p 'Create a hello world index.html'",
timeout=0,
)
print(result.stdout)
sbx.kill()
```
# Desktop
Source: https://e2b.mintlify.app/docs/template/examples/desktop
Sandbox with Ubuntu Desktop and VNC access
This template creates a sandbox with a full Ubuntu 22.04 desktop environment, including the XFCE desktop, common applications, and VNC streaming for remote access. It's ideal for building AI agents that need to interact with graphical user interfaces.
The template includes:
* **Ubuntu 22.04** with XFCE desktop environment
* **VNC streaming** via [noVNC](https://novnc.com/) for browser-based access
* **Pre-installed applications**: LibreOffice, text editors, file manager, and common utilities
* **Automation tools**: [xdotool](https://github.com/jordansissel/xdotool) and [scrot](https://github.com/resurrecting-open-source-projects/scrot) for programmatic desktop control
## Template definition
The template installs the desktop environment, sets up VNC streaming via [x11vnc](https://github.com/LibVNC/x11vnc) and noVNC, and configures a startup script.
```typescript JavaScript & TypeScript expandable theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
// template.ts
import { Template, waitForPort } from 'e2b'
export const template = Template()
.fromUbuntuImage('22.04')
// Desktop environment and system utilities
.runCmd([
'yes | unminimize',
'apt-get update',
'apt-get install -y \
xserver-xorg \
xorg \
x11-xserver-utils \
xvfb \
x11-utils \
xauth \
xfce4 \
xfce4-goodies \
util-linux \
sudo \
curl \
git \
wget \
python3-pip \
xdotool \
scrot \
ffmpeg \
x11vnc \
net-tools \
netcat \
x11-apps \
libreoffice \
xpdf \
gedit \
xpaint \
tint2 \
galculator \
pcmanfm',
'apt-get clean',
'rm -rf /var/lib/apt/lists/*',
])
// Streaming server setup
.runCmd([
'git clone --branch e2b-desktop https://github.com/e2b-dev/noVNC.git /opt/noVNC',
'ln -s /opt/noVNC/vnc.html /opt/noVNC/index.html',
'git clone --branch v0.12.0 https://github.com/novnc/websockify /opt/noVNC/utils/websockify',
])
// Set default terminal
.runCmd(
'ln -sf /usr/bin/xfce4-terminal.wrapper /etc/alternatives/x-terminal-emulator'
)
.copy('start_command.sh', '/start_command.sh')
.runCmd('chmod +x /start_command.sh')
.setStartCmd('/start_command.sh', waitForPort(6080))
```
```python Python expandable theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
# template.py
from e2b import Template, wait_for_port
template = (
Template()
.from_image("ubuntu:22.04")
# Initial system setup and packages
# We are not using .apt_install() here because some packages have interactive prompts (keyboard layout setup, etc.)
.run_cmd(
[
"yes | unminimize",
"apt-get update",
"apt-get install -y \
xserver-xorg \
xorg \
x11-xserver-utils \
xvfb \
x11-utils \
xauth \
xfce4 \
xfce4-goodies \
util-linux \
sudo \
curl \
git \
wget \
python3-pip \
xdotool \
scrot \
ffmpeg \
x11vnc \
net-tools \
netcat \
x11-apps \
libreoffice \
xpdf \
gedit \
xpaint \
tint2 \
galculator \
pcmanfm",
"apt-get clean",
"rm -rf /var/lib/apt/lists/*",
]
)
# Setup NoVNC and websockify
.run_cmd(
[
"git clone --branch e2b-desktop https://github.com/e2b-dev/noVNC.git /opt/noVNC",
"ln -s /opt/noVNC/vnc.html /opt/noVNC/index.html",
"git clone --branch v0.12.0 https://github.com/novnc/websockify /opt/noVNC/utils/websockify",
]
)
# Set default terminal
.run_cmd(
"ln -sf /usr/bin/xfce4-terminal.wrapper /etc/alternatives/x-terminal-emulator"
)
# Copy the start command
.copy("start_command.sh", "/start_command.sh")
.run_cmd("chmod +x /start_command.sh")
# Set start command to launch the desktop environment
.set_start_cmd("/start_command.sh", wait_for_port(6080))
)
```
## Startup script
The startup script initializes the virtual display using [Xvfb](https://www.x.org/releases/X11R7.6/doc/man/man1/Xvfb.1.xhtml) (X Virtual Framebuffer), launches the XFCE desktop session, starts the VNC server, and exposes the desktop via noVNC on port 6080. This script runs automatically when the sandbox starts.
```bash start_command.sh theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
#!/bin/bash
# Set display
export DISPLAY=${DISPLAY:-:0}
# Start Xvfb
Xvfb $DISPLAY -ac -screen 0 1024x768x24 -nolisten tcp &
sleep 2
# Start XFCE session
startxfce4 &
sleep 5
# Start VNC server
x11vnc -bg -display $DISPLAY -forever -wait 50 -shared -rfbport 5900 -nopw \
-noxdamage -noxfixes -nowf -noscr -ping 1 -repeat -speeds lan &
sleep 2
# Start noVNC server
cd /opt/noVNC/utils && ./novnc_proxy --vnc localhost:5900 --listen 6080 --web /opt/noVNC --heartbeat 30 &
sleep 2
```
## Building the template
Build the template with increased CPU and memory allocation to handle the desktop environment installation. The build process may take several minutes due to the size of the packages being installed.
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
// build.ts
import { Template, defaultBuildLogger } from 'e2b'
import { template as desktopTemplate } from './template'
await Template.build(desktopTemplate, 'desktop', {
cpuCount: 8,
memoryMB: 8192,
onBuildLogs: defaultBuildLogger(),
})
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
# build.py
from e2b import Template, default_build_logger
from .template import template as desktopTemplate
Template.build(desktopTemplate, 'desktop',
cpu_count=8,
memory_mb=8192,
on_build_logs=default_build_logger(),
)
```
# Docker
Source: https://e2b.mintlify.app/docs/template/examples/docker
Sandbox with Docker or Docker Compose installed for running containers
## Docker
### Template
Use the official installation script from [get.docker.com](https://get.docker.com). The `hello-world` container run validates the installation.
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
// template.ts
import { Template } from 'e2b'
export const template = Template()
.fromUbuntuImage('24.04')
.runCmd('curl -fsSL https://get.docker.com | sudo sh')
.runCmd('sudo docker run --rm hello-world')
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
# template.py
from e2b import Template
template = (
Template()
.from_ubuntu_image("24.04")
.run_cmd("curl -fsSL https://get.docker.com | sudo sh")
.run_cmd("sudo docker run --rm hello-world")
)
```
### Build
We recommend at least 2 CPUs and 2 GB of RAM for running Docker containers. **With lower RAM, your sandbox might run out of memory.**
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
// build.ts
import { Template, defaultBuildLogger } from 'e2b'
import { template as dockerTemplate } from './template'
Template.build(dockerTemplate, 'docker', {
cpuCount: 2,
memoryMB: 2048,
onBuildLogs: defaultBuildLogger(),
})
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
# build.py
from e2b import Template, default_build_logger
from .template import template as dockerTemplate
Template.build(dockerTemplate, 'docker',
cpu_count=2,
memory_mb=2048,
on_build_logs=default_build_logger(),
)
```
### Run
Run an Alpine container that prints a hello message.
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
// sandbox.ts
import { Sandbox } from 'e2b'
const sbx = await Sandbox.create('docker')
const result = await sbx.commands.run('sudo docker run --rm alpine echo "Hello from Alpine!"')
console.log(result.stdout)
await sbx.kill()
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
# sandbox.py
from e2b import Sandbox
sbx = Sandbox.create('docker')
result = sbx.commands.run('sudo docker run --rm alpine echo "Hello from Alpine!"')
print(result.stdout)
sbx.kill()
```
## Docker Compose
This example installs Docker and Docker Compose, then validates the setup with a Compose version check and a sample Compose run.
### Template
Create a new file named `template-compose.ts` (or `template_compose.py`).
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
// template-compose.ts
import { Template } from 'e2b'
export const composeTemplate = Template()
.fromUbuntuImage('24.04')
.runCmd([
'set -euxo pipefail',
'sudo apt-get update',
'sudo DEBIAN_FRONTEND=noninteractive apt-get install -y docker.io',
'sudo usermod -aG docker user',
'sudo DEBIAN_FRONTEND=noninteractive apt-get install -y docker-compose-plugin || true',
'sudo DEBIAN_FRONTEND=noninteractive apt-get install -y docker-compose-v2 || true',
'sudo DEBIAN_FRONTEND=noninteractive apt-get install -y docker-compose || true',
'sudo docker compose version || sudo docker-compose --version',
])
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
# template_compose.py
from e2b import Template
compose_template = (
Template()
.from_ubuntu_image("24.04")
.run_cmd(
[
"set -euxo pipefail",
"sudo apt-get update",
"sudo DEBIAN_FRONTEND=noninteractive apt-get install -y docker.io",
"sudo usermod -aG docker user",
"sudo DEBIAN_FRONTEND=noninteractive apt-get install -y docker-compose-plugin || true",
"sudo DEBIAN_FRONTEND=noninteractive apt-get install -y docker-compose-v2 || true",
"sudo DEBIAN_FRONTEND=noninteractive apt-get install -y docker-compose || true",
"sudo docker compose version || sudo docker-compose --version",
]
)
)
```
Expected result: you now have a local `template-compose.ts` or `template_compose.py` file.
### Build
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
// build-compose.ts
import { Template, defaultBuildLogger } from 'e2b'
import { composeTemplate } from './template-compose'
Template.build(composeTemplate, 'docker-compose', {
cpuCount: 2,
memoryMB: 2048,
onBuildLogs: defaultBuildLogger(),
})
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
# build_compose.py
from e2b import Template, default_build_logger
from template_compose import compose_template
Template.build(compose_template, "docker-compose",
cpu_count=2,
memory_mb=2048,
on_build_logs=default_build_logger(),
)
```
Expected output (example):
```text theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
BuildInfo(... name='docker-compose', alias='docker-compose', tags=['default'])
```
### Run
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
// sandbox-compose.ts
import { Sandbox } from 'e2b'
const sbx = await Sandbox.create('docker-compose')
await sbx.commands.run('mkdir -p /tmp/docker-compose-test')
await sbx.files.write('/tmp/docker-compose-test/compose.yaml', [
'services:',
' hello:',
' image: busybox:1.36',
' command: ["sh", "-lc", "echo docker-compose-ok"]',
'',
].join('\n'))
const result = await sbx.commands.run(`
set -euxo pipefail
cd /tmp/docker-compose-test
if docker compose version >/dev/null 2>&1; then
docker compose up --abort-on-container-exit --remove-orphans
docker compose down --remove-orphans -v
echo "Docker Compose ran successfully"
elif docker-compose --version >/dev/null 2>&1; then
docker-compose up --abort-on-container-exit --remove-orphans
docker-compose down --remove-orphans -v
echo "Docker Compose ran successfully"
else
echo "No compose command available"
exit 127
fi
`)
console.log(result.stdout)
await sbx.kill()
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
# sandbox_compose.py
from e2b import Sandbox
sbx = Sandbox.create("docker-compose")
sbx.commands.run("mkdir -p /tmp/docker-compose-test")
sbx.files.write(
"/tmp/docker-compose-test/compose.yaml",
"""
services:
hello:
image: busybox:1.36
command: ["sh", "-lc", "echo docker-compose-ok"]
""",
)
result = sbx.commands.run(
"""
set -euxo pipefail
cd /tmp/docker-compose-test
if docker compose version >/dev/null 2>&1; then
docker compose up --abort-on-container-exit --remove-orphans
docker compose down --remove-orphans -v
echo "Docker Compose ran successfully"
elif docker-compose --version >/dev/null 2>&1; then
docker-compose up --abort-on-container-exit --remove-orphans
docker-compose down --remove-orphans -v
echo "Docker Compose ran successfully"
else
echo "No compose command available"
exit 127
fi
""",
)
print(result.stdout)
sbx.kill()
```
Expected output (example):
```text theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
hello_1 | docker-compose-ok
Docker Compose ran successfully
```
# Expo app
Source: https://e2b.mintlify.app/docs/template/examples/expo
Expo web app running in the sandbox using Node.js
Basic Expo app.
The development server runs on port 8081 as soon as the sandbox is ready.
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
// template.ts
import { defaultBuildLogger, waitForURL } from "e2b";
export const template = Template()
.fromNodeImage()
.setWorkdir("/home/user/expo-app")
.runCmd("npx create-expo-app@latest . --yes")
.runCmd("mv /home/user/expo-app/* /home/user/ && rm -rf /home/user/expo-app")
.setWorkdir("/home/user")
.setStartCmd("npx expo start", waitForURL("http://localhost:8081"));
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
# template.py
from e2b import Template, wait_for_url
template = (
Template()
.from_node_image()
.set_workdir("/home/user/expo-app")
.run_cmd("npx create-expo-app@latest . --yes")
.run_cmd("mv /home/user/expo-app/* /home/user/ && rm -rf /home/user/expo-app")
.set_workdir("/home/user")
.set_start_cmd("npx expo start", wait_for_url('http://localhost:8081'))
)
```
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
// build.ts
import { Template, defaultBuildLogger } from 'e2b'
import { template as expoTemplate } from './template'
Template.build(expoTemplate, 'expo-app', {
cpuCount: 4,
memoryMB: 8192,
onBuildLogs: defaultBuildLogger(),
})
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
# build.py
from e2b import Template, default_build_logger
from .template import template as expoTemplate
Template.build(expoTemplate, 'expo-app',
cpu_count=4,
memory_mb=8192,
on_build_logs=default_build_logger(),
)
```
# Next.js app
Source: https://e2b.mintlify.app/docs/template/examples/nextjs
Next.js web app running in the sandbox using Node.js
Basic Next.js app with Tailwind and shadcn UI
The development server runs on port 3000 as soon as the sandbox is ready.
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
// template.ts
import { Template, waitForURL } from 'e2b'
export const template = Template()
.fromNodeImage('21-slim')
.setWorkdir('/home/user/nextjs-app')
.runCmd(
'npx create-next-app@14.2.30 . --ts --tailwind --no-eslint --import-alias "@/*" --use-npm --no-app --no-src-dir'
)
.runCmd('npx shadcn@2.1.7 init -d')
.runCmd('npx shadcn@2.1.7 add --all')
.runCmd(
'mv /home/user/nextjs-app/* /home/user/ && rm -rf /home/user/nextjs-app'
)
.setWorkdir('/home/user')
.setStartCmd('npx next --turbo', waitForURL('http://localhost:3000'))
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
# template.py
from e2b import Template, wait_for_url
template = (
Template()
.from_node_image("21-slim")
.set_workdir("/home/user/nextjs-app")
.run_cmd(
'npx create-next-app@14.2.30 . --ts --tailwind --no-eslint --import-alias "@/*" --use-npm --no-app --no-src-dir'
)
.run_cmd("npx shadcn@2.1.7 init -d")
.run_cmd("npx shadcn@2.1.7 add --all")
.run_cmd("mv /home/user/nextjs-app/* /home/user/ && rm -rf /home/user/nextjs-app")
.set_workdir("/home/user")
.set_start_cmd("npx next --turbo", wait_for_url('http://localhost:3000'))
)
```
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
// build.ts
import { Template, defaultBuildLogger } from 'e2b'
import { template as nextJSTemplate } from './template'
Template.build(nextJSTemplate, 'nextjs-app', {
cpuCount: 4,
memoryMB: 4096,
onBuildLogs: defaultBuildLogger(),
})
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
# build.py
from e2b import Template, default_build_logger
from .template import template as nextjsTemplate
Template.build(nextjsTemplate, 'nextjs-app',
cpu_count=4,
memory_mb=4096,
on_build_logs=default_build_logger(),
)
```
# Next.js app (Bun)
Source: https://e2b.mintlify.app/docs/template/examples/nextjs-bun
Next.js web app running in the sandbox using Bun
Basic Next.js app with Tailwind and shadcn UI using Bun.
The development server runs on port 3000 as soon as the sandbox is ready.
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
// template.ts
import { Template, waitForURL } from 'e2b'
export const template = Template()
.fromBunImage('1.3')
.setWorkdir('/home/user/nextjs-app')
.runCmd(
'bun create next-app --app --ts --tailwind --turbopack --yes --use-bun .'
)
.runCmd('bunx --bun shadcn@latest init -d')
.runCmd('bunx --bun shadcn@latest add --all')
.runCmd(
'mv /home/user/nextjs-app/* /home/user/ && rm -rf /home/user/nextjs-app'
)
.setWorkdir('/home/user')
.setStartCmd('bun --bun run dev --turbo', waitForURL('http://localhost:3000'))
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
# template.py
from e2b import Template, wait_for_url
template = (
Template()
.from_bun_image('1.3')
.set_workdir('/home/user/nextjs-app')
.run_cmd('bun create next-app --app --ts --tailwind --turbopack --yes --use-bun .')
.run_cmd('bunx --bun shadcn@latest init -d')
.run_cmd('bunx --bun shadcn@latest add --all')
.run_cmd('mv /home/user/nextjs-app/* /home/user/ && rm -rf /home/user/nextjs-app')
.set_workdir('/home/user')
.set_start_cmd('bun --bun run dev --turbo', wait_for_url('http://localhost:3000'))
)
```
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
// build.ts
import { Template, defaultBuildLogger } from 'e2b'
import { template as nextJSTemplate } from './template'
Template.build(nextJSTemplate, 'nextjs-app-bun', {
cpuCount: 4,
memoryMB: 4096,
onBuildLogs: defaultBuildLogger(),
})
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
# build.py
from e2b import Template, default_build_logger
from .template import template as nextjsTemplate
Template.build(nextjsTemplate, 'nextjs-app-bun',
cpu_count=4,
memory_mb=4096,
on_build_logs=default_build_logger(),
)
```
# How it works
Source: https://e2b.mintlify.app/docs/template/how-it-works
How the template building process works
## General overview
Every time you are building a sandbox template, we create a container based on the definition.
We extract the container's filesystem, do provisioning and configuration (e.g. installing required dependencies), run layers commands and start a sandbox.
Then, these steps happen:
1. We take the running sandbox.
2. (Only if you specified the [start command](/docs/template/start-ready-command#start-command), otherwise this step is skipped) Execute the start command.
3. Wait for readiness (by default 20 seconds if start command is specified, otherwise immediately ready). Readiness check can be configured using [ready command](/docs/template/start-ready-command#ready-command).
4. Snapshot the sandbox and make it ready for you to spawn it with the SDK.
We call this sandbox snapshot a *sandbox template*.
Snapshots are saved running sandboxes. We serialize and save the whole sandbox's filesystem together with all the
running processes in a way that can be loaded later.
This allows us to load the sandbox in \~80ms any time later with all the processes already running
and the filesystem exactly as it was.
## Default user and workdir
To learn more about default user and workdir, please refer to the [User and workdir](/docs/template/user-and-workdir) section.
## Caching
To learn more about caching, please refer to the [Caching](/docs/template/caching) section.
## Kernel
E2B sandboxes run on an **LTS 6.1 Linux kernel**.\
The exact kernel version available in your sandbox depends on **when your template was built**:
| Template build date (DD.MM.YYYY) | Kernel version |
| -------------------------------- | ----------------- |
| ≥ 27.11.2025 | 6.1.158 (current) |
| \< 27.11.2025 | 6.1.102 |
> **Note:**\
> Kernel versions are fixed at **template build time**.\
> If your template was created on an older kernel, **the kernel cannot be upgraded or changed** for that template.\
> To use a newer kernel, you must **rebuild the template** or create a new one.
You can find the kernel configuration files for each version [here](https://github.com/e2b-dev/fc-kernels/tree/main/configs).
# Logging
Source: https://e2b.mintlify.app/docs/template/logging
How to view logs from the template build
You can retrieve the build logs using the SDK.
## Default logger
We provide a default logger that you can use to filter logs by level:
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Template, defaultBuildLogger } from 'e2b';
await Template.build(template, 'my-template', {
onBuildLogs: defaultBuildLogger({
minLevel: "info", // Minimum log level to show
}),
});
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Template, default_build_logger
Template.build(
template,
'my-template',
on_build_logs=default_build_logger(
min_level="info", # Minimum log level to show
)
)
```
## Custom logger
You can customize how logs are handled:
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
// Simple logging
onBuildLogs: (logEntry) => console.log(logEntry.toString());
// Custom formatting
onBuildLogs: (logEntry) => {
const time = logEntry.timestamp.toISOString();
console.log(`[${time}] ${logEntry.level.toUpperCase()}: ${logEntry.message}`);
};
// Filter by log level
onBuildLogs: (logEntry) => {
if (logEntry.level === "error" || logEntry.level === "warn") {
console.error(logEntry.toString());
}
};
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
# Simple logging
on_build_logs=lambda log_entry: print(log_entry)
# Custom formatting
def custom_logger(log_entry):
time = log_entry.timestamp.isoformat()
print(f"[{time}] {log_entry.level.upper()}: {log_entry.message}")
Template.build(template, 'my-template', on_build_logs=custom_logger)
# Filter by log level
def error_logger(log_entry):
if log_entry.level in ["error", "warn"]:
print(f"ERROR/WARNING: {log_entry}", file=sys.stderr)
Template.build(template, 'my-template', on_build_logs=error_logger)
```
The `onBuildLogs`/`on_build_logs` callback receives structured `LogEntry` objects with the following properties:
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
type LogEntryLevel = 'debug' | 'info' | 'warn' | 'error'
class LogEntry {
constructor(
public readonly timestamp: Date,
public readonly level: LogEntryLevel,
public readonly message: string
)
toString() // Returns formatted log string
}
// Indicates the start of the build process
class LogEntryStart extends LogEntry {
constructor(timestamp: Date, message: string) {
super(timestamp, 'debug', message)
}
}
// Indicates the end of the build process
class LogEntryEnd extends LogEntry {
constructor(timestamp: Date, message: string) {
super(timestamp, 'debug', message)
}
}
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
LogEntryLevel = Literal["debug", "info", "warn", "error"]
@dataclass
class LogEntry:
timestamp: datetime
level: LogEntryLevel
message: str
def __str__(self) -> str: # Returns formatted log string
# Indicates the start of the build process
@dataclass
class LogEntryStart(LogEntry):
level: LogEntryLevel = field(default="debug", init=False)
# Indicates the end of the build process
@dataclass
class LogEntryEnd(LogEntry):
level: LogEntryLevel = field(default="debug", init=False)
```
Together with the `LogEntry` type, there are also `LogEntryStart` and `LogEntryEnd` types that indicate the start and end of the build process. Their default log level is `debug` and you can use them to like this:
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
if (logEntry instanceof LogEntryStart) {
// Build started
return
}
if (logEntry instanceof LogEntryEnd) {
// Build ended
return
}
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
if isinstance(log_entry, LogEntryStart):
# Build started
return
if isinstance(log_entry, LogEntryEnd):
# Build ended
return
```
# V2 migration guide
Source: https://e2b.mintlify.app/docs/template/migration-v2
How to migrate from the legacy template definition
We've made ready for you three ways how to migrate existing template to the new definition format.
* Using the migration command (recommended)
* Using the `fromDockerfile()` method to parse existing Dockerfile
* Building the Docker image manually and using `fromImage()` method
*This is handled for you automatically when using any of the options below.*
The default user for the template is `user` with the working directory set to the user home directory. This is a difference from the Docker defaults.
## Migration command (recommended)
To migrate the existing template definition to the new format, follow these steps:
Install the latest version of the [E2B CLI](https://e2b.dev/docs/cli)
Navigate to the folder of your existing template definition (where you have `e2b.toml` and `e2b.Dockerfile` files).
Your existing `e2b.toml` and `e2b.Dockerfile` files will be renamed to `e2b.toml.old` and `e2b.Dockerfile.old`, respectively, as they are no longer required.
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
e2b template migrate
```
Follow the prompts to complete the migration process.
### Generated output
The migration command generates three files (based on the selected language).
```markdown JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
template.ts - Template definition using the SDK
build.dev.ts - Development build script
build.prod.ts - Production build script
```
```markdown Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
template.py - Template definition using the SDK
build_dev.py - Development build script
build_prod.py - Production build script
```
## Using `fromDockerfile()` Method
If you want to keep using Dockerfile for your template, you can use the `fromDockerfile()` method.
We'll automatically parse the Dockerfile for you and build the template based on it.
You can find more at [Base Image](/docs/template/base-image#parsing-existing-dockerfiles).
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
// template.ts
import { Template } from 'e2b';
const template = Template()
.fromDockerfile(dockerfileContent);
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
# template.py
from e2b import Template
template = (
Template()
.from_dockerfile(dockerfile_content)
)
```
Only a limited set of instructions are supported and converted to equivalent template.
Compatible instructions:
`FROM`, `RUN`, `COPY`, `ADD`, `WORKDIR`, `USER`, `ENV`, `ARG`, `CMD`, `ENTRYPOINT`
After the template definition, you can create the build scripts as described in the [Quickstart](/docs/template/quickstart#create-a-development-build-script) section.
## Using `fromImage()` Method
If you already have a Docker image built, or you want to keep building the Docker image yourself you can use the `fromImage()` method.
Simply build the Docker image for the `linux/amd64` platform, push it to a registry of your choice and reference the image tag in the `fromImage()` method.
You can also specify credentials for private registries as described in the [Base Image](/docs/template/base-image#custom-base-image) section.
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
// template.ts
import { Template } from 'e2b';
const template = Template()
.fromImage("image-tag");
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
# template.py
from e2b import Template
template = (
Template()
.from_image("image-tag")
)
```
# Template names
Source: https://e2b.mintlify.app/docs/template/names
Understanding and managing template names
Template names are unique identifiers used to reference and create sandboxes from your templates. They serve as human-readable names that make it easy to identify and use your templates across your applications.
## What is a template name?
A name is a string identifier that you assign to a template when building it. Once a template is built with a name, you can use that name to create sandboxes from the template.
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
// Build a template with a name
await Template.build(template, 'my-python-env', {
cpuCount: 2,
memoryMB: 2048,
})
// Create a sandbox using the name
const sandbox = await Sandbox.create('my-python-env')
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
# Build a template with a name
Template.build(
template,
'my-python-env',
cpu_count=2,
memory_mb=2048,
)
# Create a sandbox using the name
sandbox = Sandbox.create('my-python-env')
```
## Team-local naming
Template names are scoped to your team. This means:
* Your template named `my-app` is stored as `your-team-slug/my-app`
* You can reference it simply as `my-app` within your team
* Other teams can have their own `my-app` template without conflict
* Public templates should be referenced using the full namespaced format (`team-slug/template-name`)
**Backwards Compatibility**: Existing public templates remain accessible without the team slug prefix. New public templates should be referenced using the full namespaced format (`team-slug/template-name`).
## Common use cases
### Development and production environments
Use different names for different environments:
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
// Development template
await Template.build(template, 'myapp-dev', {
cpuCount: 1,
memoryMB: 1024,
})
// Production template
await Template.build(template, 'myapp-prod', {
cpuCount: 4,
memoryMB: 4096,
})
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
# Development template
Template.build(
template,
'myapp-dev',
cpu_count=1,
memory_mb=1024,
)
# Production template
Template.build(
template,
'myapp-prod',
cpu_count=4,
memory_mb=4096,
)
```
### Multiple template variants
Create different variants of the same template with different configurations:
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
// Small instance
await Template.build(template, 'myapp-small', {
cpuCount: 1,
memoryMB: 512,
})
// Large instance
await Template.build(template, 'myapp-large', {
cpuCount: 8,
memoryMB: 8192,
})
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
# Small instance
Template.build(
template,
'myapp-small',
cpu_count=1,
memory_mb=512,
)
# Large instance
Template.build(
template,
'myapp-large',
cpu_count=8,
memory_mb=8192,
)
```
When building variants with the same template definition but different CPU/RAM configurations, E2B's caching system will reuse common layers, making subsequent builds much faster.
## Checking name availability
You can check if a name is already in use within your team with the `exists` method.
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Template } from 'e2b'
const exists = await Template.exists('my-template')
console.log(`Name ${exists ? 'is taken' : 'is available'}`)
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Template
exists = Template.exists('my-template')
print(f"Name {'is taken' if exists else 'is available'}")
```
## Best practices
1. **Use descriptive names**: Choose names that clearly indicate the template's purpose or configuration
2. **Use tags for versioning**: Instead of baking version numbers into names, use [tags](/docs/template/tags) for version management (e.g., `myapp:v1`, `myapp:v2`)
3. **Use consistent naming**: Establish a naming convention for your team and stick to it
# Private registries
Source: https://e2b.mintlify.app/docs/template/private-registries
Access private registries as the base image
If your base image is hosted in a private registry, you can provide credentials using the following helpers:
* General registry
* GCP Artifact Registry
* AWS ECR
## General registry
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
Template().fromImage('ubuntu:22.04', {
username: 'user',
password: 'pass',
})
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
Template().from_image(
image="ubuntu:22.04",
username="user",
password="pass",
)
```
## GCP Artifact Registry
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
// From file path
Template().fromGCPRegistry('ubuntu:22.04', {
serviceAccountJSON: './service_account.json',
})
// From object
Template().fromGCPRegistry('ubuntu:22.04', {
serviceAccountJSON: { project_id: '123', private_key_id: '456' },
})
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
# From file path
Template().from_gcp_registry(
image="ubuntu:22.04",
service_account_json="./service_account.json",
)
# From object
Template().from_gcp_registry(
image="ubuntu:22.04",
service_account_json={"project_id": "123", "private_key_id": "456"},
)
```
## AWS ECR
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
Template().fromAWSRegistry('ubuntu:22.04', {
accessKeyId: '123',
secretAccessKey: '456',
region: 'us-west-1',
})
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
Template().from_aws_registry(
image="ubuntu:22.04",
access_key_id="123",
secret_access_key="456",
region="us-west-1",
)
```
# Quickstart
Source: https://e2b.mintlify.app/docs/template/quickstart
Start with custom sandbox templates
E2B templates allow you to define custom sandboxes.
You can define the base image, environment variables, files to copy, commands to run, and
a [start command](/docs/template/start-ready-command#start-command) that runs during the template build and is captured in a snapshot — so the process is **already running** when you create a sandbox from that template.
This gives you fully configured sandboxes with running processes ready to use with zero wait time for your users.
There are two ways how you can start creating a new template:
* using the CLI
* manually using the SDK
## CLI
You can use the E2B CLI to create a new template.
Install the latest version of the [E2B CLI](https://e2b.dev/docs/cli)
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
e2b template init
```
Follow the prompts to create a new template.
Check the generated `README.md` file to see how to build and use your new template.
## Manual
### Install the packages
Requires the E2B SDK version at least 2.3.0
```bash JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
npm install e2b dotenv
```
```bash Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
pip install e2b dotenv
```
Create the `.env` file
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
E2B_API_KEY=e2b_***
```
### Create a new template file
Create a template file with the following name and content
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
// template.ts
import { Template, waitForTimeout } from 'e2b';
export const template = Template()
.fromBaseImage()
.setEnvs({
HELLO: "Hello, World!",
})
.setStartCmd("echo $HELLO", waitForTimeout(5_000));
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
# template.py
from e2b import Template, wait_for_timeout
template = (
Template()
.from_base_image()
.set_envs(
{
"HELLO": "Hello, World!",
}
)
.set_start_cmd("echo $HELLO", wait_for_timeout(5_000)))
```
### Create a development build script
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
// build.dev.ts
import 'dotenv/config';
import { Template, defaultBuildLogger } from 'e2b';
import { template } from './template';
async function main() {
await Template.build(template, 'template-tag-dev', {
cpuCount: 1,
memoryMB: 1024,
onBuildLogs: defaultBuildLogger(),
});
}
main().catch(console.error);
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
# build_dev.py
from dotenv import load_dotenv
from e2b import Template, default_build_logger
from template import template
load_dotenv()
if __name__ == '__main__':
Template.build(
template,
'template-tag-dev',
cpu_count=1,
memory_mb=1024,
on_build_logs=default_build_logger(),
)
```
### Create a production build script
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
// build.prod.ts
import 'dotenv/config';
import { Template, defaultBuildLogger } from 'e2b';
import { template } from './template';
async function main() {
await Template.build(template, 'template-tag', {
cpuCount: 1,
memoryMB: 1024,
onBuildLogs: defaultBuildLogger(),
});
}
main().catch(console.error);
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
# build_prod.py
from dotenv import load_dotenv
from e2b import Template, default_build_logger
from template import template
load_dotenv()
if __name__ == '__main__':
Template.build(
template,
'template-tag',
cpu_count=1,
memory_mb=1024,
on_build_logs=default_build_logger(),
)
```
### Build the template
Build the development template
```bash JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
npx tsx build.dev.ts
```
```bash Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
python build_dev.py
```
Build the production template
```bash JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
npx tsx build.prod.ts
```
```bash Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
python build_prod.py
```
## Create a new Sandbox from the Template
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import 'dotenv/config';
import { Sandbox } from 'e2b';
// Create a Sandbox from development template
const sandbox = await Sandbox.create("template-tag-dev");
// Create a Sandbox from production template
const sandbox = await Sandbox.create("template-tag");
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from dotenv import load_dotenv
from e2b import Sandbox
load_dotenv()
# Create a new Sandbox from the development template
sbx = Sandbox(template="template-tag-dev")
# Create a new Sandbox from the production template
sbx = Sandbox(template="template-tag")
```
The template name is the identifier that can be used to create a new Sandbox.
# Start & ready commands
Source: https://e2b.mintlify.app/docs/template/start-ready-command
Define running processes for the sandbox
## Start command
The start command specifies a process that runs at the **end of the template build** — not when a sandbox is created.
During the build, E2B executes the start command, waits for the [ready command](#ready-command) to confirm the process is up, and then takes a [snapshot](/docs/template/how-it-works) of the entire sandbox including the running process.
When you later create a sandbox from that template, the snapshotted process is **already running** — there is no startup wait.
This is how you get servers, seeded databases, or any long-running process available instantly when spawning sandboxes with the SDK.
The start command runs **once during template build** and is captured in a snapshot. It does not re-execute each time you create a sandbox. If you need to run a command every time a sandbox is created, use `sandbox.commands.run()` after creating the sandbox instead.
This also means that [environment variables passed to `Sandbox.create()`](/docs/sandbox/environment-variables#1-global-environment-variables) are **not available** to the start command process — it already ran during the build. If your start command needs environment variables, set them in the template definition using `setEnvs()` / `set_envs()`.
You can see the full build process [here](/docs/template/how-it-works).
## Ready command
The ready command determines when the sandbox is ready before a [snapshot](/docs/template/how-it-works) is created.
It is executed in an infinite loop until it returns a successful **exit code 0**.
This lets you control how long the build waits for the [start command](#start-command) or any other system state to be ready.
## `setStartCmd` / `set_start_cmd`
Use `setStartCmd` / `set_start_cmd` when you want to run a process during the template build **and** wait for it to be ready. This method accepts **two arguments**: the start command and the ready command.
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Template, waitForPort, waitForURL, waitForTimeout } from 'e2b'
// Start a Python HTTP server and wait for it to listen on port 8000
const template = Template()
.fromUbuntuImage("22.04")
.aptInstall(["curl", "python3"])
.setStartCmd('python3 -m http.server 8000', waitForPort(8000))
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Template, wait_for_port, wait_for_url, wait_for_timeout
# Start a Python HTTP server and wait for it to listen on port 8000
template = (
Template()
.from_ubuntu_image("22.04")
.apt_install(["curl", "python3"])
.set_start_cmd("python3 -m http.server 8000", wait_for_port(8000))
)
```
You can also pass a custom shell command as the ready command instead of using a helper:
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
// Start command with a custom ready command
template.setStartCmd('npm start', 'curl -s http://localhost:3000/health')
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
# Start command with a custom ready command
template.set_start_cmd("npm start", "curl -s http://localhost:3000/health")
```
More examples:
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Template, waitForURL, waitForPort } from 'e2b'
// Next.js app — wait for the dev server URL
template.setStartCmd('npx next --turbo', waitForURL('http://localhost:3000'))
// Python HTTP server — wait for port 8000
template.setStartCmd('python -m http.server 8000', waitForPort(8000))
// VNC desktop — wait for the VNC port
template.setStartCmd('/start_command.sh', waitForPort(6080))
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Template, wait_for_url, wait_for_port
# Next.js app — wait for the dev server URL
template.set_start_cmd("npx next --turbo", wait_for_url("http://localhost:3000"))
# Python HTTP server — wait for port 8000
template.set_start_cmd("python -m http.server 8000", wait_for_port(8000))
# VNC desktop — wait for the VNC port
template.set_start_cmd("/start_command.sh", wait_for_port(6080))
```
## `setReadyCmd` / `set_ready_cmd`
Use `setReadyCmd` / `set_ready_cmd` when you **don't need a start command** but still want to control when the sandbox snapshot is taken. This method accepts only **one argument**: the ready command.
This is useful when your template's build steps (e.g., `runCmd` / `run_cmd`) already start a background process or when you just need extra time for the system to settle before snapshotting.
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Template, waitForTimeout, waitForPort, waitForFile } from 'e2b'
// Wait 10 seconds before taking the snapshot
const template = Template()
.fromUbuntuImage("22.04")
.runCmd("apt-get install -y nginx && service nginx start")
.setReadyCmd(waitForPort(80))
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Template, wait_for_timeout, wait_for_port, wait_for_file
# Wait for nginx to start listening before taking the snapshot
template = (
Template()
.from_ubuntu_image("22.04")
.run_cmd("apt-get install -y nginx && service nginx start")
.set_ready_cmd(wait_for_port(80))
)
```
More examples:
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
// Wait for a file to be created by a background process
template.setReadyCmd(waitForFile('/tmp/ready'))
// Wait a fixed duration for the system to stabilize
template.setReadyCmd(waitForTimeout(10_000))
// Custom readiness check
template.setReadyCmd('curl -s http://localhost:8080/health')
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
# Wait for a file to be created by a background process
template.set_ready_cmd(wait_for_file("/tmp/ready"))
# Wait a fixed duration for the system to stabilize
template.set_ready_cmd(wait_for_timeout(10_000))
# Custom readiness check
template.set_ready_cmd("curl -s http://localhost:8080/health")
```
## Ready command helpers
The SDK provides helper functions for common ready command patterns. These can be used with both `setStartCmd` / `set_start_cmd` and `setReadyCmd` / `set_ready_cmd`.
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import {
waitForPort,
waitForURL,
waitForProcess,
waitForFile,
waitForTimeout,
} from 'e2b'
// Wait for a port to be available
waitForPort(3000)
// Wait for a URL to return a specific status code (defaults to 200)
waitForURL('http://localhost:3000/health')
waitForURL('http://localhost:3000/health', 200)
// Wait for a process to be running
waitForProcess('node')
// Wait for a file to exist
waitForFile('/tmp/ready')
// Wait for a specified duration
waitForTimeout(10_000) // 10 seconds
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import wait_for_port, wait_for_url, wait_for_process, wait_for_file, wait_for_timeout
# Wait for a port to be available
wait_for_port(3000)
# Wait for a URL to return a specific status code (defaults to 200)
wait_for_url("http://localhost:3000/health")
wait_for_url("http://localhost:3000/health", 200)
# Wait for a process to be running
wait_for_process("node")
# Wait for a file to exist
wait_for_file("/tmp/ready")
# Wait for a specified duration
wait_for_timeout(10_000) # 10 seconds
```
# Template tags & versioning
Source: https://e2b.mintlify.app/docs/template/tags
Use tags to version your templates and manage environment-based deployments
Template versioning allows you to maintain multiple versions of the same template using tags. This enables workflows like semantic versioning, environment-based deployments, and gradual rollouts.
## Tag format
Tags follow the `name:tag` format, where `name` is your template's identifier and `tag` is the version label.
```
my-template:v1.0.0 // Within your team
my-template:production // Within your team
acme/my-template:v1.0.0 // Full namespaced reference
```
## The default tag
When you build or reference a template without specifying a tag, E2B uses the `default` tag automatically. This means:
* `my-template` is equivalent to `my-template:default`
* Existing templates without tags continue to work seamlessly
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
// These are equivalent
const sandbox1 = await Sandbox.create('my-template')
const sandbox2 = await Sandbox.create('my-template:default')
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
# These are equivalent
sandbox1 = Sandbox.create('my-template')
sandbox2 = Sandbox.create('my-template:default')
```
## Referencing a specific build
Instead of using a named tag, you can start a sandbox from a specific build by passing its `build_id` directly. This is useful when you need to pin a sandbox to an exact build artifact — for example, during debugging or when reproducing an issue from a known build.
The format follows the same colon syntax as tags: `:` or `/:`.
You can find the `build_id` from the return value of `Template.build()` or by listing tags with `Template.getTags()` / `Template.get_tags()`.
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
// Start a sandbox from a specific build ID
const sandbox = await Sandbox.create('my-template:f47ac10b-58cc-4372-a567-0e02b2c3d479')
// With namespace
const sandbox2 = await Sandbox.create('acme/my-template:f47ac10b-58cc-4372-a567-0e02b2c3d479')
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
# Start a sandbox from a specific build ID
sandbox = Sandbox.create('my-template:f47ac10b-58cc-4372-a567-0e02b2c3d479')
# With namespace
sandbox2 = Sandbox.create('acme/my-template:f47ac10b-58cc-4372-a567-0e02b2c3d479')
```
## Building with tags
You can build templates with one or more tags to create versioned builds.
### Single tag
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Template } from 'e2b'
// Build with a specific version tag
await Template.build(template, 'my-template:v1.0.0')
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Template
# Build with a specific version tag
Template.build(template, 'my-template:v1.0.0')
```
### Multiple tags
Build with multiple tags to assign several version labels to the same build artifact.
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Template } from 'e2b'
// Build with multiple tags pointing to the same artifact
await Template.build(template, 'my-template', { tags: ['v1.2.0', 'latest'] })
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Template
# Build with multiple tags pointing to the same artifact
Template.build(template, 'my-template', tags=['v1.2.0', 'latest'])
```
## Managing tags
You can manage tags on existing template builds without rebuilding.
### Assign tags
Assign new tag(s) to an existing build. This is useful for promoting a tested version to production or marking a version as stable.
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Template } from 'e2b'
// Assign a single tag
await Template.assignTags('my-template:v1.2.0', 'production')
// Assign multiple tags at once
await Template.assignTags('my-template:v1.2.0', ['production', 'stable'])
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Template
# Assign a single tag
Template.assign_tags('my-template:v1.2.0', 'production')
# Assign multiple tags at once
Template.assign_tags('my-template:v1.2.0', tags=['production', 'stable'])
```
### Remove tags
Remove a tag from a template. The underlying build artifact remains accessible via other tags.
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Template } from 'e2b'
// Remove a tag
await Template.removeTags('my-template', 'staging')
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Template
# Remove a tag
Template.remove_tags('my-template', 'staging')
```
Removing a tag does not delete the build artifact. Other tags pointing to the same build will continue to work.
### List tags
Retrieve all tags for a template. Each tag includes the tag name, the associated build ID, and when the tag was assigned.
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Template } from 'e2b'
const tags = await Template.getTags('my-template')
for (const tag of tags) {
console.log(`Tag: ${tag.tag}, Build: ${tag.buildId}, Created: ${tag.createdAt}`)
}
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Template
tags = Template.get_tags('my-template')
for tag in tags:
print(f"Tag: {tag.tag}, Build: {tag.build_id}, Created: {tag.created_at}")
```
## Use cases
### Semantic versioning
Use semantic version tags to track releases and enable rollbacks.
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
// Release versions
await Template.build(template, 'api-server:v1.0.0')
await Template.build(template, 'api-server:v1.1.0')
await Template.build(template, 'api-server:v2.0.0')
// Create sandbox from specific version
const sandbox = await Sandbox.create('api-server:v1.1.0')
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
# Release versions
Template.build(template, 'api-server:v1.0.0')
Template.build(template, 'api-server:v1.1.0')
Template.build(template, 'api-server:v2.0.0')
# Create sandbox from specific version
sandbox = Sandbox.create('api-server:v1.1.0')
```
### Environment tags
Use environment tags for deployment pipelines.
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
// Build new version
await Template.build(template, 'my-app:v1.5.0')
// Promote through environments
await Template.assignTags('my-app:v1.5.0', 'staging')
// After testing, promote to production
await Template.assignTags('my-app:v1.5.0', 'production')
// Use in your application
const env = process.env.NODE_ENV
const sandbox = await Sandbox.create(`my-app:${env}`)
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import os
# Build new version
Template.build(template, 'my-app:v1.5.0')
# Promote through environments
Template.assign_tags('my-app:v1.5.0', 'staging')
# After testing, promote to production
Template.assign_tags('my-app:v1.5.0', 'production')
# Use in your application
env = os.environ.get('ENV', 'staging')
sandbox = Sandbox.create(f'my-app:{env}')
```
### Latest and stable tags
Maintain rolling tags that always point to specific versions.
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
// Build with version and latest tag
await Template.build(template, 'my-tool', { tags: ['v3.0.0', 'latest'] })
// Mark a tested version as stable
await Template.assignTags('my-tool:v2.9.0', 'stable')
// You can choose your risk tolerance
const latestSandbox = await Sandbox.create('my-tool:latest') // Newest
const stableSandbox = await Sandbox.create('my-tool:stable') // Tested
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
# Build with version and latest tag
Template.build(template, 'my-tool', tags=['v3.0.0', 'latest'])
# Mark a tested version as stable
Template.assign_tags('my-tool:v2.9.0', 'stable')
# You can choose your risk tolerance
latest_sandbox = Sandbox.create('my-tool:latest') # Newest
stable_sandbox = Sandbox.create('my-tool:stable') # Tested
```
# User and workdir
Source: https://e2b.mintlify.app/docs/template/user-and-workdir
Default user and working directory in the sandbox and template
The default user in the template is `user` with the `/home/user` (home directory) as the working directory.
This is different from the Docker defaults, where the default user is `root` with `/` as the working directory. This is to help with tools installation, and to improve default security.
The last set user and workdir in the template is then persisted as a default to the sandbox execution.
Example of setting user and workdir in the template definition are below.
Requires the E2B SDK version at least 2.3.0
## Default user and workdir in sandbox
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
const sbx = await Sandbox.create()
await sbx.commands.run("whoami") // user
await sbx.commands.run("pwd") // /home/user
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
sbx = Sandbox.create()
sbx.commands.run("whoami") # user
sbx.commands.run("pwd") # /home/user
```
## Custom user and workdir template
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
// template.ts
const template = Template()
.fromBaseImage()
.runCmd("whoami") // user
.runCmd("pwd") // /home/user
.setUser("guest")
.runCmd("whoami") // guest
.runCmd("pwd") // /home/guest
// build_dev.ts
await Template.build(template, 'custom-user-template', {
onBuildLogs: defaultBuildLogger()
})
// index.ts
const sbx = await Sandbox.create("custom-user-template")
await sbx.commands.run("whoami") // guest
await sbx.commands.run("pwd") // /home/guest
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
# template.py
template = (
Template()
.from_base_image()
.run_cmd("whoami") # user
.run_cmd("pwd") # /home/user
.set_user("guest")
.run_cmd("whoami") # guest
.run_cmd("pwd") # /home/guest
)
# build_dev.py
Template.build(template, 'custom-user-template',
on_build_logs=default_build_logger()
)
# main.py
sbx = Sandbox.create("custom-user-template")
sbx.commands.run("whoami") # guest
sbx.commands.run("pwd") # /home/guest
```
# GitHub Actions CI/CD
Source: https://e2b.mintlify.app/docs/use-cases/ci-cd
Run AI-powered code review, testing, and validation in secure E2B sandboxes from your GitHub Actions workflows.
CI/CD pipelines can use AI agents to review pull requests, generate tests, and validate code changes automatically. E2B sandboxes provide the secure, isolated execution environment where these agents can safely clone repositories, run untrusted code, and report results — all triggered by [GitHub Actions](https://docs.github.com/en/actions) on every pull request. Each run uses its own isolated sandbox, so malicious or buggy PR code never touches your CI runner.
## GitHub Actions workflow
The workflow triggers on pull request events and runs a review script. `E2B_API_KEY` and the LLM API key are stored as [GitHub Actions secrets](https://docs.github.com/en/actions/security-for-github-actions/security-guides/using-secrets-in-github-actions), while the built-in `GITHUB_TOKEN` is available automatically. The `permissions` block grants write access so the script can post PR comments.
```yaml .github/workflows/ai-review.yml (JavaScript) theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
name: AI Code Review
on:
pull_request:
types: [opened, synchronize]
permissions:
pull-requests: write
jobs:
ai-review:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: "20"
- name: Install dependencies
run: npm install e2b openai
- name: Run AI review
env:
E2B_API_KEY: ${{ secrets.E2B_API_KEY }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR_REPO: ${{ github.event.pull_request.head.repo.full_name }}
PR_BRANCH: ${{ github.event.pull_request.head.ref }}
PR_NUMBER: ${{ github.event.pull_request.number }}
GITHUB_REPOSITORY: ${{ github.repository }}
run: node review.mjs
```
```yaml .github/workflows/ai-review.yml (Python) theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
name: AI Code Review
on:
pull_request:
types: [opened, synchronize]
permissions:
pull-requests: write
jobs:
ai-review:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Install dependencies
run: pip install e2b openai
- name: Run AI review
env:
E2B_API_KEY: ${{ secrets.E2B_API_KEY }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR_REPO: ${{ github.event.pull_request.head.repo.full_name }}
PR_BRANCH: ${{ github.event.pull_request.head.ref }}
PR_NUMBER: ${{ github.event.pull_request.number }}
GITHUB_REPOSITORY: ${{ github.repository }}
run: python review.py
```
## Review script
The workflow calls this script on every PR. It runs five steps inside an E2B sandbox, keeping all untrusted code isolated from the CI runner.
```typescript review.mjs expandable theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox, CommandExitError } from 'e2b'
import OpenAI from 'openai'
// --- 1. Create sandbox ---
const sandbox = await Sandbox.create({ timeoutMs: 300_000 })
console.log('Sandbox created:', sandbox.sandboxId)
// --- 2. Clone the PR branch ---
const repoUrl = `https://github.com/${process.env.PR_REPO}.git`
await sandbox.git.clone(repoUrl, {
path: '/home/user/repo',
branch: process.env.PR_BRANCH,
username: 'x-access-token',
password: process.env.GITHUB_TOKEN,
depth: 1,
})
console.log('Repository cloned')
// --- 3. Get the diff and send it to an LLM for review ---
const diffResult = await sandbox.commands.run(
'cd /home/user/repo && git diff origin/main...HEAD'
)
const openai = new OpenAI()
const response = await openai.chat.completions.create({
model: 'gpt-5.2-mini',
messages: [
{
role: 'system',
content:
'You are a senior code reviewer. Analyze the following git diff and provide a concise review with actionable feedback. Focus on bugs, security issues, and code quality.',
},
{
role: 'user',
content: `Review this diff:\n\n${diffResult.stdout}`,
},
],
})
const review = response.choices[0].message.content
console.log('AI Review:', review)
// --- 4. Run the test suite inside the sandbox ---
await sandbox.commands.run('cd /home/user/repo && npm install', {
onStdout: (data) => console.log(data),
onStderr: (data) => console.error(data),
})
try {
await sandbox.commands.run('cd /home/user/repo && npm test', {
onStdout: (data) => console.log(data),
onStderr: (data) => console.error(data),
})
console.log('All tests passed')
} catch (err) {
if (err instanceof CommandExitError) {
console.error('Tests failed with exit code:', err.exitCode)
await sandbox.kill()
process.exit(1)
}
throw err
}
// --- 5. Post results as a PR comment ---
const prNumber = process.env.PR_NUMBER
const repo = process.env.GITHUB_REPOSITORY
await fetch(
`https://api.github.com/repos/${repo}/issues/${prNumber}/comments`,
{
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.GITHUB_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
body: `## AI Code Review\n\n${review}`,
}),
}
)
await sandbox.kill()
console.log('Done')
```
```python review.py expandable theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import os
import sys
import requests
from e2b import Sandbox, CommandExitException
from openai import OpenAI
# --- 1. Create sandbox ---
sandbox = Sandbox.create(timeout=300)
print(f"Sandbox created: {sandbox.sandbox_id}")
# --- 2. Clone the PR branch ---
repo_url = f"https://github.com/{os.environ['PR_REPO']}.git"
sandbox.git.clone(
repo_url,
path="/home/user/repo",
branch=os.environ["PR_BRANCH"],
username="x-access-token",
password=os.environ["GITHUB_TOKEN"],
depth=1,
)
print("Repository cloned")
# --- 3. Get the diff and send it to an LLM for review ---
diff_result = sandbox.commands.run(
"cd /home/user/repo && git diff origin/main...HEAD"
)
client = OpenAI()
response = client.chat.completions.create(
model="gpt-5.2-mini",
messages=[
{
"role": "system",
"content": "You are a senior code reviewer. Analyze the following git diff and provide a concise review with actionable feedback. Focus on bugs, security issues, and code quality.",
},
{
"role": "user",
"content": f"Review this diff:\n\n{diff_result.stdout}",
},
],
)
review = response.choices[0].message.content
print("AI Review:", review)
# --- 4. Run the test suite inside the sandbox ---
sandbox.commands.run(
"cd /home/user/repo && npm install",
on_stdout=lambda data: print(data),
on_stderr=lambda data: print(data, file=sys.stderr),
)
try:
sandbox.commands.run(
"cd /home/user/repo && npm test",
on_stdout=lambda data: print(data),
on_stderr=lambda data: print(data, file=sys.stderr),
)
print("All tests passed")
except CommandExitException as err:
print(f"Tests failed with exit code: {err.exit_code}", file=sys.stderr)
sandbox.kill()
sys.exit(1)
# --- 5. Post results as a PR comment ---
pr_number = os.environ["PR_NUMBER"]
repo = os.environ["GITHUB_REPOSITORY"]
requests.post(
f"https://api.github.com/repos/{repo}/issues/{pr_number}/comments",
headers={
"Authorization": f"Bearer {os.environ['GITHUB_TOKEN']}",
"Content-Type": "application/json",
},
json={
"body": f"## AI Code Review\n\n{review}",
},
)
sandbox.kill()
print("Done")
```
1. **Create sandbox** — `Sandbox.create()` creates an isolated Linux environment for the review
2. **Clone the PR** — `sandbox.git.clone()` checks out the PR branch using `x-access-token` + `GITHUB_TOKEN` for [authentication](https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/authenticating-as-a-github-app-installation)
3. **AI review** — runs `git diff` inside the sandbox, sends the output to an LLM — swap the model for any provider via [Connect LLMs](/docs/quickstart/connect-llms)
4. **Run tests** — `commands.run()` streams output in real time and throws on failure (`CommandExitError` / `CommandExitException`)
5. **Post results** — comments the review on the PR via the GitHub REST API, then shuts down the sandbox
## Related guides
Clone repos, manage branches, and push changes from sandboxes
Integrate AI models with sandboxes using tool calling
Build reproducible sandbox environments for your pipelines
# Coding Agents
Source: https://e2b.mintlify.app/docs/use-cases/coding-agents
Run AI coding agents like Claude Code, Codex, and Amp in secure E2B sandboxes with full terminal, filesystem, and git access.
Coding agents like [Claude Code](https://docs.anthropic.com/en/docs/agents-and-tools/claude-code/overview), [Codex](https://github.com/openai/codex), and [Amp](https://ampcode.com/) can write, debug, and refactor code autonomously. E2B sandboxes give each agent a full Linux environment with terminal, filesystem, and git — completely isolated from your infrastructure. Pre-built templates mean you can go from zero to a running agent in a single API call.
## Why Use a Sandbox
Running coding agents directly on your machine or servers means giving AI-generated code unrestricted access to your environment. E2B sandboxes solve this:
1. **Isolation** — agent-generated code runs in a secure sandbox, never touching your production systems or local machine
2. **Full dev environment** — terminal, filesystem, git, and package managers are all available out of the box, so agents work like a developer would
3. **Pre-built templates** — ready-made templates for popular agents get you started fast, and you can [build your own](/docs/template/quickstart) for any agent
4. **Scalability** — run many sandboxes in parallel, each with its own agent on a separate task
## How It Works
1. **Create a sandbox** — use a pre-built template or [build your own](/docs/template/quickstart) with any agent installed
2. **Agent gets a full environment** — terminal, filesystem, git access, and any tools installed in the template
3. **Agent works autonomously** — it reads the codebase, writes code, runs tests, and iterates until the task is done
4. **Extract results** — pull out the git diff, structured output, or modified files via the SDK. The sandbox stays available for follow-up work, or you can pause it to pick up later
## Agent Examples
Since each sandbox is a full Linux environment, you can run any coding agent — just install it in a [custom template](/docs/template/quickstart). E2B also provides pre-built templates for popular agents to get you started quickly.
Anthropic's autonomous coding agent with structured output and MCP tool support
OpenAI's coding agent with schema-validated output and image input
Coding agent with streaming JSON and thread management
Open-source multi-provider agent with a built-in web UI
## Related Guides
Clone repos, manage branches, and push changes from sandboxes
Pause and resume sandboxes to preserve state
Build your own sandbox templates with custom tools and dependencies
# Computer use
Source: https://e2b.mintlify.app/docs/use-cases/computer-use
Build AI agents that see, understand, and control virtual Linux desktops using E2B Desktop sandboxes.
Computer use agents interact with graphical desktops the same way a human would — viewing the screen, clicking, typing, and scrolling. E2B provides the sandboxed desktop environment where these agents operate safely, with [VNC](https://en.wikipedia.org/wiki/Virtual_Network_Computing) streaming for real-time visual feedback.
For a complete working implementation, see [E2B Surf](https://github.com/e2b-dev/surf) — an open-source computer use agent you can try via the [live demo](https://surf.e2b.dev).
## How it works
The computer use agent loop follows this pattern:
1. **User sends a command** — e.g., "Open Firefox and search for AI news"
2. **Agent creates a desktop sandbox** — an Ubuntu 22.04 environment with [XFCE](https://xfce.org/) desktop and pre-installed applications
3. **Agent takes a screenshot** — captures the current desktop state via E2B Desktop SDK
4. **LLM analyzes the screenshot** — a vision model (e.g., [OpenAI Computer Use API](https://platform.openai.com/docs/guides/computer-use)) decides what action to take
5. **Action is executed** — click, type, scroll, or keypress via E2B Desktop SDK
6. **Repeat** — new screenshot is taken and sent back to the LLM until the task is complete
## Install the E2B Desktop SDK
The [E2B Desktop](https://github.com/e2b-dev/desktop) SDK gives your agent a full Linux desktop with mouse, keyboard, and screen capture APIs.
```bash JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
npm i @e2b/desktop
```
```bash Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
pip install e2b-desktop
```
## Core implementation
The following snippets are adapted from [E2B Surf](https://github.com/e2b-dev/surf).
### Setting up the sandbox
Create a desktop sandbox and start VNC streaming so you can view the desktop in a browser.
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from '@e2b/desktop'
// Create a desktop sandbox with a 5-minute timeout
const sandbox = await Sandbox.create({
resolution: [1024, 720],
dpi: 96,
timeoutMs: 300_000,
})
// Start VNC streaming for browser-based viewing
await sandbox.stream.start()
const streamUrl = sandbox.stream.getUrl()
console.log('View desktop at:', streamUrl)
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b_desktop import Sandbox
# Create a desktop sandbox with a 5-minute timeout
sandbox = Sandbox.create(
resolution=(1024, 720),
dpi=96,
timeout=300,
)
# Start VNC streaming for browser-based viewing
sandbox.stream.start()
stream_url = sandbox.stream.get_url()
print("View desktop at:", stream_url)
```
### Executing desktop actions
The E2B Desktop SDK maps directly to mouse and keyboard actions. Here's how Surf translates LLM-returned actions into desktop interactions.
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from '@e2b/desktop'
const sandbox = await Sandbox.create({ timeoutMs: 300_000 })
// Mouse actions
await sandbox.leftClick(500, 300)
await sandbox.rightClick(500, 300)
await sandbox.doubleClick(500, 300)
await sandbox.middleClick(500, 300)
await sandbox.moveMouse(500, 300)
await sandbox.drag([100, 200], [400, 500])
// Keyboard actions
await sandbox.write('Hello, world!') // Type text
await sandbox.press('Enter') // Press a key
// Scrolling
await sandbox.scroll('down', 3) // Scroll down 3 ticks
await sandbox.scroll('up', 3) // Scroll up 3 ticks
// Screenshots
const screenshot = await sandbox.screenshot() // Returns Buffer
// Run terminal commands
await sandbox.commands.run('ls -la /home')
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b_desktop import Sandbox
sandbox = Sandbox.create(timeout=300)
# Mouse actions
sandbox.left_click(500, 300)
sandbox.right_click(500, 300)
sandbox.double_click(500, 300)
sandbox.middle_click(500, 300)
sandbox.move_mouse(500, 300)
sandbox.drag([100, 200], [400, 500])
# Keyboard actions
sandbox.write("Hello, world!") # Type text
sandbox.press("Enter") # Press a key
# Scrolling
sandbox.scroll("down", 3) # Scroll down 3 ticks
sandbox.scroll("up", 3) # Scroll up 3 ticks
# Screenshots
screenshot = sandbox.screenshot() # Returns bytes
# Run terminal commands
sandbox.commands.run("ls -la /home")
```
### Agent loop
The core loop takes screenshots, sends them to an LLM, and executes the returned actions on the desktop. This is a simplified version of how [Surf](https://github.com/e2b-dev/surf) drives the computer use cycle.
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from '@e2b/desktop'
const sandbox = await Sandbox.create({
resolution: [1024, 720],
timeoutMs: 300_000,
})
await sandbox.stream.start()
while (true) {
// 1. Capture the current desktop state
const screenshot = await sandbox.screenshot()
// 2. Send screenshot to your LLM and get the next action
// (use OpenAI Computer Use, Anthropic Claude, etc.)
const action = await getNextActionFromLLM(screenshot)
if (!action) break // LLM signals task is complete
// 3. Execute the action on the desktop
switch (action.type) {
case 'click':
await sandbox.leftClick(action.x, action.y)
break
case 'type':
await sandbox.write(action.text)
break
case 'keypress':
await sandbox.press(action.keys)
break
case 'scroll':
await sandbox.scroll(
action.scrollY < 0 ? 'up' : 'down',
Math.abs(action.scrollY)
)
break
case 'drag':
await sandbox.drag(
[action.startX, action.startY],
[action.endX, action.endY]
)
break
}
}
await sandbox.kill()
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b_desktop import Sandbox
sandbox = Sandbox.create(
resolution=(1024, 720),
timeout=300,
)
sandbox.stream.start()
while True:
# 1. Capture the current desktop state
screenshot = sandbox.screenshot()
# 2. Send screenshot to your LLM and get the next action
# (use OpenAI Computer Use, Anthropic Claude, etc.)
action = get_next_action_from_llm(screenshot)
if not action:
break # LLM signals task is complete
# 3. Execute the action on the desktop
if action.type == "click":
sandbox.left_click(action.x, action.y)
elif action.type == "type":
sandbox.write(action.text)
elif action.type == "keypress":
sandbox.press(action.keys)
elif action.type == "scroll":
direction = "up" if action.scroll_y < 0 else "down"
sandbox.scroll(direction, abs(action.scroll_y))
elif action.type == "drag":
sandbox.drag(
[action.start_x, action.start_y],
[action.end_x, action.end_y],
)
sandbox.kill()
```
The `getNextActionFromLLM` / `get_next_action_from_llm` function is where you integrate your chosen LLM. See [Connect LLMs to E2B](/docs/quickstart/connect-llms) for integration patterns with OpenAI, Anthropic, and other providers.
## Related guides
Build desktop sandboxes with Ubuntu, XFCE, and VNC streaming
Integrate AI models with sandboxes using tool calling
Create, manage, and control sandbox lifecycle
# Volumes
Source: https://e2b.mintlify.app/docs/volumes
Volumes are currently in private beta.
If you'd like access, please reach out to us at [support@e2b.dev](mailto:support@e2b.dev).
Volumes provide persistent storage that exists independently of sandbox lifecycles. Data written to a volume persists even after a sandbox is shut down, and volumes can be mounted to multiple sandboxes over time.
**One volume shared across multiple sandboxes**
```mermaid actions={false} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
graph LR
V1[Volume A] --- S1[Sandbox 1]
V1 --- S2[Sandbox 2]
V1 --- S3[Sandbox 3]
```
**Each sandbox with its own volume**
```mermaid actions={false} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
graph LR
V2[Volume A] --- S4[Sandbox 1]
V3[Volume B] --- S5[Sandbox 2]
```
**Standalone usage via SDK**
```mermaid actions={false} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
graph LR
SDK[SDK] --- V4[Volume A]
SDK --- V5[Volume B]
```
When a volume is mounted to a sandbox, files can be read and written directly at the mount path. The SDK methods are meant to be used when the volume is not mounted to any sandbox.
With E2B SDK you can:
* [Manage volumes.](/docs/volumes/manage)
* [Mount volumes to sandboxes.](/docs/volumes/mount)
* [Read and write files to a volume.](/docs/volumes/read-write)
* [Get file and directory metadata.](/docs/volumes/info)
* [Upload data to a volume.](/docs/volumes/upload)
* [Download data from a volume.](/docs/volumes/download)
## Example
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Volume, Sandbox } from 'e2b'
const volume = await Volume.create('my-volume')
const sandbox = await Sandbox.create({
volumeMounts: {
'/mnt/my-data': volume,
},
})
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Volume, Sandbox
volume = Volume.create('my-volume')
sandbox = Sandbox.create(
volume_mounts={
'/mnt/my-data': volume,
},
)
```
# Download data from volume
Source: https://e2b.mintlify.app/docs/volumes/download
You can download data from a volume using the `readFile()` / `read_file()` method.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import fs from 'fs'
import { Volume } from 'e2b'
const volume = await Volume.create('my-volume')
// Read file from volume
const content = await volume.readFile('/path/in/volume')
// Write file to local filesystem
fs.writeFileSync('/local/path', content)
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Volume
volume = Volume.create('my-volume')
# Read file from volume
content = volume.read_file('/path/in/volume')
# Write file to local filesystem
with open('/local/path', 'w') as file:
file.write(content)
```
# Get information about a file or directory
Source: https://e2b.mintlify.app/docs/volumes/info
You can get information about a file or directory in a volume using the `getInfo()` / `get_info()` method.
### Getting information about a file
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Volume } from 'e2b'
const volume = await Volume.create('my-volume')
// Create a new file
await volume.writeFile('/test_file.txt', 'Hello, world!')
// Get information about the file
const info = await volume.getInfo('/test_file.txt')
console.log(info)
// {
// name: 'test_file.txt',
// type: 'file',
// path: '/test_file.txt',
// size: 13,
// mode: 0o644,
// uid: 0,
// gid: 0,
// atime: 2025-05-26T12:00:00.000Z,
// mtime: 2025-05-26T12:00:00.000Z,
// ctime: 2025-05-26T12:00:00.000Z,
// }
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Volume
volume = Volume.create('my-volume')
# Create a new file
volume.write_file('/test_file.txt', 'Hello, world!')
# Get information about the file
info = volume.get_info('/test_file.txt')
print(info)
# VolumeEntryStat(
# name='test_file.txt',
# type_='file',
# path='/test_file.txt',
# size=13,
# mode=0o644,
# uid=0,
# gid=0,
# atime=datetime(2025, 5, 26, 12, 0, 0),
# mtime=datetime(2025, 5, 26, 12, 0, 0),
# ctime=datetime(2025, 5, 26, 12, 0, 0),
# )
```
### Getting information about a directory
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Volume } from 'e2b'
const volume = await Volume.create('my-volume')
// Create a new directory
await volume.makeDir('/test_dir')
// Get information about the directory
const info = await volume.getInfo('/test_dir')
console.log(info)
// {
// name: 'test_dir',
// type: 'directory',
// path: '/test_dir',
// size: 0,
// mode: 0o755,
// uid: 0,
// gid: 0,
// atime: 2025-05-26T12:00:00.000Z,
// mtime: 2025-05-26T12:00:00.000Z,
// ctime: 2025-05-26T12:00:00.000Z,
// }
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Volume
volume = Volume.create('my-volume')
# Create a new directory
volume.make_dir('/test_dir')
# Get information about the directory
info = volume.get_info('/test_dir')
print(info)
# VolumeEntryStat(
# name='test_dir',
# type_='directory',
# path='/test_dir',
# size=0,
# mode=0o755,
# uid=0,
# gid=0,
# atime=datetime(2025, 5, 26, 12, 0, 0),
# mtime=datetime(2025, 5, 26, 12, 0, 0),
# ctime=datetime(2025, 5, 26, 12, 0, 0),
# )
```
### Checking if a path exists
You can check whether a file or directory exists in a volume using the `exists()` method.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Volume } from 'e2b'
const volume = await Volume.create('my-volume')
const fileExists = await volume.exists('/test_file.txt')
console.log(fileExists) // true or false
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Volume
volume = Volume.create('my-volume')
file_exists = volume.exists('/test_file.txt')
print(file_exists) # True or False
```
### Updating metadata
You can update file or directory metadata such as user ID, group ID, and permissions mode using the `updateMetadata()` / `update_metadata()` method.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Volume } from 'e2b'
const volume = await Volume.create('my-volume')
await volume.writeFile('/test_file.txt', 'Hello, world!')
const updated = await volume.updateMetadata('/test_file.txt', { uid: 1000, gid: 1000, mode: 0o600 })
console.log(updated)
// {
// name: 'test_file.txt',
// type: 'file',
// path: '/test_file.txt',
// size: 13,
// mode: 0o600,
// uid: 1000,
// gid: 1000,
// ...
// }
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Volume
volume = Volume.create('my-volume')
volume.write_file('/test_file.txt', 'Hello, world!')
updated = volume.update_metadata('/test_file.txt', uid=1000, gid=1000, mode=0o600)
print(updated)
# VolumeEntryStat(
# name='test_file.txt',
# type_='file',
# path='/test_file.txt',
# size=13,
# mode=0o600,
# uid=1000,
# gid=1000,
# ...
# )
```
# Managing volumes
Source: https://e2b.mintlify.app/docs/volumes/manage
## Create a volume
Volume names can only contain letters, numbers, and hyphens.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Volume } from 'e2b'
const volume = await Volume.create('my-volume')
console.log(volume.volumeId) // Volume ID
console.log(volume.name) // 'my-volume'
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Volume
volume = Volume.create('my-volume')
print(volume.volume_id) # Volume ID
print(volume.name) # 'my-volume'
```
## Connect to an existing volume
You can connect to an existing volume by its ID using the `connect()` method.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Volume } from 'e2b'
const volume = await Volume.connect('volume-id')
console.log(volume.volumeId) // Volume ID
console.log(volume.name) // Volume name
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Volume
volume = Volume.connect('volume-id')
print(volume.volume_id) # Volume ID
print(volume.name) # Volume name
```
## List volumes
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Volume } from 'e2b'
const volumes = await Volume.list()
console.log(volumes)
// [{ volumeId: '...', name: 'my-volume' }, ...]
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Volume
volumes = Volume.list()
print(volumes)
# [VolumeInfo(volume_id='...', name='my-volume'), ...]
```
## Get volume info
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Volume } from 'e2b'
const info = await Volume.getInfo('volume-id')
console.log(info)
// { volumeId: '...', name: 'my-volume' }
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Volume
info = Volume.get_info('volume-id')
print(info)
# VolumeInfo(volume_id='...', name='my-volume')
```
## Destroy a volume
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Volume } from 'e2b'
const success = await Volume.destroy('volume-id')
console.log(success) // true
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Volume
success = Volume.destroy('volume-id')
print(success) # True
```
# Mounting volumes
Source: https://e2b.mintlify.app/docs/volumes/mount
You can mount one or more volumes to a sandbox when creating it. The keys of the `volumeMounts` / `volume_mounts` object are the mount paths inside the sandbox.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Volume, Sandbox } from 'e2b'
const volume = await Volume.create('my-volume')
// You can pass a Volume object
const sandbox = await Sandbox.create({
volumeMounts: {
'/mnt/my-data': volume,
},
})
// Or pass the volume name directly
const sandbox = await Sandbox.create({
volumeMounts: {
'/mnt/my-data': 'my-volume',
},
})
// Files written to /mnt/my-data inside the sandbox are persisted in the volume
await sandbox.files.write('/mnt/my-data/hello.txt', 'Hello, world!')
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Volume, Sandbox
volume = Volume.create('my-volume')
# You can pass a Volume object
sandbox = Sandbox.create(
volume_mounts={
'/mnt/my-data': volume,
},
)
# Or pass the volume name directly
sandbox = Sandbox.create(
volume_mounts={
'/mnt/my-data': 'my-volume',
},
)
# Files written to /mnt/my-data inside the sandbox are persisted in the volume
sandbox.files.write('/mnt/my-data/hello.txt', 'Hello, world!')
```
# Read & write files
Source: https://e2b.mintlify.app/docs/volumes/read-write
## Reading files
You can read files from a volume using the `readFile()` / `read_file()` method.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Volume } from 'e2b'
const volume = await Volume.create('my-volume')
const content = await volume.readFile('/path/to/file')
console.log(content)
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Volume
volume = Volume.create('my-volume')
content = volume.read_file('/path/to/file')
print(content)
```
## Writing files
You can write files to a volume using the `writeFile()` / `write_file()` method.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Volume } from 'e2b'
const volume = await Volume.create('my-volume')
await volume.writeFile('/path/to/file', 'file content')
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Volume
volume = Volume.create('my-volume')
volume.write_file('/path/to/file', 'file content')
```
## Creating directories
You can create directories in a volume using the `makeDir()` / `make_dir()` method.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Volume } from 'e2b'
const volume = await Volume.create('my-volume')
await volume.makeDir('/path/to/dir')
// Create nested directories with force option
await volume.makeDir('/path/to/nested/dir', { force: true })
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Volume
volume = Volume.create('my-volume')
volume.make_dir('/path/to/dir')
# Create nested directories with force option
volume.make_dir('/path/to/nested/dir', force=True)
```
## Listing directory contents
You can list the contents of a directory in a volume using the `list()` method.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Volume } from 'e2b'
const volume = await Volume.create('my-volume')
const entries = await volume.list('/path/to/dir')
console.log(entries)
// [
// { name: 'file.txt', type: 'file', path: '/path/to/dir/file.txt', size: 13, ... },
// { name: 'subdir', type: 'directory', path: '/path/to/dir/subdir', size: 0, ... },
// ]
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Volume
volume = Volume.create('my-volume')
entries = volume.list('/path/to/dir')
print(entries)
# [
# VolumeEntryStat(name='file.txt', type_='file', path='/path/to/dir/file.txt', size=13, ...),
# VolumeEntryStat(name='subdir', type_='directory', path='/path/to/dir/subdir', size=0, ...),
# ]
```
## Removing files or directories
You can remove files or directories from a volume using the `remove()` method.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Volume } from 'e2b'
const volume = await Volume.create('my-volume')
// Remove a file
await volume.remove('/path/to/file')
// Remove a directory recursively
await volume.remove('/path/to/dir', { recursive: true })
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Volume
volume = Volume.create('my-volume')
# Remove a file
volume.remove('/path/to/file')
# Remove a directory recursively
volume.remove('/path/to/dir', recursive=True)
```
# Upload data to volume
Source: https://e2b.mintlify.app/docs/volumes/upload
You can upload data to a volume using the `writeFile()` / `write_file()` method.
## Upload single file
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import fs from 'fs'
import { Volume } from 'e2b'
const volume = await Volume.create('my-volume')
// Read file from local filesystem
const content = fs.readFileSync('/local/path')
// Upload file to volume
await volume.writeFile('/path/in/volume', content)
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Volume
volume = Volume.create('my-volume')
# Read file from local filesystem
with open('/local/path', 'rb') as file:
# Upload file to volume
volume.write_file('/path/in/volume', file)
```
## Upload directory / multiple files
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import fs from 'fs'
import path from 'path'
import { Volume } from 'e2b'
const volume = await Volume.create('my-volume')
const directoryPath = '/local/dir'
const files = fs.readdirSync(directoryPath)
for (const file of files) {
const fullPath = path.join(directoryPath, file)
// Skip directories
if (!fs.statSync(fullPath).isFile()) continue
const content = fs.readFileSync(fullPath)
await volume.writeFile(`/upload/${file}`, content)
}
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import os
from e2b import Volume
volume = Volume.create('my-volume')
directory_path = '/local/dir'
for filename in os.listdir(directory_path):
file_path = os.path.join(directory_path, filename)
# Skip directories
if not os.path.isfile(file_path):
continue
with open(file_path, 'rb') as file:
volume.write_file(f'/upload/{filename}', file)
```
# BYOC (Bring Your Own Cloud)
Source: https://e2b.mintlify.app/docs/byoc
BYOC (Bring Your Own Cloud) allows you to deploy E2B sandboxes to your own cloud infrastructure within your VPC.
BYOC is currently only available for AWS and Google Cloud (GCP). We are working on adding support for Azure.
BYOC is offered to enterprise customers only.
If you’re interested in BYOC offering, please book a call with our team [here](https://e2b.dev/contact) or contact us at [enterprise@e2b.dev](mailto:enterprise@e2b.dev).
## Architecture
Sandbox templates, snapshots, and runtime logs are stored within the customer's BYOC VPC.
Anonymized system metrics such as cluster memory and cpu are sent to the E2B Cloud for observability and cluster management purposes.
All potentially sensitive traffic, such as sandbox template build source files,
sandbox traffic, and logs, is transmitted directly from the client to the customer's BYOC VPC without ever touching the E2B Cloud infrastructure.
### Glossary
* **BYOC VPC**: The customer's Virtual Private Network where the E2B sandboxes are deployed. For example your AWS account.
* **E2B Cloud**: The managed service that provides the E2B platform, observability and cluster management.
* **OAuth Provider**: Customer-managed service that provides user and E2B Cloud with access to the cluster.
### BYOC cluster components
* **Orchestrator**: Represents a node that is responsible for managing sandboxes and their lifecycle. Optionally, it can also run the template builder component.
* **Edge Controller**: Routes traffic to sandboxes, exposes API for cluster management, and gRPC proxy used by E2B control plane to communicate with orchestrators.
* **Monitoring**: Collector that receives sandbox and build logs and system metrics from orchestrators and edge controllers. Only anonymized metrics are sent to the E2B Cloud for observability purposes.
* **Storage**: Persistent storage for sandbox templates, snapshots, and runtime logs. Image container repository for template images.
## Onboarding
Customers can initiate the onboarding process by reaching out to us.
Customers need to have a dedicated AWS account and know the region they will use.
After that, we will receive the IAM role needed for managing account resources.
For AWS account quota limits may need to be increased.
Terraform configuration and machine images will be used for provisioning BYOC cluster.
When provisioning is done and running, we will create a new team under your E2B account that can be used by SDK/CLI the same way as it is hosted on E2B Cloud.
## FAQ
Cluster is forwarding anonymized metrics such as machine cpu/memory usage to E2B Control plane for advanced observability and alerting.
The whole observability stack is anonymized and does not contain any sensitive information.
A cluster can be scaled horizontally by adding more orchestrators and edge controllers.
The autoscaler is currently in V1 not capable of automatically scale orchestrator nodes that are needed for sandbox spawning.
This feature is coming in the next versions.
Yes. Load balancer that is handling all requests coming to sandbox can be configured as internal and VPC peering
with additional customer’s VPC can be configured so sandbox traffic can stay in the private network.
Data sent between the E2B Cloud and your BYOC VPC is encrypted using TLS.
VPC peering can be established to allow direct communication between the E2B Cloud and your BYOC VPC.
When using VPC peering, the load balancer can be configured as private without a public IP address.
# SDK v2 migration guide
Source: https://e2b.mintlify.app/docs/migration/v2
This guide helps you migrate from E2B SDK v1 to v2, covering all breaking changes and new patterns.
## Table of contents
SDK v2 introduces several important changes:
* [**New creation pattern in Python Synchronous SDK**](#1-sandbox-creation-in-synchronous-python-sdk)
* [**Secure by default**](#2-secure-communication-by-default)
* [**Updated file operations in Python SDK**](#3-file-writing-in-python-sdk)
* [**Updated list method**](#4-listing-sandboxes)
## Breaking changes
### 1. Sandbox creation in synchronous Python SDK
In v2, the synchronous Python SDK uses a class method `create()` instead of the constructor `Sandbox()`.
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b_code_interpreter import Sandbox
sandbox = Sandbox.create()
sandbox = Sandbox.create(template="base")
```
In the v1, you would instantiate directly:
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b_code_interpreter import Sandbox
sandbox = Sandbox()
sandbox = Sandbox(template="base")
```
### 2. Secure communication by default
Sandboxes are now **secure by default**. This means you can't access the sandbox controller directly through its URL without an authentication header.
The SDK automatically handles the authentication header for you.
For custom templates created before envd `v0.2.0`, you need to rebuild them to enable secure communication.
Otherwise, you will receive error messages when creating sandboxes. You can check the template envd version using the `e2b template list` command or view the templates list in the dashboard.
You can temporarily disable secure communication by setting `secure` to `false` during sandbox creation, but this is not recommended for production environments.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from '@e2b/code-interpreter'
const sandbox = await Sandbox.create({ secure: false }) // Explicitly disable
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b_code_interpreter import Sandbox
sandbox = Sandbox.create(secure=False) # Explicitly disable
```
Check more details in the [secured access documentation](/docs/sandbox/secured-access).
### 3. File writing in Python SDK
The file writing API in Python has been made more consistent.
In v2, use `sandbox.files.write()` for single files and `sandbox.files.write_files()` for multiple files:
```python v2 theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b_code_interpreter import Sandbox
sandbox = Sandbox.create()
# Write single file
info = sandbox.files.write("/tmp/file.txt", "content")
# Write multiple files
infos = sandbox.files.write_files([
{"path": "/tmp/file1.txt", "data": "content1"},
{"path": "/tmp/file2.txt", "data": "content2"}
])
```
In v1, the same `write()` method was overloaded for both single and multiple files:
```python v1 theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b_code_interpreter import Sandbox
sandbox = Sandbox.create()
# Write single file
info = sandbox.write(path="/tmp/file.txt", data="content")
# Write multiple files
infos = sandbox.write([
{"path": "/tmp/file1.txt", "data": "content1"},
{"path": "/tmp/file2.txt", "data": "content2"}
])
```
### 4. Listing sandboxes
The method for listing sandboxes has been updated to use pagination.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox, SandboxInfo } from '@e2b/code-interpreter'
// Get paginator
const paginator = Sandbox.list()
// Iterate through all sandboxes
for (const sandbox of await paginator.nextItems()) {
console.log(sandbox.sandboxId)
}
// Iterate through all sandboxes
const allSandboxes: SandboxInfo[] = []
while (paginator.hasNext) {
const items = await paginator.nextItems()
allSandboxes.push(...items)
}
// With query
const queryPaginator = Sandbox.list({query: {metadata: {key: "value"}}})
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b_code_interpreter import Sandbox, SandboxQuery
# Get paginator
paginator = Sandbox.list()
# Iterate through all sandboxes
while paginator.has_next:
sandboxes = paginator.next_items()
print(sandboxes)
# With query
paginator = Sandbox.list(query=SandboxQuery(metadata={"key": "value"}))
```
# Errors
Source: https://e2b.mintlify.app/docs/sdk-reference/js-sdk/v2.14.1/errors
### AuthenticationError
Thrown when authentication fails.
#### Extended by
* `GitAuthError`
#### Constructors
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
new AuthenticationError(message: string): AuthenticationError
```
###### Parameters
| Parameter | Type |
| --------- | -------- |
| `message` | `string` |
###### Returns
`AuthenticationError`
***
### BuildError
Thrown when the build fails.
#### Extended by
* `FileUploadError`
#### Constructors
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
new BuildError(message: string, stackTrace?: string): BuildError
```
###### Parameters
| Parameter | Type |
| ------------- | -------- |
| `message` | `string` |
| `stackTrace`? | `string` |
###### Returns
`BuildError`
***
### FileUploadError
Thrown when the file upload fails.
#### Constructors
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
new FileUploadError(message: string, stackTrace?: string): FileUploadError
```
###### Parameters
| Parameter | Type |
| ------------- | -------- |
| `message` | `string` |
| `stackTrace`? | `string` |
###### Returns
`FileUploadError`
***
### GitAuthError
Thrown when git authentication fails.
#### Constructors
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
new GitAuthError(message: string): GitAuthError
```
###### Parameters
| Parameter | Type |
| --------- | -------- |
| `message` | `string` |
###### Returns
`GitAuthError`
***
### GitUpstreamError
Thrown when git upstream tracking is missing.
#### Constructors
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
new GitUpstreamError(message: string, stackTrace?: string): GitUpstreamError
```
###### Parameters
| Parameter | Type |
| ------------- | -------- |
| `message` | `string` |
| `stackTrace`? | `string` |
###### Returns
`GitUpstreamError`
***
### InvalidArgumentError
Thrown when an invalid argument is provided.
#### Constructors
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
new InvalidArgumentError(message: string, stackTrace?: string): InvalidArgumentError
```
###### Parameters
| Parameter | Type |
| ------------- | -------- |
| `message` | `string` |
| `stackTrace`? | `string` |
###### Returns
`InvalidArgumentError`
***
### NotEnoughSpaceError
Thrown when there is not enough disk space.
#### Constructors
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
new NotEnoughSpaceError(message: string, stackTrace?: string): NotEnoughSpaceError
```
###### Parameters
| Parameter | Type |
| ------------- | -------- |
| `message` | `string` |
| `stackTrace`? | `string` |
###### Returns
`NotEnoughSpaceError`
***
### NotFoundError
Thrown when a resource is not found.
#### Constructors
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
new NotFoundError(message: string, stackTrace?: string): NotFoundError
```
###### Parameters
| Parameter | Type |
| ------------- | -------- |
| `message` | `string` |
| `stackTrace`? | `string` |
###### Returns
`NotFoundError`
***
### RateLimitError
Thrown when the API rate limit is exceeded.
#### Constructors
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
new RateLimitError(message: string): RateLimitError
```
###### Parameters
| Parameter | Type |
| --------- | -------- |
| `message` | `string` |
###### Returns
`RateLimitError`
***
### SandboxError
Base class for all sandbox errors.
Thrown when general sandbox errors occur.
#### Extended by
* `TimeoutError`
* `InvalidArgumentError`
* `NotEnoughSpaceError`
* `NotFoundError`
* `GitUpstreamError`
* `TemplateError`
* `RateLimitError`
#### Constructors
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
new SandboxError(message?: string, stackTrace?: string): SandboxError
```
###### Parameters
| Parameter | Type |
| ------------- | -------- |
| `message`? | `string` |
| `stackTrace`? | `string` |
###### Returns
`SandboxError`
***
### TemplateError
Thrown when the template uses old envd version. It isn't compatible with the new SDK.
#### Constructors
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
new TemplateError(message: string, stackTrace?: string): TemplateError
```
###### Parameters
| Parameter | Type |
| ------------- | -------- |
| `message` | `string` |
| `stackTrace`? | `string` |
###### Returns
`TemplateError`
***
### TimeoutError
Thrown when a timeout error occurs.
The \[unavailable] error type is caused by sandbox timeout.
The \[canceled] error type is caused by exceeding request timeout.
The \[deadline\_exceeded] error type is caused by exceeding the timeout for command execution, watch, etc.
The \[unknown] error type is sometimes caused by the sandbox timeout when the request is not processed correctly.
#### Constructors
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
new TimeoutError(message: string, stackTrace?: string): TimeoutError
```
###### Parameters
| Parameter | Type |
| ------------- | -------- |
| `message` | `string` |
| `stackTrace`? | `string` |
###### Returns
`TimeoutError`
## Functions
### formatSandboxTimeoutError()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
function formatSandboxTimeoutError(message: string): TimeoutError
```
#### Parameters
| Parameter | Type |
| --------- | -------- |
| `message` | `string` |
#### Returns
`TimeoutError`
# Sandbox
Source: https://e2b.mintlify.app/docs/sdk-reference/js-sdk/v2.14.1/sandbox
### Sandbox
E2B cloud sandbox is a secure and isolated cloud environment.
The sandbox allows you to:
* Access Linux OS
* Create, list, and delete files and directories
* Run commands
* Run git operations
* Run isolated code
* Access the internet
Check docs here.
Use Sandbox.create to create a new sandbox.
#### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const sandbox = await Sandbox.create()
```
#### Properties
| Property | Modifier | Type | Description |
| --------------------- | ---------- | ------------ | ----------------------------------------------------------------------------------- |
| `commands` | `readonly` | `Commands` | Module for running commands in the sandbox |
| `files` | `readonly` | `Filesystem` | Module for interacting with the sandbox filesystem |
| `git` | `readonly` | `Git` | Module for running git operations in the sandbox |
| `pty` | `readonly` | `Pty` | Module for interacting with the sandbox pseudo-terminals |
| `sandboxDomain` | `readonly` | `string` | Domain where the sandbox is hosted. |
| `sandboxId` | `readonly` | `string` | Unique identifier of the sandbox. |
| `trafficAccessToken?` | `readonly` | `string` | Traffic access token for accessing sandbox services with restricted public traffic. |
#### Methods
### ~~betaPause()~~
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
betaPause(opts?: ConnectionOpts): Promise
```
###### Parameters
| Parameter | Type |
| --------- | ---------------- |
| `opts`? | `ConnectionOpts` |
###### Returns
`Promise`\<`boolean`>
###### Deprecated
Use Sandbox.pause instead.
### connect()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
connect(opts?: SandboxConnectOpts): Promise
```
Connect to a sandbox. If the sandbox is paused, it will be automatically resumed.
Sandbox must be either running or be paused.
With sandbox ID you can connect to the same sandbox from different places or environments (serverless functions, etc).
###### Parameters
| Parameter | Type | Description |
| --------- | -------------------- | ------------------- |
| `opts`? | `SandboxConnectOpts` | connection options. |
###### Returns
`Promise`\<`Sandbox`>
A running sandbox instance
###### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
const sandbox = await Sandbox.create()
await sandbox.betaPause()
// Connect to the same sandbox.
const sameSandbox = await sandbox.connect()
```
### createSnapshot()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
createSnapshot(opts?: SandboxApiOpts): Promise
```
Create a snapshot of the sandbox's current state.
The sandbox will be paused while the snapshot is being created.
The snapshot can be used to create new sandboxes with the same filesystem and state.
Snapshots are persistent and survive sandbox deletion.
Use the returned `snapshotId` with `Sandbox.create(snapshotId)` to create a new sandbox from the snapshot.
###### Parameters
| Parameter | Type | Description |
| --------- | ---------------- | ------------------- |
| `opts`? | `SandboxApiOpts` | connection options. |
###### Returns
`Promise`\<`SnapshotInfo`>
snapshot information including the snapshot ID.
###### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
const sandbox = await Sandbox.create()
await sandbox.files.write('/app/state.json', '{"step": 1}')
// Create a snapshot
const snapshot = await sandbox.createSnapshot()
// Create a new sandbox from the snapshot
const newSandbox = await Sandbox.create(snapshot.snapshotId)
```
### downloadUrl()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
downloadUrl(path: string, opts?: SandboxUrlOpts): Promise
```
Get the URL to download a file from the sandbox.
###### Parameters
| Parameter | Type | Description |
| --------- | ---------------- | -------------------------------- |
| `path` | `string` | path to the file in the sandbox. |
| `opts`? | `SandboxUrlOpts` | download url options. |
###### Returns
`Promise`\<`string`>
URL for downloading file.
### getHost()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
getHost(port: number): string
```
Get the host address for the specified sandbox port.
You can then use this address to connect to the sandbox port from outside the sandbox via HTTP or WebSocket.
###### Parameters
| Parameter | Type | Description |
| --------- | -------- | ---------------------------------- |
| `port` | `number` | number of the port in the sandbox. |
###### Returns
`string`
host address of the sandbox port.
###### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
const sandbox = await Sandbox.create()
// Start an HTTP server
await sandbox.commands.exec('python3 -m http.server 3000')
// Get the hostname of the HTTP server
const serverURL = sandbox.getHost(3000)
```
### getInfo()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
getInfo(opts?: Pick): Promise
```
Get sandbox information like sandbox ID, template, metadata, started at/end at date.
###### Parameters
| Parameter | Type | Description |
| --------- | -------------------------------------------- | ------------------- |
| `opts`? | `Pick`\<`SandboxOpts`, `"requestTimeoutMs"`> | connection options. |
###### Returns
`Promise`\<`SandboxInfo`>
information about the sandbox
### getMcpToken()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
getMcpToken(): Promise
```
Get the MCP token for the sandbox.
###### Returns
`Promise`\<`undefined` | `string`>
MCP token for the sandbox, or undefined if MCP is not enabled.
### getMcpUrl()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
getMcpUrl(): string
```
Get the MCP URL for the sandbox.
###### Returns
`string`
MCP URL for the sandbox.
### getMetrics()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
getMetrics(opts?: SandboxMetricsOpts): Promise
```
Get the metrics of the sandbox.
###### Parameters
| Parameter | Type | Description |
| --------- | -------------------- | ------------------- |
| `opts`? | `SandboxMetricsOpts` | connection options. |
###### Returns
`Promise`\<`SandboxMetrics`\[]>
List of sandbox metrics containing CPU, memory and disk usage information.
### isRunning()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
isRunning(opts?: Pick): Promise
```
Check if the sandbox is running.
###### Parameters
| Parameter | Type |
| --------- | ----------------------------------------------- |
| `opts`? | `Pick`\<`ConnectionOpts`, `"requestTimeoutMs"`> |
###### Returns
`Promise`\<`boolean`>
`true` if the sandbox is running, `false` otherwise.
###### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
const sandbox = await Sandbox.create()
await sandbox.isRunning() // Returns true
await sandbox.kill()
await sandbox.isRunning() // Returns false
```
### kill()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
kill(opts?: Pick): Promise
```
Kill the sandbox.
###### Parameters
| Parameter | Type | Description |
| --------- | -------------------------------------------- | ------------------- |
| `opts`? | `Pick`\<`SandboxOpts`, `"requestTimeoutMs"`> | connection options. |
###### Returns
`Promise`\<`void`>
### listSnapshots()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
listSnapshots(opts?: Omit): SnapshotPaginator
```
List all snapshots created from this sandbox.
###### Parameters
| Parameter | Type | Description |
| --------- | ------------------------------------------ | ------------- |
| `opts`? | `Omit`\<`SnapshotListOpts`, `"sandboxId"`> | list options. |
###### Returns
`SnapshotPaginator`
paginator for listing snapshots from this sandbox.
### pause()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
pause(opts?: ConnectionOpts): Promise
```
Pause a sandbox by its ID.
###### Parameters
| Parameter | Type | Description |
| --------- | ---------------- | ------------------- |
| `opts`? | `ConnectionOpts` | connection options. |
###### Returns
`Promise`\<`boolean`>
sandbox ID that can be used to resume the sandbox.
###### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
const sandbox = await Sandbox.create()
await sandbox.pause()
```
### setTimeout()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
setTimeout(timeoutMs: number, opts?: Pick): Promise
```
Set the timeout of the sandbox.
This method can extend or reduce the sandbox timeout set when creating the sandbox or from the last call to `.setTimeout`.
Maximum time a sandbox can be kept alive is 24 hours (86\_400\_000 milliseconds) for Pro users and 1 hour (3\_600\_000 milliseconds) for Hobby users.
###### Parameters
| Parameter | Type | Description |
| ----------- | -------------------------------------------- | ---------------------------- |
| `timeoutMs` | `number` | timeout in **milliseconds**. |
| `opts`? | `Pick`\<`SandboxOpts`, `"requestTimeoutMs"`> | connection options. |
###### Returns
`Promise`\<`void`>
### uploadUrl()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
uploadUrl(path?: string, opts?: SandboxUrlOpts): Promise
```
Get the URL to upload a file to the sandbox.
You have to send a POST request to this URL with the file as multipart/form-data.
###### Parameters
| Parameter | Type | Description |
| --------- | ---------------- | -------------------------------- |
| `path`? | `string` | path to the file in the sandbox. |
| `opts`? | `SandboxUrlOpts` | download url options. |
###### Returns
`Promise`\<`string`>
URL for uploading file.
### betaCreate()
###### betaCreate(this, opts)
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
static betaCreate(this: S, opts?: SandboxBetaCreateOpts): Promise>
```
**`Beta`**
This feature is in beta and may change in the future.
Create a new sandbox from the default `base` sandbox template.
###### Type Parameters
| Type Parameter |
| -------------------------------- |
| `S` *extends* *typeof* `Sandbox` |
###### Parameters
| Parameter | Type | Description |
| --------- | ----------------------- | ------------------- |
| `this` | `S` | - |
| `opts`? | `SandboxBetaCreateOpts` | connection options. |
###### Returns
`Promise`\<`InstanceType`\<`S`>>
sandbox instance for the new sandbox.
###### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
const sandbox = await Sandbox.betaCreate()
```
###### Constructs
Sandbox
###### betaCreate(this, template, opts)
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
static betaCreate(
this: S,
template: string,
opts?: SandboxBetaCreateOpts): Promise>
```
**`Beta`**
This feature is in beta and may change in the future.
Create a new sandbox from the specified sandbox template.
###### Type Parameters
| Type Parameter |
| -------------------------------- |
| `S` *extends* *typeof* `Sandbox` |
###### Parameters
| Parameter | Type | Description |
| ---------- | ----------------------- | ---------------------------- |
| `this` | `S` | - |
| `template` | `string` | sandbox template name or ID. |
| `opts`? | `SandboxBetaCreateOpts` | connection options. |
###### Returns
`Promise`\<`InstanceType`\<`S`>>
sandbox instance for the new sandbox.
###### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
const sandbox = await Sandbox.betaCreate('')
```
###### Constructs
Sandbox
### ~~betaPause()~~
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
static betaPause(sandboxId: string, opts?: SandboxApiOpts): Promise
```
###### Parameters
| Parameter | Type |
| ----------- | ---------------- |
| `sandboxId` | `string` |
| `opts`? | `SandboxApiOpts` |
###### Returns
`Promise`\<`boolean`>
###### Deprecated
Use SandboxApi.pause instead.
### connect()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
static connect(
this: S,
sandboxId: string,
opts?: SandboxConnectOpts): Promise>
```
Connect to a sandbox. If the sandbox is paused, it will be automatically resumed.
Sandbox must be either running or be paused.
With sandbox ID you can connect to the same sandbox from different places or environments (serverless functions, etc).
###### Type Parameters
| Type Parameter |
| -------------------------------- |
| `S` *extends* *typeof* `Sandbox` |
###### Parameters
| Parameter | Type | Description |
| ----------- | -------------------- | ------------------- |
| `this` | `S` | - |
| `sandboxId` | `string` | sandbox ID. |
| `opts`? | `SandboxConnectOpts` | connection options. |
###### Returns
`Promise`\<`InstanceType`\<`S`>>
A running sandbox instance
###### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
const sandbox = await Sandbox.create()
const sandboxId = sandbox.sandboxId
// Connect to the same sandbox.
const sameSandbox = await Sandbox.connect(sandboxId)
```
### create()
###### create(this, opts)
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
static create(this: S, opts?: SandboxOpts): Promise>
```
Create a new sandbox from the default `base` sandbox template.
###### Type Parameters
| Type Parameter |
| -------------------------------- |
| `S` *extends* *typeof* `Sandbox` |
###### Parameters
| Parameter | Type | Description |
| --------- | ------------- | ------------------- |
| `this` | `S` | - |
| `opts`? | `SandboxOpts` | connection options. |
###### Returns
`Promise`\<`InstanceType`\<`S`>>
sandbox instance for the new sandbox.
###### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
const sandbox = await Sandbox.create()
```
###### Constructs
Sandbox
###### create(this, template, opts)
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
static create(
this: S,
template: string,
opts?: SandboxOpts): Promise>
```
Create a new sandbox from the specified sandbox template.
###### Type Parameters
| Type Parameter |
| -------------------------------- |
| `S` *extends* *typeof* `Sandbox` |
###### Parameters
| Parameter | Type | Description |
| ---------- | ------------- | ---------------------------- |
| `this` | `S` | - |
| `template` | `string` | sandbox template name or ID. |
| `opts`? | `SandboxOpts` | connection options. |
###### Returns
`Promise`\<`InstanceType`\<`S`>>
sandbox instance for the new sandbox.
###### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
const sandbox = await Sandbox.create('')
```
###### Constructs
Sandbox
### createSnapshot()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
static createSnapshot(sandboxId: string, opts?: SandboxApiOpts): Promise
```
Create a snapshot from a sandbox.
The sandbox will be paused while the snapshot is being created.
The snapshot can be used to create new sandboxes with the same state.
The snapshot is a persistent image that survives sandbox deletion.
###### Parameters
| Parameter | Type | Description |
| ----------- | ---------------- | ----------------------------------- |
| `sandboxId` | `string` | sandbox ID to create snapshot from. |
| `opts`? | `SandboxApiOpts` | connection options. |
###### Returns
`Promise`\<`SnapshotInfo`>
snapshot information including the snapshot name that can be used with Sandbox.create().
### deleteSnapshot()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
static deleteSnapshot(snapshotId: string, opts?: SandboxApiOpts): Promise
```
Delete a snapshot.
###### Parameters
| Parameter | Type | Description |
| ------------ | ---------------- | ------------------- |
| `snapshotId` | `string` | snapshot ID. |
| `opts`? | `SandboxApiOpts` | connection options. |
###### Returns
`Promise`\<`boolean`>
`true` if the snapshot was deleted, `false` if it was not found.
### getFullInfo()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
static getFullInfo(sandboxId: string, opts?: SandboxApiOpts): Promise