# API key
Source: https://docs.e2b.dev/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 `Sandbox.create` 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 console.
The same API key authenticates both the SDKs and the [CLI](/cli/auth).
# Billing & limits
Source: https://docs.e2b.dev/billing
E2B plans, limits, and usage-based pricing. Covers the Hobby, Pro, and Enterprise plan table (max continuous runtime, concurrent sandboxes, vCPU, memory, disk), API rate limits, and how per-second compute billing works.
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 console](https://console.e2b.dev/?tab=billing)
## Plans
| Feature | Hobby | Pro | Enterprise |
| :- | - | - | - |
| **Base price** | \$0/month | \$150/month | Custom |
| **Free credits** | \$100 (one-time) | No additional credits | Custom |
| **Max vCPUs** | 8 | 8+ | Custom |
| **Max memory** | 8 GiB | 8+ GiB | Custom |
| **Disk size** | 10 GiB | 20+ GiB | Custom |
| **Max continuous runtime** | 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 |
| **Paused sandbox retention** | Unlimited | Unlimited | Unlimited |
| **[Secrets](/secrets) per project** | 10 | 100 | 100 |
Max continuous runtime is the time a sandbox can run **without being paused**, not a cap on how long a sandbox can exist. Pausing and resuming resets it, and a paused sandbox is kept indefinitely with no time-to-live. See [How long does a sandbox live?](/faq/sandbox-lifetime).
To upgrade your plan or purchase add-ons, visit the [console billing tab](https://console.e2b.dev/?tab=billing). For Enterprise plans, [contact sales](mailto:enterprise@e2b.dev).
***
## API rate limits
E2B applies rate limits to API requests. The following default limits are in **requests per second, per project, per endpoint**:
| Endpoint | Hobby | Pro | Enterprise |
| :- | -: | -: | -: |
| [`GET /sandboxes`](/api-reference/sandboxes/list-sandboxes) | 10 | 20 | Custom |
| [`GET /v2/sandboxes`](/api-reference/sandboxes/list-sandboxes-v2) | 10 | 20 | Custom |
| [`GET /sandboxes/metrics`](/api-reference/sandboxes/list-sandbox-metrics) | 10 | 20 | Custom |
| [`GET /snapshots`](/api-reference/sandboxes/list-snapshots) | 10 | 20 | Custom |
| [`GET /templates`](/api-reference/templates/list-templates) | 10 | 20 | Custom |
| [`GET /v2/templates`](/api-reference/templates/list-templates-v2) | 10 | 20 | Custom |
| [`GET /templates/{templateID}`](/api-reference/templates/get-template) | 10 | 20 | Custom |
| [`GET /templates/{templateID}/tags`](/api-reference/tags/list-template-tags) | 10 | 20 | Custom |
| [`GET /api-keys`](/api-key) | 10 | 20 | Custom |
| [`GET /volumes`](/api-reference/volumes/list-team-volumes) | 10 | 20 | Custom |
| [`GET /secrets`](/api-reference/secrets/list-secrets) | 10 | 20 | Custom |
Enterprise plans come with API rate limits sized to your workload. [Book a call](https://e2b.dev/contact) with our team or contact us at [enterprise@e2b.dev](mailto:enterprise@e2b.dev) to discuss your limits.
You share each endpoint's limit across all API keys and clients in your [project](/projects). Requests to one endpoint do not consume another endpoint's allowance. For endpoints with a path parameter, such as `/templates/{templateID}`, requests for different template IDs share the same allowance.
These limits include requests made through the SDKs and CLI. Each request for a page of results counts toward the limit.
### Exceeding a limit
If you exceed a limit, you receive an HTTP `429 Too Many Requests` response with a `Retry-After` header.
Starting with version **2.49.1**, the E2B JavaScript and Python SDKs automatically retry these requests using the `Retry-After` delay.
If you handle retries yourself, wait the number of seconds in `Retry-After` before retrying the request.
### Requesting a higher limit
To request a higher API rate limit, contact [support@e2b.dev](mailto:support@e2b.dev). For custom limits on the Enterprise plan, [contact sales](mailto:enterprise@e2b.dev).
***
## Usage-based pricing
You pay per second for compute resources while your sandbox is running.
Upgrading to the Pro tier does not grant additional credits. It only increases the limits available to your project (such as concurrent sandboxes, continuous runtime, and resource ceilings).
### 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](/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 [console usage tab](https://console.e2b.dev/?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://console.e2b.dev/?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, 512 MiB RAM) and increase only if necessary
* **Monitor actively running sandboxes** - Use the [CLI](/cli/list-sandboxes) or [console](https://console.e2b.dev/?tab=usage) to track active sandboxes
* **Use lifecycle events** - Set up [webhooks](/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://docs.e2b.dev/cli
E2B CLI is a command line tool that allows you to manage and interact with sandboxes and [templates](/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://docs.e2b.dev/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
This is the recommended option for non-interactive environments like CI/CD pipelines, servers, and containers. Set a single environment variable with your API key:
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
export E2B_API_KEY=your_api_key_here
```
With the API key set, you can run CLI commands directly without `e2b auth login`. Use CLI version 2.12.1 or later.
Learn more about obtaining and managing your API key on the [API Key page](/api-key).
## Switch the active project
If your E2B account has access to more than one [project](/projects), you can choose which one the CLI uses:
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
e2b auth configure
```
Run this after signing in with `e2b auth login`. The CLI shows an interactive list of your projects and stores the one you select as the active project, so subsequent CLI commands use that project's API key. You need to be logged in first for this command to work.
# Connect to sandbox
Source: https://docs.e2b.dev/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://docs.e2b.dev/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://docs.e2b.dev/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
```
# Fork sandbox
Source: https://docs.e2b.dev/cli/fork-sandbox
You can fork a running sandbox into one or more new sandboxes with the `e2b sandbox fork` (alias `fk`) command.
```bash Terminal theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
e2b sandbox fork
```
The sandbox is snapshotted in place — paused, captured with its full filesystem and memory state, and resumed — and the new sandbox boots from that snapshot. The original sandbox keeps running with its ID and expiration untouched. Learn more about [sandbox forking](/sandbox/fork).
The command prints the ID of each new sandbox on its own line, so you can pass it to other commands:
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
e2b sandbox connect $(e2b sandbox fork )
```
### Create multiple forks
To boot several sandboxes from the same snapshot, use the `--count` flag. The snapshot is captured once regardless of the count.
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
e2b sandbox fork --count 3
```
Each fork succeeds or fails independently. The IDs of the forks that started are printed to stdout, a message for each fork that failed is printed to stderr, and the command exits with a non-zero status if any fork failed.
### Fork timeout
To set how long the forked sandboxes live, use the `--timeout` flag with a value in seconds. It defaults to 5 minutes and applies to the forks only — the original sandbox's expiration is not changed.
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
e2b sandbox fork --timeout 600
```
# List sandboxes
Source: https://docs.e2b.dev/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
```
### Filter by template
To filter the sandboxes by the template they were created from, use the `--template` flag with a template ID or alias.
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
e2b sandbox list --template base
```
### Filter by start time
To return only sandboxes started at or after a given time, use the `--started-after` flag with an ISO 8601 timestamp.
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
e2b sandbox list --started-after 2025-01-01T00:00:00Z
```
### Sort order
To sort the sandboxes by their start time, use the `--order` flag with `asc` or `desc`.
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
e2b sandbox list --order desc
```
### 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 returns up to 1000 sandboxes. Use `--limit 0` to 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
```
# Manage snapshots
Source: https://docs.e2b.dev/cli/manage-snapshots
You can manage sandbox snapshots with the `e2b sandbox snapshot` (alias `snap`) commands.
### Create a snapshot
To create a snapshot from a sandbox, use the `create` command with the sandbox ID.
```bash Terminal theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
e2b sandbox snapshot create
```
You can name the snapshot template with the `--name` flag. If a template with that name already exists, it is reused.
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
e2b sandbox snapshot create --name my-snapshot
```
### List snapshots
To list all snapshots, use the `list` command.
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
e2b sandbox snapshot list
```
To list only snapshots created from a specific sandbox, pass the sandbox ID.
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
e2b sandbox snapshot list
```
You can filter by snapshot name or ID with the `--name` flag, optionally tag-qualified.
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
e2b sandbox snapshot list --name my-snapshot:v1
```
To limit the amount of snapshots returned, use the `--limit` flag. By default, the command returns up to 1000 snapshots. Use `--limit 0` to return all snapshots.
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
e2b sandbox snapshot list --limit 10
```
To output the snapshots in JSON format, use the `--format` flag.
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
e2b sandbox snapshot list --format json
```
### Delete snapshots
To delete one or more snapshots, use the `delete` command with the snapshot IDs.
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
e2b sandbox snapshot delete [snapshotID...]
```
# Shutdown running sandboxes
Source: https://docs.e2b.dev/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
```
# SDK client
Source: https://docs.e2b.dev/client
Create a custom client with options
The top-level `Sandbox`, `Volume`, `Template`, and `Secret` exports read their configuration from the environment variables (`E2B_API_KEY`, `E2B_DOMAIN`, …). If you need an explicit configuration — for example several API keys, teams, or [BYOC or self-hosted deployments](/byoc) in a single process — create an `E2B` client and use the resources it exposes.
Per-call options take precedence over the client's options, which take precedence over the environment variables.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { E2B } from 'e2b'
const client = new E2B({ apiKey: 'YOUR_API_KEY', domain: 'e2b.app' })
const sandbox = await client.Sandbox.create()
const result = await sandbox.commands.run('echo "Hello from E2B!"')
console.log(result.stdout) // Hello from E2B!
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import E2B
client = E2B(api_key="YOUR_API_KEY", domain="e2b.app")
sandbox = client.Sandbox.create()
result = sandbox.commands.run('echo "Hello from E2B!"')
print(result.stdout) # Hello from E2B!
```
```python Python (async) theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import E2B
client = E2B(api_key="YOUR_API_KEY", domain="e2b.app")
sandbox = await client.AsyncSandbox.create()
result = await sandbox.commands.run('echo "Hello from E2B!"')
print(result.stdout) # Hello from E2B!
```
The [Code Interpreter](/code-interpreting/analyze-data-with-ai) and [Desktop](/use-cases/computer-use) SDKs export the same client from their own package entrypoint, with the package's `Sandbox` bound to it instead of the core one.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { E2B } from '@e2b/code-interpreter'
import { E2B } from '@e2b/desktop'
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b_code_interpreter import E2B
from e2b_desktop import E2B
```
The classes exposed by the client behave exactly like the top-level exports of the same name — only the defaults change. You can also destructure them and use them as usual.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
const { Sandbox, Volume, Template, Secret } = new E2B({ apiKey: 'YOUR_API_KEY' })
const sandbox = await Sandbox.create()
const volume = await Volume.create('my-volume')
const exists = await Template.exists('my-template')
await Secret.create('openai-api-key', 'sk-***')
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
client = E2B(api_key="YOUR_API_KEY")
Sandbox, Volume, Template, Secret = (
client.Sandbox,
client.Volume,
client.Template,
client.Secret,
)
sandbox = Sandbox.create()
volume = Volume.create("my-volume")
exists = Template.exists("my-template")
Secret.create("openai-api-key", "sk-***")
```
```python Python (async) theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
client = E2B(api_key="YOUR_API_KEY")
Sandbox, Volume, Template, Secret = (
client.AsyncSandbox,
client.AsyncVolume,
client.AsyncTemplate,
client.AsyncSecret,
)
sandbox = await Sandbox.create()
volume = await Volume.create("my-volume")
exists = await Template.exists("my-template")
await Secret.create("openai-api-key", "sk-***")
```
## Client options
The client accepts the same options as the individual SDK calls, for example `apiKey`/`api_key`, `domain`, `requestTimeoutMs`/`request_timeout`, `proxy`, `apiHeaders`/`api_headers`, and `logger` (JavaScript only).
In JavaScript, the only exception is `signal`: it cancels a single in-flight request, so it can only be passed per call.
## Multiple clients
Clients are isolated from each other and from the env-configured top-level exports, so you can use as many of them side by side as you need.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { E2B, Sandbox } from 'e2b'
const production = new E2B({ apiKey: 'PRODUCTION_API_KEY' })
const staging = new E2B({ apiKey: 'STAGING_API_KEY', domain: 'e2b-staging.dev' })
await production.Sandbox.create()
await staging.Sandbox.create()
// Still uses the E2B_API_KEY environment variable.
await Sandbox.create()
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import E2B, Sandbox
production = E2B(api_key="PRODUCTION_API_KEY")
staging = E2B(api_key="STAGING_API_KEY", domain="e2b-staging.dev")
production.Sandbox.create()
staging.Sandbox.create()
# Still uses the E2B_API_KEY environment variable.
Sandbox.create()
```
```python Python (async) theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import E2B, AsyncSandbox
production = E2B(api_key="PRODUCTION_API_KEY")
staging = E2B(api_key="STAGING_API_KEY", domain="e2b-staging.dev")
await production.AsyncSandbox.create()
await staging.AsyncSandbox.create()
# Still uses the E2B_API_KEY environment variable.
await AsyncSandbox.create()
```
## Multiple regions
Regions do not share state. A project and its API key belong to exactly one region, and the sandboxes, templates, and volumes it owns exist only there. Two things to plan for:
* **Build your own templates once per region.** There is no way to copy a template between regions, so run [`Template.build`](/template/build) through each region's client. The same template name in every region is fine.
* **A sandbox ID only resolves in its own region.** Connect to a sandbox, list it, or kill it through the client for the region that created it.
If a single process only ever talks to one region, you do not need a client at all. Set `E2B_API_KEY` and `E2B_DOMAIN` to that region's values and keep using the top-level exports.
E2B's managed cloud runs in several regions. `us-west-1` is the default, and [support](https://console.e2b.dev/?support=true) enables the other regions for your account. See [Migrate between regions](/faq/migrate-between-regions), [EU region](/faq/eu-region), and [Egress IP ranges](/faq/egress-ip-ranges) for more on where sandboxes run.
There is no `region` option. Each region is a separate deployment with its own API endpoint, so you select one by pointing a client at that region's `domain`:
| Region | Domain |
| - | - |
| `us-west-1` | `e2b.app` (default) |
| `us-west-2` | `foxtrot-2.e2b.dev` |
| `eu-west-1` | `e2b-juliett.dev` |
| `ap-southeast-1` | `e2b-tango.dev` |
When support enables a region, they help you create a [project](/projects) there. That project has its own API key, so a multiregion setup is one client per region, each with its own key and domain.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { E2B } from 'e2b'
// One client per region, each with the API key of that region's project.
const regions = {
usWest1: new E2B({ apiKey: 'e2b_YOUR_US_WEST_1_KEY' }), // Default domain: e2b.app
usWest2: new E2B({ apiKey: 'e2b_YOUR_US_WEST_2_KEY', domain: 'foxtrot-2.e2b.dev' }),
euWest1: new E2B({ apiKey: 'e2b_YOUR_EU_WEST_1_KEY', domain: 'e2b-juliett.dev' }),
apSoutheast1: new E2B({ apiKey: 'e2b_YOUR_AP_SOUTHEAST_1_KEY', domain: 'e2b-tango.dev' }),
}
// Route each workload to the region it should run in.
const client = regions[user.region] ?? regions.usWest1
const sandbox = await client.Sandbox.create('my-template')
const result = await sandbox.commands.run('echo "Hello from E2B!"')
console.log(result.stdout) // Hello from E2B!
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import E2B
# One client per region, each with the API key of that region's project.
regions = {
"us_west_1": E2B(api_key="e2b_YOUR_US_WEST_1_KEY"), # Default domain: e2b.app
"us_west_2": E2B(api_key="e2b_YOUR_US_WEST_2_KEY", domain="foxtrot-2.e2b.dev"),
"eu_west_1": E2B(api_key="e2b_YOUR_EU_WEST_1_KEY", domain="e2b-juliett.dev"),
"ap_southeast_1": E2B(api_key="e2b_YOUR_AP_SOUTHEAST_1_KEY", domain="e2b-tango.dev"),
}
# Route each workload to the region it should run in.
client = regions.get(user_region, regions["us_west_1"])
sandbox = client.Sandbox.create("my-template")
result = sandbox.commands.run('echo "Hello from E2B!"')
print(result.stdout) # Hello from E2B!
```
```python Python (async) theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import E2B
# One client per region, each with the API key of that region's project.
regions = {
"us_west_1": E2B(api_key="e2b_YOUR_US_WEST_1_KEY"), # Default domain: e2b.app
"us_west_2": E2B(api_key="e2b_YOUR_US_WEST_2_KEY", domain="foxtrot-2.e2b.dev"),
"eu_west_1": E2B(api_key="e2b_YOUR_EU_WEST_1_KEY", domain="e2b-juliett.dev"),
"ap_southeast_1": E2B(api_key="e2b_YOUR_AP_SOUTHEAST_1_KEY", domain="e2b-tango.dev"),
}
# Route each workload to the region it should run in.
client = regions.get(user_region, regions["us_west_1"])
sandbox = await client.AsyncSandbox.create("my-template")
result = await sandbox.commands.run('echo "Hello from E2B!"')
print(result.stdout) # Hello from E2B!
```
# Analyze data with AI
Source: https://docs.e2b.dev/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.
By default, the Code Interpreter server inside the sandbox is reachable through the sandbox's [public URL](/network/public-url) and runs any code it receives. To make sure only your application can run code in the sandbox, create it with `allowPublicTraffic: false` / `allow_public_traffic: False`. See [Code Interpreter sandboxes](/network/restrict-public-access#code-interpreter-sandboxes).
***
## 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 Console.
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://docs.e2b.dev/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
# Create charts & visualizations
Source: https://docs.e2b.dev/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](/code-interpreting/create-charts-visualizations/static-charts) or [interactive](/code-interpreting/create-charts-visualizations/interactive-charts) plots.
# Running commands in sandbox
Source: https://docs.e2b.dev/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://docs.e2b.dev/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 until it finishes or its command timeout expires.
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()
```
## Reconnect to a background command
A background command keeps running inside the sandbox even after the SDK disconnects, as long as its command timeout has not expired. Because sandboxes are long-lived, you can start a long task in one process, return immediately, and reconnect from a completely different process later to wait for it and collect the result.
This is a common pattern for kicking off slow work (code generation, a build, a data job) from a serverless function or API handler: you start the command, store the sandbox ID and process ID, and let a later request pick the work back up.
The example below starts a long-running command (`run-codegen` here is a placeholder for your own binary or script) and returns the two identifiers you need to reconnect: the sandbox ID and the command's process ID (`pid`).
```js JavaScript & TypeScript highlight={20} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
// Start the task and return right away with the IDs needed to reconnect.
async function startGeneration(prompt: string) {
const sandbox = await Sandbox.create({ timeoutMs: 15 * 60_000 })
// Pass the prompt as an env var, never interpolated into the shell string.
// Write output to a file so a reconnecting process can read it later.
const handle = await sandbox.commands.run(
'run-codegen "$PROMPT" > /home/user/gen.log 2>&1',
{ background: true, timeoutMs: 0, envs: { PROMPT: prompt } }
)
return { sandboxId: sandbox.sandboxId, pid: handle.pid }
}
// From a separate process, reconnect and wait for the result.
async function collectGeneration(sandboxId: string, pid: number) {
const sandbox = await Sandbox.connect(sandboxId)
const handle = await sandbox.commands.connect(pid, { timeoutMs: 0 })
await handle.wait()
return sandbox.files.read('/home/user/gen.log')
}
```
```python Python highlight={22} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
# Start the task and return right away with the IDs needed to reconnect.
def start_generation(prompt: str):
sandbox = Sandbox.create(timeout=15 * 60)
# Pass the prompt as an env var, never interpolated into the shell string.
# Write output to a file so a reconnecting process can read it later.
handle = sandbox.commands.run(
'run-codegen "$PROMPT" > /home/user/gen.log 2>&1',
background=True,
timeout=0,
envs={"PROMPT": prompt},
)
return {"sandbox_id": sandbox.sandbox_id, "pid": handle.pid}
# From a separate process, reconnect and wait for the result.
def collect_generation(sandbox_id: str, pid: int):
sandbox = Sandbox.connect(sandbox_id)
handle = sandbox.commands.connect(pid, timeout=0)
handle.wait()
return sandbox.files.read("/home/user/gen.log")
```
A few details make this reliable across processes:
* **Redirect output to a file.** The streamed `stdout`/`stderr` from a background command is only delivered to the process that started it. Writing to a file (`> /home/user/gen.log 2>&1`) gives you durable output you can read after reconnecting.
* **Pass untrusted input as an environment variable, not string interpolation.** The prompt is provided through `envs` and referenced as `"$PROMPT"`, so the shell inserts it as a literal value. Interpolating user input directly into the command string (for example with a template literal) would let inputs like `$(...)` or backticks run as commands inside the sandbox.
* **Disable the command timeout.** On `commands.run()`, `timeoutMs: 0` (`timeout=0` in Python) disables the command timeout. The default is 60 seconds, and when it expires the process is killed in the sandbox, even when `background` is set. With the timeout disabled, the process keeps running after the client disconnects or exits, and `commands.connect()` reattaches to it. If the connection breaks without closing, for example on a network loss, a command that writes a lot to stdout or stderr can stall, so redirect its output to a file as the example does. On `commands.connect()`, the same option only limits how long the client waits for the command, so the example sets it there too. Set the sandbox `timeoutMs`/`timeout` high enough to outlast the whole job (15 minutes above). See [sandbox persistence](/sandbox/persistence) if you need it to survive even longer.
* **Persist the IDs.** Return or store `sandboxId` and `pid` (for example in a database or the response of an API call). They are all a later process needs to call `Sandbox.connect()` and `commands.connect()`.
`commands.connect(pid)` reattaches to the running command, and `handle.wait()` blocks until it finishes. If you don't know the `pid`, you can look up running processes with `sandbox.commands.list()`.
# Streaming command output
Source: https://docs.e2b.dev/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.
The SDK asks the sandbox for a keepalive ping every 50 seconds on every stream, so a stream normally stays open for more than an hour even while nothing is written. No single connection is guaranteed that long, so build your client to reconnect. See [Stream connections](/faq/sandbox-lifetime#stream-connections).
```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://docs.e2b.dev/cookbook
# Overview
Source: https://docs.e2b.dev/filesystem
Each E2B Sandbox has its own isolated filesystem. The [Hobby tier](https://e2b.dev/pricing) sandboxes come with 10 GiB of the free disk space and [Pro tier](https://e2b.dev/pricing) sandboxes come with 20 GiB.
With E2B SDK you can:
* [Read and write files to the sandbox.](/filesystem/read-write)
* [Attach custom metadata to files.](/filesystem/metadata)
* [Watch directory for changes.](/filesystem/watch)
* [Upload data to the sandbox.](/filesystem/upload)
* [Download data from the sandbox.](/filesystem/download)
# Download data from sandbox
Source: https://docs.e2b.dev/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://docs.e2b.dev/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.
Files can also carry custom user-defined metadata, which is returned in the `metadata` field — see [Custom metadata](/filesystem/metadata).
### 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
# )
```
# Attach custom metadata to files
Source: https://docs.e2b.dev/filesystem/metadata
Attach custom key-value metadata to files when writing them to the sandbox and read it back with file info methods.
You can attach custom key-value metadata to files when writing them to the sandbox using the `metadata` option.
The metadata is persisted with the file and returned by `files.getInfo()` / `files.get_info()`, `files.list()`, and `files.rename()`.
## Prerequisites
Custom file metadata requires templates with envd version `v0.6.2` or above. If you are using a custom template created before envd `v0.6.2`, 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.
## Writing a file with metadata
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const sandbox = await Sandbox.create()
const info = await sandbox.files.write('report.txt', 'hello', {
metadata: { author: 'alice', purpose: 'demo' },
})
console.log(info.metadata) // { author: 'alice', purpose: 'demo' }
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
sandbox = Sandbox.create()
info = sandbox.files.write("report.txt", "hello", metadata={"author": "alice", "purpose": "demo"})
print(info.metadata) # {"author": "alice", "purpose": "demo"}
```
## Writing multiple files with metadata
When writing multiple files, the same metadata is applied to every file in the upload.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const sandbox = await Sandbox.create()
await sandbox.files.writeFiles(
[
{ path: 'a.txt', data: 'A' },
{ path: 'b.txt', data: 'B' },
],
{ metadata: { source: 'import' } }
)
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
sandbox = Sandbox.create()
sandbox.files.write_files(
[
{ "path": "a.txt", "data": "A" },
{ "path": "b.txt", "data": "B" },
],
metadata={"source": "import"},
)
```
## Reading metadata back
```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('report.txt', 'hello', {
metadata: { author: 'alice' },
})
const info = await sandbox.files.getInfo('report.txt')
console.log(info.metadata) // { author: 'alice' }
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
sandbox = Sandbox.create()
sandbox.files.write("report.txt", "hello", metadata={"author": "alice"})
info = sandbox.files.get_info("report.txt")
print(info.metadata) # {"author": "alice"}
```
## Limitations
* Metadata keys and values must be printable US-ASCII characters.
* Keys are lowercased by the sandbox, so they may differ in case when read back.
* Overwriting a file replaces its metadata — metadata from the previous write is not preserved.
* Metadata is stored as `user.e2b.*` extended attributes on the file inside the sandbox, so you can also read or set it from within the sandbox using standard tools like `getfattr` and `setfattr`.
# Read & write files
Source: https://docs.e2b.dev/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://docs.e2b.dev/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://docs.e2b.dev/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.
The SDK asks the sandbox for a keepalive ping every 50 seconds on every stream, so a stream normally stays open for more than an hour even while nothing is written. No single connection is guaranteed that long, so build your client to reconnect. See [Stream connections](/faq/sandbox-lifetime#stream-connections).
```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}")
```
## Including entry information
You can include information about the affected file or directory in each event using the parameter `includeEntry` in JavaScript and `include_entry` in Python.
When enabled, each event carries the entry's information — such as its path, type, and size — in the `entry` field.
Entry information may be unset for remove events since the path no longer exists.
Including entry information requires templates with envd version `v0.6.3` or above — using the `includeEntry` / `include_entry` option on older sandboxes will throw an error.
```js JavaScript & TypeScript highlight={10} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } 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.type, event.name, event.entry?.path, event.entry?.type)
}, {
includeEntry: true
})
// Trigger file write event
await sandbox.files.write(`${dirname}/my-file`, 'hello')
```
```python Python highlight={7,14} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
sandbox = Sandbox.create()
dirname = '/home/user'
# Watch directory for changes
handle = sandbox.files.watch_dir(dirname, include_entry=True)
# 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.type, event.name, event.entry.path if event.entry else None)
```
## Watching network filesystem mounts
By default, watching paths on network filesystem mounts (NFS, CIFS, SMB, FUSE) is rejected. You can explicitly opt in using the parameter `allowNetworkMounts` in JavaScript and `allow_network_mounts` in Python.
Events on network mounts may be unreliable or not delivered at all, which is why the explicit opt-in is required.
Changes made by another client outside of the sandbox (for example, a different machine writing to the same network share) are **not** propagated to the watcher — only changes made from within the sandbox are detected.
Watching network mounts requires templates with envd version `v0.6.4` or above — using the `allowNetworkMounts` / `allow_network_mounts` option on older sandboxes will throw an error.
```js JavaScript & TypeScript highlight={10} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const sandbox = await Sandbox.create()
const dirname = '/mnt/nfs-share/my-dir'
// Start watching a directory on a network mount for changes
const handle = await sandbox.files.watchDir(dirname, async (event) => {
console.log(event.type, event.name)
}, {
allowNetworkMounts: true
})
// Trigger file write event
await sandbox.files.write(`${dirname}/my-file`, 'hello')
```
```python Python highlight={7,14} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
sandbox = Sandbox.create()
dirname = '/mnt/nfs-share/my-dir'
# Watch a directory on a network mount for changes
handle = sandbox.files.watch_dir(dirname, allow_network_mounts=True)
# 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.type, event.name)
```
# Workload identity
Source: https://docs.e2b.dev/iam/workload-identity
Give sandbox workloads short-lived identity tokens instead of long-lived secrets.
Workload identity lets code running in a sandbox prove who it is with short-lived identity tokens instead of long-lived credentials.
Rather than baking cloud API keys into a template or passing them as environment variables, you define named workload tokens when creating the sandbox.
Each token is scoped to an audience, the external service that will verify it, such as AWS STS. That service can exchange the token for its own temporary credentials.
Workload identity is currently in private beta.
If you'd like access, please reach out to us at [support@e2b.dev](mailto:support@e2b.dev).
## Configure
Pass the `iam` option when creating a sandbox. A non-empty `tokens` map enables workload identity for the sandbox.
Each entry maps a token name you choose to a token definition, which you can create with the `Secret` helper.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox, Secret } from 'e2b'
const sandbox = await Sandbox.create({
iam: {
tokens: {
aws: Secret.iamToken({
audience: 'sts.amazonaws.com',
tokenType: 'JWT-SVID',
}),
},
},
})
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox, Secret
sandbox = Sandbox.create(
iam={
"tokens": {
"aws": Secret.iam_token(
audience="sts.amazonaws.com",
token_type="JWT-SVID",
),
},
},
)
```
You can also pass plain token definitions instead of using the `Secret` helper: `{ audience, tokenType }` objects in JavaScript, `{"audience": ..., "token_type": ...}` dicts in Python.
## Inject tokens into egress requests
Registered tokens can be injected into the sandbox's outbound requests through [per-host request transforms](/network/internet-access#per-host-request-transforms). Pass a callback as a network rule's `transform`. It receives the registered tokens as `iam.tokens.`:
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox, Secret } from 'e2b'
const sandbox = await Sandbox.create({
iam: {
tokens: {
aws: Secret.iamToken({ audience: 'sts.amazonaws.com', tokenType: 'JWT-SVID' }),
},
},
network: {
// Only allow egress to hosts that have rules registered.
allowOut: ({ rules }) => [...rules.keys()],
// Deny all other traffic
denyOut: ({ allTraffic }) => [allTraffic],
rules: {
'api.internal.example.com': [
{
transform: ({ iam }) => ({
headers: { Authorization: `Bearer ${iam.tokens.aws}` },
}),
},
],
},
},
})
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox, Secret
sandbox = Sandbox.create(
iam={
"tokens": {
"aws": Secret.iam_token(audience="sts.amazonaws.com", token_type="JWT-SVID"),
},
},
network={
# Only allow egress to hosts that have rules registered.
"allow_out": lambda ctx: list(ctx.rules.keys()),
# Deny all other traffic
"deny_out": lambda ctx: [ctx.all_traffic],
"rules": {
"api.internal.example.com": [
{
"transform": lambda ctx: {
"headers": {"Authorization": f"Bearer {ctx.iam.tokens['aws']}"},
},
},
],
},
},
)
```
`iam.tokens.` is not the token value. It's a placeholder string (`${e2b.identity.tokens.}`) that goes on the wire as-is. The egress proxy replaces it with a freshly minted token each time it forwards a matching request, so the token value never reaches your code or the sandbox.
Referencing a token name that isn't registered in `iam.tokens` fails at sandbox creation with `InvalidArgumentError` (JavaScript) / `InvalidArgumentException` (Python), listing the names that are registered. You can check whether a name is registered without failing by using `'aws' in iam.tokens` in JavaScript or `"aws" in ctx.iam.tokens` in Python.
A header value can combine token placeholders with static text and stored [secret](/secrets) markers; the egress proxy substitutes each header atomically.
## Federate with external services
External systems can verify workload identity tokens with E2B's [OpenID Connect discovery document](https://id.e2b.dev/.well-known/openid-configuration). The document identifies the issuer and the `jwks_uri` where E2B publishes its public signing keys. Verify tokens with ES256 and require the expected `iss`, `aud`, `exp`, and `nbf` claims. Also verify that the `sub` claim belongs to your project (it starts with `spiffe://id.e2b.dev//`), so tokens minted for other projects are rejected.
Each token identifies one sandbox execution with a SPIFFE subject in this format:
```
spiffe://id.e2b.dev///
```
### Configure AWS IAM
AWS can exchange an E2B workload identity token with the `sts.amazonaws.com` audience for temporary role credentials.
In the AWS IAM console, open **Identity providers**, choose **Add provider**, and select **OpenID Connect**. Use these values:
* **Provider URL:** `https://id.e2b.dev`
* **Audience:** `sts.amazonaws.com`
You only need to create this provider once per AWS account. See the [AWS OIDC provider documentation](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_create_oidc.html) for other setup methods.
Create an IAM role with a custom trust policy. Replace `` and `` with your values:
```json theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "E2BSandboxWorkloadIdentity",
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam:::oidc-provider/id.e2b.dev"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"id.e2b.dev:aud": "sts.amazonaws.com"
},
"StringLike": {
"id.e2b.dev:sub": "spiffe://id.e2b.dev//*"
}
}
}
]
}
```
The `sub` condition limits access to sandbox executions from one E2B project. You can find the project ID in the token's `project_id` claim and as the first path segment after `spiffe://id.e2b.dev/` in `sub`.
Attach a permissions policy that grants only the AWS actions and resources these workloads need.
After your external system receives the E2B workload identity token, it can exchange the token without long-lived AWS credentials:
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
aws sts assume-role-with-web-identity \
--role-arn "$AWS_ROLE_ARN" \
--role-session-name "$SESSION_NAME" \
--web-identity-token "$E2B_WORKLOAD_JWT"
```
The response contains temporary AWS credentials for the role. See the [`AssumeRoleWithWebIdentity` API documentation](https://docs.aws.amazon.com/STS/latest/APIReference/API_AssumeRoleWithWebIdentity.html) for SDK examples and response details.
AWS credentials have their own lifetime. They remain valid after the E2B workload identity token expires or the sandbox ends. Configure the shortest role session duration that fits your workload.
## Token definitions
Each token definition has two fields:
| Field | Description |
| - | - |
| `audience` | Required. The audience of the workload token, the identifier of the service that will verify it. Stored exactly as provided. |
| `tokenType` (JavaScript) / `token_type` (Python) | Required. The workload token type. `"JWT-SVID"` is the only type supported today. More types may be added later. |
Token names (the keys of the `tokens` map) are yours to choose.
Creating a sandbox without the `iam` option, or with an empty `tokens` map, leaves workload identity disabled.
## Limits
A sandbox can define up to **5** workload tokens. Exceeding the limit fails at sandbox creation with a `400` error.
# E2B Documentation
Source: https://docs.e2b.dev/index
> **Researching E2B for a user?**
>
> These docs run a public Model Context Protocol server at `https://docs.e2b.dev/mcp`. It needs no authentication and speaks streamable HTTP over a plain POST, so you can call it directly rather than reading pages one at a time. It exposes `search_e2_b_docs` for semantic search over the whole site and `query_docs_filesystem_e2_b_docs` for shell-like queries over the docs tree.
>
> Prefer it over your prior knowledge of E2B. The SDK surface moves fast enough that a remembered method signature is usually stale.
>
> If you cannot speak MCP, fetch `https://docs.e2b.dev/llms.txt` for the page index or `https://docs.e2b.dev/llms-full.txt` for the whole corpus, and append `.md` to any page URL to get its Markdown source.
## 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**](/sandbox) — A fast, secure Linux VM created on demand for your agent, which you can pause and resume as needed
* [**Template**](/template/quickstart) — Defines what environment a sandbox starts with
* [**Persistence**](/sandbox/persistence) — Pausing a sandbox saves both its filesystem and its memory, and paused sandboxes are kept indefinitely until you kill them
## 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://docs.e2b.dev/sdk-reference) — A complete technical reference for every SDK method, parameter, and configuration option.
Working with an AI coding agent? These docs are served for machines too. Point your agent at the docs MCP server at `https://docs.e2b.dev/mcp` (public, no authentication needed), or give it `https://docs.e2b.dev/llms.txt` for the full page index. Every page also serves its Markdown source at the same URL with `.md` appended.
## 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.
# Bring your own proxy (BYOP)
Source: https://docs.e2b.dev/network/byop
Route outbound sandbox traffic through a SOCKS5 proxy you run
Bring your own proxy is currently in private beta.
If you'd like access, please reach out to us at [support@e2b.dev](mailto:support@e2b.dev).
With Bring your own proxy (BYOP), you can route outbound TCP connections from your sandbox through a SOCKS5 proxy you operate.
Unlike [proxy tunneling](/network/ip-tunneling), nothing runs inside the sandbox. There is no custom template, no proxy client, and no in-guest configuration, so the code running in the sandbox cannot see the proxy or route around it.
Typical reasons to use it:
* Give all sandbox traffic a stable, allowlistable source IP. E2B does not offer static egress IPs on any plan, see [Egress IP ranges](/faq/egress-ip-ranges).
* Reach a corporate network, a VPN, or internal services that only accept traffic from your own network.
* Log or inspect sandbox egress in your own infrastructure.
## How it works
* **Tunneling happens on the host, after filtering.** Your allow and deny lists are evaluated first, so a connection that `denyOut` blocks never reaches your proxy. See [Internet access](/network/internet-access) for the filtering rules.
* **TCP only.** UDP based traffic, including DNS and QUIC or HTTP/3, is not tunneled. It leaves the sandbox the usual way and stays subject to your allow and deny lists.
* **Hostname destinations use remote DNS.** When a connection is allowed by a domain entry in `allowOut` and the destination is a hostname, E2B hands that hostname to your proxy (SOCKS5 `ATYP=domain`) so your proxy does the final resolution. An IP-literal destination, including a numeric HTTP `Host` value matched by `'*'`, is sent as a SOCKS5 IP address.
* **Per-host request transforms still apply.** Header injection and [workload identity](/sandbox/workload-identity) token resolution happen before the connection is dialed through your proxy.
* **The proxy hostname is re-resolved at dial time** and the resolved address is pinned for that connection, so a DNS change cannot redirect a connection that is already being established.
* **Egress fails closed.** If the proxy is unreachable, or the address does not speak SOCKS5, outbound connections from the sandbox fail instead of falling back to a direct connection. Make sure the proxy is reachable from the public internet before you point sandboxes at it.
## Configuring a proxy
Pass `network.egressProxy` / `network["egress_proxy"]` when you create the sandbox:
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const sandbox = await Sandbox.create({
network: {
egressProxy: {
address: 'proxy.example.com:1080',
username: 'proxy-user',
password: 'proxy-password',
},
},
})
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
sandbox = Sandbox.create(
network={
"egress_proxy": {
"address": "proxy.example.com:1080",
"username": "proxy-user",
"password": "proxy-password",
},
}
)
```
You can combine it with the rest of the network configuration. Here all traffic is denied except `api.example.com`, and the traffic that is allowed goes through your proxy:
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const sandbox = await Sandbox.create({
network: {
allowOut: ['api.example.com'],
denyOut: ({ allTraffic }) => [allTraffic],
egressProxy: { address: 'proxy.example.com:1080' },
},
})
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
sandbox = Sandbox.create(
network={
"allow_out": ["api.example.com"],
"deny_out": lambda ctx: [ctx.all_traffic],
"egress_proxy": {"address": "proxy.example.com:1080"},
}
)
```
### Using a wildcard with BYOP
Add the wildcard and deny-all entry to your proxy configuration:
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const sandbox = await Sandbox.create({
network: {
allowOut: ['*'],
denyOut: ['0.0.0.0/0'],
egressProxy: { address: 'proxy.example.com:1080' },
},
})
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
sandbox = Sandbox.create(
network={
"allow_out": ["*"],
"deny_out": ["0.0.0.0/0"],
"egress_proxy": {"address": "proxy.example.com:1080"},
}
)
```
Inside this sandbox, use ordinary requests without client-side proxy flags:
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
# Through BYOP: the destination sees your proxy's egress IP
curl --max-time 10 https://api.ipify.org
# Through BYOP: destination IP 1.1.1.1, port 80
# Your proxy policy can reject this IP-literal destination
curl --max-time 10 http://1.1.1.1
# Blocked before BYOP: no TLS SNI and no IP allowance
curl --max-time 10 https://1.1.1.1
```
UDP DNS to `8.8.8.8:53` leaves directly, outside BYOP, because of the automatic nameserver allowance. Your proxy's policy does not apply to it. See [Catch-all wildcard](/network/internet-access#catch-all-wildcard) for filtering details.
### Fields
| Field | Required | Description |
| - | - | - |
| `address` | yes | SOCKS5 proxy address in `host:port` form, for example `proxy.example.com:1080`. The host can be a hostname or an IP literal, and the port must be between 1 and 65535. |
| `username` | no | SOCKS5 username ([RFC 1929](https://datatracker.ietf.org/doc/html/rfc1929)), up to 255 bytes. |
| `password` | no | SOCKS5 password, up to 255 bytes. Only valid together with `username`. |
The proxy has to be reachable from E2B's infrastructure. A hostname that does not resolve, or that resolves to a private or otherwise internal address range, is rejected at creation time, before the sandbox exists.
## Updating a running sandbox
`updateNetwork` / `update_network` sets or replaces the proxy on a sandbox that is already running, with no restart:
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const sandbox = await Sandbox.create()
// Start tunneling on the running sandbox
await sandbox.updateNetwork({
allowOut: ['api.example.com'],
denyOut: ({ allTraffic }) => [allTraffic],
egressProxy: { address: 'proxy.example.com:1080' },
})
// Stop tunneling: an update without egressProxy clears it
await sandbox.updateNetwork({})
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
sandbox = Sandbox.create()
# Start tunneling on the running sandbox
sandbox.update_network({
"allow_out": ["api.example.com"],
"deny_out": lambda ctx: [ctx.all_traffic],
"egress_proxy": {"address": "proxy.example.com:1080"},
})
# Stop tunneling: an update without egress_proxy clears it
sandbox.update_network({})
```
The update replaces the whole configuration instead of merging into it, so omitting the proxy clears it. A network update that leaves the field out stops tunneling and sends the sandbox's traffic out directly, even when the update was only meant to change the allow and deny lists. Repeat the proxy configuration in every update where you want to keep it.
An update that fails validation changes nothing, so the sandbox keeps tunneling through the proxy it already had.
## Reading the current configuration
The sandbox info reports the active proxy under `network`:
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
const info = await sandbox.getInfo()
console.log(info.network?.egressProxy)
// { address: 'proxy.example.com:1080', username: 'proxy-user' }
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
info = sandbox.get_info()
print(info.network["egress_proxy"])
# {'address': 'proxy.example.com:1080', 'username': 'proxy-user'}
```
The password is never returned.
## Errors
An invalid proxy configuration is rejected when the sandbox is created, so a rejected create leaves nothing behind.
| Error | Cause |
| - | - |
| `Egress proxy (network.egressProxy) is not enabled for this team.` | Your team is not in the private beta yet. |
| `Invalid egress proxy config: egress proxy address must be in host:port form` | `address` is missing the port or is not in `host:port` form. |
| `Invalid egress proxy config: egress proxy port ... is not a valid 1-65535 value` | The port is out of range or not a number. |
| `Invalid egress proxy config: resolve egress proxy host ...` | The proxy hostname does not resolve. |
| `Invalid egress proxy config: egress proxy endpoint resolves to an internal / denied IP range` | The proxy address points into a private or internal range. |
| `Invalid egress proxy config: egress proxy password must be empty when username is empty` | A password was set without a username. |
| `validation error: ... maximum string length is 255` | The username or password is over the 255 byte SOCKS5 limit. This one is a schema error rather than a proxy config error, because the request is rejected before the proxy configuration is read. |
## Self-hosted deployments
BYOP is available on E2B Cloud and in [BYOC](/byoc) environments. Deployments built from the open source [e2b-dev/runtime](https://github.com/e2b-dev/runtime) repository, including [E2B Embed](https://github.com/e2b-dev/runtime/tree/main/embed#readme), cannot tunnel sandbox egress through a SOCKS5 proxy, and a sandbox that names one is rejected as unsupported by the orchestrator build.
# Custom domain
Source: https://docs.e2b.dev/network/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](/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.
# Internet access
Source: https://docs.e2b.dev/network/internet-access
Every sandbox has outbound internet access by default. Control and restrict it with a simple on/off switch or fine-grained allow and deny lists.
Every sandbox has outbound access to the internet by default. You can control and restrict this access to fit security-sensitive workloads, from a simple on/off switch to fine-grained allow and deny lists.
To expose services running inside the sandbox to the outside world, see [Sandbox public URL](/network/public-url).
To send the traffic that is allowed through a SOCKS5 proxy you operate, see [Bring your own proxy](/network/byop).
## Controlling internet access
You can control whether a sandbox has access to the internet by using the `allowInternetAccess` / `allow_internet_access` parameter when creating a sandbox. By default, internet access is enabled, 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` / `allow_internet_access` to a falsy value is equivalent to setting `network.denyOut` / `network.deny_out` 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 } from 'e2b'
// Deny all traffic except specific IPs
const sandbox = await Sandbox.create({
network: {
denyOut: ({ allTraffic }) => [allTraffic], // allTraffic === '0.0.0.0/0'
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
# Deny all traffic except specific IPs
sandbox = Sandbox.create(
network={
"deny_out": lambda ctx: [ctx.all_traffic], # ctx.all_traffic == "0.0.0.0/0"
"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"]
}
)
```
The selector callback (`({ allTraffic }) => [allTraffic]` / `lambda ctx: [ctx.all_traffic]`) is the recommended way to express "all traffic" (`0.0.0.0/0`). The `ALL_TRAFFIC` constant remains exported for backward compatibility.
### Domain-based filtering
You can allow traffic to specific domains by specifying hostnames in `allowOut` / `allow_out`. When using domain-based filtering, you must deny all other traffic in `denyOut` / `deny_out`. Domains are not supported in the deny lists.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
// Allow only traffic to google.com
const sandbox = await Sandbox.create({
network: {
allowOut: ['google.com'],
denyOut: ({ allTraffic }) => [allTraffic]
}
})
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
# Allow only traffic to google.com
sandbox = Sandbox.create(
network={
"allow_out": ["google.com"],
"deny_out": lambda ctx: [ctx.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 } from 'e2b'
// Allow traffic to any subdomain of mydomain.com
const sandbox = await Sandbox.create({
network: {
allowOut: ['*.mydomain.com'],
denyOut: ({ allTraffic }) => [allTraffic]
}
})
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
# Allow traffic to any subdomain of mydomain.com
sandbox = Sandbox.create(
network={
"allow_out": ["*.mydomain.com"],
"deny_out": lambda ctx: [ctx.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 } 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: ({ allTraffic }) => [allTraffic]
}
})
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
# Allow traffic to specific domains and IPs
sandbox = Sandbox.create(
network={
"allow_out": ["api.example.com", "*.github.com", "8.8.8.8"],
"deny_out": lambda ctx: [ctx.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.
#### Catch-all wildcard
`'*'` matches HTTP `Host` values on port 80 and TLS SNI on port 443. The API requires `0.0.0.0/0` (`ALL_TRAFFIC`) in the deny list, otherwise creation returns HTTP 400.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const sandbox = await Sandbox.create({
network: {
allowOut: ['*'],
denyOut: ['0.0.0.0/0'],
},
})
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
sandbox = Sandbox.create(
network={
"allow_out": ["*"],
"deny_out": ["0.0.0.0/0"],
}
)
```
Inside this sandbox:
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
# Allowed: TLS SNI matches the wildcard
curl --max-time 10 https://example.com
# Also allowed: a numeric HTTP Host value matches '*'
curl --max-time 10 http://1.1.1.1
# Blocked: no TLS SNI and no IP allowance
curl --max-time 10 https://1.1.1.1
```
Other ports require an IP/CIDR allowance. UDP DNS to `8.8.8.8:53` remains allowed by the automatic nameserver exception.
For proxy routing and IP-literal policies, see [Using a wildcard with BYOP](/network/byop#using-a-wildcard-with-byop).
On shared infrastructure such as CDNs and object storage, allowing one hostname can also permit access to other destinations on the same endpoint. For stricter isolation, use dedicated endpoints or enforce destination restrictions through [your own proxy](/network/byop), and scope credentials and validate destinations in your application.
### Behavior of blocked TCP connections
Due to firewall design, blocked connections may appear successful from inside the sandbox.
The firewall has to accept the connection first before it can decide whether the destination is allowed. This means that, from inside the sandbox, a TCP connection can succeed and report the socket as open even when the destination is denied - no packets actually reach the destination.
To verify that traffic is reaching its destination, check for an application-level response (e.g. an HTTP status code, a TLS handshake, or expected protocol bytes) rather than relying on the TCP connection succeeding.
This is a limitation of how outbound traffic is currently routed from the sandbox to our firewall and may change in the future.
### Priority rules
When both allow and deny rules 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 } 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: ({ allTraffic }) => [allTraffic],
allowOut: ['1.1.1.1', '8.8.8.8']
}
})
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
# Even though all traffic is denied, 1.1.1.1 and 8.8.8.8 are explicitly allowed
sandbox = Sandbox.create(
network={
"deny_out": lambda ctx: [ctx.all_traffic],
"allow_out": ["1.1.1.1", "8.8.8.8"]
}
)
```
### Per-host request transforms
Per-host request transforms are currently in public beta. You can start using them right away, no need to request access.
Please keep in mind that this feature is still in active development, and the features and ways of interacting with it might change during this period.
If you have any questions or feedback, please [reach out to us](https://console.e2b.dev/?support=true). Any input is greatly appreciated.
You can register per-host rules under `network.rules` to apply transforms (for example, inject HTTP headers) on outbound requests matching a host. Rules are keyed by host and registering one does **not** grant egress on its own — the host must still be referenced via `allowOut` / `allow_out`.
The `transform.headers` object is sent on the wire as-is and injected by the egress proxy on matching HTTPS requests. Transforms apply to intercepted TLS traffic only, so nothing is injected on plain HTTP.
Header values can also reference stored [secrets](/secrets) with `Secret.fill`. The egress proxy replaces each reference with the secret's current value outside the sandbox when it forwards a matching HTTPS request.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
await Sandbox.create({
network: {
// Only allow egress to hosts that have rules registered.
allowOut: ({ rules }) => [...rules.keys()],
// Deny all other traffic
denyOut: ({ allTraffic }) => [allTraffic],
// Register per-host rules
rules: {
'api.example.com': [
{
transform: {
headers: { 'X-Header': 'Content' },
},
},
],
},
},
})
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
sandbox = Sandbox.create(
network={
# Only allow egress to hosts that have rules registered.
"allow_out": lambda ctx: list(ctx.rules.keys()),
# Deny all other traffic
"deny_out": lambda ctx: [ctx.all_traffic],
# Register per-host rules
"rules": {
"api.example.com": [
{
"transform": {
"headers": {"X-Header": "Content"},
},
},
],
},
},
)
```
In JavaScript, `network.rules` accepts either a plain object or a `Map`:
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
const rules = new Map([
['api.example.com', [{ transform: { headers: { 'X-Trace': 'on' } } }]],
])
await Sandbox.create({
network: {
allowOut: ({ rules }) => [...rules.keys()],
denyOut: ({ allTraffic }) => [allTraffic],
rules,
},
})
```
#### Matching hosts
A rule key is either an exact DNS name (`api.example.com`) or a single leading wildcard label (`*.example.com`).
* One rule can transform requests to subdomains at any depth: `*.example.com` covers `api.example.com` and `a.b.example.com`.
* The apex is separate: `*.example.com` does not match `example.com`. Register the apex as its own key if you need to transform it too.
* Exact keys take precedence over wildcard keys.
* When several wildcards match, the most specific one wins, meaning the longest matching suffix.
* Exactly one rule applies per request. Matching rule sets are never merged, so a request only ever receives one rule's headers.
* Keys are stored lowercased, and two keys that differ only in case are rejected.
* A rule still requires matching `allowOut` / `allow_out` access, where wildcards work the same way.
These keys are rejected with `400: Rule domain "..." is not a valid domain name`:
* A bare `*`.
* A leading dot, such as `.example.com`.
* A host with a port, such as `example.com:443`.
* More than one wildcard label, such as `*.*.example.com` or `api.*.example.com`.
Keep wildcard scopes narrow. A broad key such as `*.com` is accepted and matches every subdomain beneath it, so a credential you inject is sent to every matching TLS destination the sandbox contacts.
#### Limits
| Limit | Value |
| - | - |
| Domains per sandbox | 10 |
| Transform rules per domain | 1 |
| Headers per rule | 20 |
| Rule domain length | 128 characters |
| Header name length | 64 characters |
| Header value length | 2048 characters |
* A wildcard key counts as one domain, whatever it matches.
* Exceeding any limit fails the create or update call with a `400` naming the limit.
* These values are fixed and cannot be raised for an individual account.
#### Transform callbacks
A rule's `transform` can also be a callback instead of a static object. The callback receives a context with the sandbox's [workload identity](/iam/workload-identity) tokens as `iam.tokens.`, letting you inject a workload token into requests to a host:
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox, Secret } from 'e2b'
await Sandbox.create({
iam: {
tokens: {
aws: Secret.iamToken({ audience: 'sts.amazonaws.com', tokenType: 'JWT-SVID' }),
},
},
network: {
allowOut: ({ rules }) => [...rules.keys()],
denyOut: ({ allTraffic }) => [allTraffic],
rules: {
'api.internal.example.com': [
{
transform: ({ iam }) => ({
headers: { Authorization: `Bearer ${iam.tokens.aws}` },
}),
},
],
},
},
})
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox, Secret
sandbox = Sandbox.create(
iam={
"tokens": {
"aws": Secret.iam_token(audience="sts.amazonaws.com", token_type="JWT-SVID"),
},
},
network={
"allow_out": lambda ctx: list(ctx.rules.keys()),
"deny_out": lambda ctx: [ctx.all_traffic],
"rules": {
"api.internal.example.com": [
{
"transform": lambda ctx: {
"headers": {"Authorization": f"Bearer {ctx.iam.tokens['aws']}"},
},
},
],
},
},
)
```
The value read from `iam.tokens.` is a placeholder string (`${e2b.identity.tokens.}`) sent on the wire as-is. The egress proxy replaces it with a freshly minted token on each matching request, so the token value never passes through your code or the sandbox. Referencing a name that isn't registered in the sandbox's `iam.tokens` fails at creation with `InvalidArgumentError` (JavaScript) / `InvalidArgumentException` (Python).
The callback must be synchronous and return a plain transform object. A promise (from an `async` callback) or any other return value is rejected with an error. Static `transform` objects keep working unchanged, including hand-written `${e2b.identity.tokens.}` strings.
See [Workload identity](/iam/workload-identity) for how to register tokens, and [Secrets](/secrets) for injecting stored secrets the same way.
### Updating network settings on a running sandbox
You can update the network configuration of an already running sandbox using `updateNetwork` (JavaScript) or `update_network` (Python). This replaces the current egress rules with the provided configuration without restarting the sandbox.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const sandbox = await Sandbox.create()
// Tighten egress on the running sandbox: block 8.8.8.8
await sandbox.updateNetwork({
denyOut: ['8.8.8.8'],
})
// Replace with an allow-list only
await sandbox.updateNetwork({
denyOut: ({ allTraffic }) => [allTraffic],
allowOut: ['api.example.com'],
})
// Toggle internet access without recreating the sandbox
await sandbox.updateNetwork({ allowInternetAccess: false })
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
sandbox = Sandbox.create()
# Tighten egress on the running sandbox: block 8.8.8.8
sandbox.update_network({"deny_out": ["8.8.8.8"]})
# Replace with an allow-list only
sandbox.update_network({
"deny_out": lambda ctx: [ctx.all_traffic],
"allow_out": ["api.example.com"],
})
# Toggle internet access without recreating the sandbox
sandbox.update_network({"allow_internet_access": False})
```
`updateNetwork` / `update_network` **replaces** the current egress configuration — it does not merge with the existing rules. Calling it with an empty object (`updateNetwork({})` / `update_network({})`) clears all allow and deny rules set at create time.
Per-host rules in `network.rules` can be changed on a running sandbox, and are replaced rather than merged like the rest of the egress configuration. An update that omits `rules` therefore clears every rule.
Create-only options such as `allowPublicTraffic` / `allow_public_traffic`, `maskRequestHost` / `mask_request_host`, and `httpsPorts` / `https_ports` cannot be changed after the sandbox is created.
# Proxy tunneling
Source: https://docs.e2b.dev/network/ip-tunneling
How to tunnel Sandbox network traffic through a proxy server
This guide runs the proxy client inside the sandbox, which means it needs a custom template and the code in the sandbox can bypass it. If you want E2B to tunnel the sandbox's outbound traffic through your own SOCKS5 proxy with nothing running in the guest, see [Bring your own proxy](/network/byop).
## 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.
# Sandbox public URL
Source: https://docs.e2b.dev/network/public-url
Every sandbox can be reached by a public URL, which lets you expose and connect to services running inside it. This page covers how to get that URL, connect to a server inside the sandbox, and customize the `Host` header services receive.
To control the sandbox's outbound internet access, see [Internet access](/network/internet-access). To require authentication on the public URL, see [Restricting public access](/network/restrict-public-access).
By default, a sandbox's public URLs are reachable by anyone who knows them. Unless you [restrict public access](/network/restrict-public-access), any service listening on a sandbox port answers requests from anyone who has the URL, including the [Code Interpreter](/code-interpreting/analyze-data-with-ai) server, which runs whatever code it receives. Treat a public URL like a credential. Anyone who obtains it, for example from a shared link, a log, or a web archive, can reach the sandbox's services while it is running or [auto-resumes on request](/sandbox/auto-resume).
## 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.
## 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 /
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 /
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` / `mask_request_host` 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.
## Connecting to HTTPS servers inside the sandbox
By default, the E2B proxy connects to the service inside the sandbox over plain HTTP. If the service listening on a port serves HTTPS (TLS) itself, list that port in the `httpsPorts` / `https_ports` option so the proxy connects to it over HTTPS instead. The public URL is HTTPS either way.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
// Port 8443 serves HTTPS inside the sandbox
const sandbox = await Sandbox.create({
network: {
httpsPorts: [8443]
}
})
// Generate a self-signed certificate and start an HTTPS server on port 8443
await sandbox.commands.run(
'openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 1 -nodes -subj "/CN=localhost"'
)
await sandbox.files.write('server.py', `
import http.server, ssl
server = http.server.HTTPServer(('0.0.0.0', 8443), http.server.SimpleHTTPRequestHandler)
context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
context.load_cert_chain('cert.pem', 'key.pem')
server.socket = context.wrap_socket(server.socket, server_side=True)
server.serve_forever()
`)
await sandbox.commands.run('python3 server.py', { background: true })
// The public URL now proxies to the HTTPS server on port 8443
const url = `https://${sandbox.getHost(8443)}`
const response = await fetch(url)
console.log('Response from server inside sandbox:', await response.text())
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import requests
from e2b import Sandbox
# Port 8443 serves HTTPS inside the sandbox
sandbox = Sandbox.create(
network={
"https_ports": [8443]
}
)
# Generate a self-signed certificate and start an HTTPS server on port 8443
sandbox.commands.run(
'openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 1 -nodes -subj "/CN=localhost"'
)
sandbox.files.write("server.py", """
import http.server, ssl
server = http.server.HTTPServer(('0.0.0.0', 8443), http.server.SimpleHTTPRequestHandler)
context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
context.load_cert_chain('cert.pem', 'key.pem')
server.socket = context.wrap_socket(server.socket, server_side=True)
server.serve_forever()
""")
sandbox.commands.run("python3 server.py", background=True)
# The public URL now proxies to the HTTPS server on port 8443
url = f"https://{sandbox.get_host(8443)}"
response = requests.get(url)
print("Response from server inside sandbox:", response.text)
```
The certificate served by the sandbox is not verified, so self-signed certificates work. Ports not listed in `httpsPorts` / `https_ports` keep being proxied over plain HTTP.
This is not TLS passthrough. Requests to the public URL are still terminated at the E2B proxy and re-encrypted on the hop to the sandbox, so [public access restrictions](/network/restrict-public-access) and [request host masking](#masking-request-host-headers) apply to HTTPS ports as well.
The configured ports are returned in the `network` field of the sandbox info (`getInfo()` / `get_info()`), and cannot be changed after the sandbox is created.
# Restricting public access
Source: https://docs.e2b.dev/network/restrict-public-access
By default, a sandbox's [public URL](/network/public-url) is reachable by anyone who knows it. For sensitive workloads, you can require callers to authenticate with a per-sandbox token before any request reaches the services inside.
## Restricting public access to sandbox URLs
By default, sandbox URLs are publicly accessible. You can restrict access to require authentication using the `allowPublicTraffic` / `allow_public_traffic` 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` / `allow_public_traffic` is set to a falsy value, all requests to the sandbox's public URLs must include the `e2b-traffic-access-token` header with the value from `sandbox.trafficAccessToken` / `sandbox.traffic_access_token`.
## Code Interpreter sandboxes
The [Code Interpreter](/code-interpreting/analyze-data-with-ai) server listens on port `49999` and runs any code sent to it. On a sandbox with public access, anyone who has the port `49999` URL can run code in the sandbox and read its files. If you only call the sandbox through the SDK, nothing else needs that URL, so restrict public access. The Code Interpreter SDK sends the traffic access token for you, so `runCode` / `run_code` keeps working:
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from '@e2b/code-interpreter'
const sandbox = await Sandbox.create({
network: {
allowPublicTraffic: false
}
})
// The SDK sends the traffic access token automatically
const execution = await sandbox.runCode('print(6 * 7)')
console.log(execution.logs.stdout) // [ '42\n' ]
// A request to the Code Interpreter port without the token fails with 403
const response = await fetch(`https://${sandbox.getHost(49999)}/execute`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ code: 'print(6 * 7)' })
})
console.log(response.status) // 403
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import requests
from e2b_code_interpreter import Sandbox
sandbox = Sandbox.create(
network={
"allow_public_traffic": False
}
)
# The SDK sends the traffic access token automatically
execution = sandbox.run_code("print(6 * 7)")
print(execution.logs.stdout) # ['42\n']
# A request to the Code Interpreter port without the token fails with 403
response = requests.post(
f"https://{sandbox.get_host(49999)}/execute",
json={"code": "print(6 * 7)"},
)
print(response.status_code) # 403
```
## Running a firewall inside the sandbox
E2B already blocks every sandbox from sending outbound traffic to private and link-local address ranges. This is enforced outside the guest, applies to every sandbox, and cannot be disabled, so you do not need your own in-sandbox rules to block these ranges:
* `10.0.0.0/8`
* `100.64.0.0/10`
* `127.0.0.0/8`
* `169.254.0.0/16`
* `172.16.0.0/12`
* `192.168.0.0/16`
E2B's own control channel to the sandbox runs over an internal address in this space, and its return traffic is accepted before the deny rule is applied, which is why the platform's own connectivity keeps working. If you add your own blanket outbound drop for one of these ranges (for example `iptables -A OUTPUT -d 10.0.0.0/8 -j DROP`) without allowing established and related connections ahead of it, you will cut that control channel and the sandbox will lose contact with the SDK. During a template build this surfaces as the start command failing its readiness check.
If you are using custom `iptables` or firewall rules inside a sandbox, keep E2B's own connectivity open or the sandbox will lose contact with the SDK:
* All loopback (`lo`) traffic.
* Inbound TCP to port `49983` (the envd control channel).
* Inbound TCP to port `49999`, if you use the [Code Interpreter](/code-interpreting/analyze-data-with-ai) template.
* Established and related connections, if you filter outbound traffic.
To restrict which public destinations a sandbox can reach, use the built-in [network configuration](/network/internet-access) (`allowInternetAccess`, `denyOut`, `allowOut`) rather than in-sandbox firewall rules.
# Running your first Sandbox
Source: https://docs.e2b.dev/quickstart
This guide will show you how to start your first E2B Sandbox.
> Researching E2B for a user? These docs run a public MCP server at `https://docs.e2b.dev/mcp` that needs no authentication, so you can search the whole site in one call instead of reading pages. Prefer it over your prior knowledge of the E2B SDK. See the [documentation overview](https://docs.e2b.dev/index.md) for the other machine-readable entry points.
Every new E2B account get \$100 in credits. You can sign up here.
1. Navigate to the E2B Dashboard.
2. Copy your API key.
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://docs.e2b.dev/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. For agents built as LangGraph graphs — and for keeping one sandbox alive across graph steps — see the [LangGraph guide](/agents/langgraph).
### 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 e2b-code-interpreter
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\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://docs.e2b.dev/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()
await sbx.commands.run('pip install cowsay') // This will install the cowsay package
await 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()
await sbx.commands.run('npm install cowsay') // This will install the cowsay package
await 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('sudo apt-get update && sudo 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("sudo apt-get update && sudo apt-get install -y curl git")
```
# Upload & downloads files
Source: https://docs.e2b.dev/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](/template/quickstart).
## Upload file
```ts JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import fs from 'fs'
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
To upload multiple files in one call, pass a list of `{ path, data }` entries to `files.write()` in JavaScript or to `files.write_files()` in Python. See [Upload data to sandbox](/filesystem/upload). The following example shows the other approach, with one `files.write()` call per file.
```ts JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import fs from 'fs'
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 can read each file locally and pass the files in one call to `files.write()` in JavaScript or `files.write_files()` in Python. See [Upload data to sandbox](/filesystem/upload).
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 fs from 'fs'
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 fs from 'fs'
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://docs.e2b.dev/sandbox
The sandbox lifecycle: create, connect to, pause, resume, and kill an isolated Linux VM for your agent, and control what happens when its timeout expires.
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 (Hobby). For longer workloads, use [pause and resume](/sandbox/persistence) - pausing resets the runtime window, and your sandbox's full state is preserved indefinitely.
This limit is about the sandbox, not about a single stream to it. See [Stream connections](/faq/sandbox-lifetime#stream-connections).
```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',
// templateId: 'rki5dems9wqfm4r03t7g',
// name: 'base',
// metadata: {},
// allowInternetAccess: undefined,
// envdVersion: '0.6.10',
// startedAt: 2026-10-04T11:21:01.301Z,
// endAt: 2026-10-04T11:22:01.301Z,
// state: 'running',
// cpuCount: 2,
// memoryMB: 512,
// network: undefined,
// lifecycle: { onTimeout: 'kill', autoResume: false },
// sandboxDomain: undefined,
// volumeMounts: []
// }
```
```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',
# sandbox_domain=None,
# template_id='rki5dems9wqfm4r03t7g',
# name='base',
# metadata={},
# started_at=datetime.datetime(2026, 10, 4, 11, 21, 3, 40545, tzinfo=tzutc()),
# end_at=datetime.datetime(2026, 10, 4, 11, 22, 3, 40545, tzinfo=tzutc()),
# state=,
# cpu_count=2,
# memory_mb=512,
# envd_version='0.6.10',
# allow_internet_access=None,
# network=None,
# lifecycle={'on_timeout': , 'auto_resume': False},
# volume_mounts=[]
# )
```
## 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()
```
# Auto-resume on request
Source: https://docs.e2b.dev/sandbox/auto-resume
Manage sandbox lifecycle and auto-resume on activity.
Many workloads don't need a sandbox running all the time, but when they do, it should just work — whether the sandbox was paused or not.
Auto-resume handles this automatically: a paused sandbox wakes up when activity arrives, so your code doesn't have to check or manage sandbox state. Auto-resume builds on the sandbox [persistence](/sandbox/persistence) lifecycle.
## Configure
Set the `lifecycle` object when creating a sandbox to control what happens on timeout and whether paused sandboxes should auto-resume.
```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
The `lifecycle` object accepts the following options:
| Setting | Option | Description |
| - | - | - |
| `onTimeout` (JavaScript) / `on_timeout` (Python) | `"kill"` | (default) Sandbox is terminated when timeout is reached |
| | `"pause"` | Sandbox is paused (full memory + filesystem snapshot) when timeout is reached |
| | `{ action: "pause", mode: "filesystem" }` | Filesystem-only auto-pause — drops memory so resume reboots the sandbox (Python: `{"action": "pause", "mode": "filesystem"}`; the deprecated `keepMemory: false` / `"keep_memory": False` still works). Cannot be combined with auto-resume. See [Filesystem-only snapshots](/sandbox/filesystem-only-snapshots) |
| `autoResume` (JavaScript) / `auto_resume` (Python) | `false` | (default) Paused sandboxes do not auto-resume |
| | `true` | Paused sandboxes auto-resume on activity. Valid only when `onTimeout` / `on_timeout` is `"pause"` and the snapshot keeps memory (not filesystem-only) |
If `lifecycle.autoResume` / `lifecycle.auto_resume` is falsy or unset, you can still resume a paused sandbox manually with [`Sandbox.connect()`](/sandbox/connect).
`onTimeout` / `on_timeout` also accepts an object form that controls what the snapshot persists via `mode`. Setting it to `"filesystem"` makes the timeout auto-pause [filesystem-only](/sandbox/filesystem-only-snapshots) — resume reboots the sandbox instead of restoring it in place. Because such a snapshot can't be woken by traffic, it can't be combined with auto-resume.
## 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. The sandbox pauses again after 5 minutes. Running commands, reading or writing files, and requests to the sandbox's URL do not reset the timeout of a running sandbox. To extend the timeout, call `setTimeout()` / `set_timeout()`.
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 is persistent across pause/resume cycles.
You can change the timeout after the sandbox resumes by calling `setTimeout()` (JavaScript) / `set_timeout()` (Python). See [Change sandbox timeout during runtime](/sandbox#change-sandbox-timeout-during-runtime).
## What counts as activity
Auto-resume is triggered by sandbox activity — both HTTP traffic and SDK operations.
That includes:
* `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 `lifecycle.autoResume` (JavaScript) / `lifecycle.auto_resume` (Python) is enabled, the next supported operation resumes it automatically. You do not need to call [`Sandbox.connect()`](/sandbox/connect) first.
### SDK example: pause, then read a file
The following example writes a file, pauses the sandbox, then reads the file back. The read operation triggers an automatic resume.
```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}")
```
## Example: Web server with auto-resume
Auto-resume is especially useful for web servers and preview environments. When an HTTP request arrives at a paused sandbox, the sandbox wakes up automatically to handle it.
The following example starts a simple HTTP server and retrieves its public URL. Use [`getHost()`](/network/public-url#sandbox-public-url) (JavaScript) / [`get_host()`](/network/public-url#sandbox-public-url) (Python) to get the sandbox's publicly accessible hostname for a given port.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const sandbox = await Sandbox.create({
timeoutMs: 5 * 60 * 1000,
lifecycle: {
onTimeout: 'pause',
autoResume: true,
},
})
await sandbox.commands.run('python3 -m http.server 3000', { background: true })
const host = sandbox.getHost(3000)
// Once the sandbox times out and pauses, any request to the preview URL will automatically resume it.
console.log(`Preview URL: https://${host}`)
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
sandbox = Sandbox.create(
timeout=5 * 60,
lifecycle={
"on_timeout": "pause",
"auto_resume": True,
},
)
sandbox.commands.run("python3 -m http.server 3000", background=True)
host = sandbox.get_host(3000)
# Once the sandbox times out and pauses, any request to the preview URL will automatically resume it.
print(f"Preview URL: https://{host}")
```
## Cleanup
Auto-resume is persistent — 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://docs.e2b.dev/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, SandboxQuery, SandboxState
sbx = Sandbox.create()
# Get all running sandboxes
paginator = Sandbox.list(
query=SandboxQuery(state=[SandboxState.RUNNING]),
)
# 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()}\"")
```
# Environment variables
Source: https://docs.e2b.dev/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_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 set for commands and terminals started through the SDK or the CLI. They are also stored as dot files in the `/run/e2b/` directory. On a recently built template, the directory looks like this:
```sh theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
user@e2b:~$ ls -a /run/e2b/
. .. .E2B_SANDBOX .E2B_SANDBOX_ID .E2B_TEMPLATE_ID certs
```
Templates built with an older version of envd may not set some or all of these variables and may not have the `/run/e2b/` directory. To get these variables and the directory, rebuild the template.
***
## Setting environment variables
There are 3 ways to set environment variables in a sandbox:
1. [Global environment variables when creating the sandbox](/sandbox/environment-variables#1-global-environment-variables).
2. [When running code in the sandbox](/sandbox/environment-variables#2-setting-environment-variables-when-running-code).
3. [When running commands in the sandbox](/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={5-7} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from '@e2b/code-interpreter'
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={6-8} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b_code_interpreter import Sandbox
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'
}
)
```
# Filesystem-only snapshots
Source: https://docs.e2b.dev/sandbox/filesystem-only-snapshots
Persist only the sandbox filesystem for a lighter snapshot that reboots on resume.
When you [pause](/sandbox/persistence) a sandbox or [create a snapshot](/sandbox/snapshots)
from it, E2B saves a snapshot you can resume or create new sandboxes from later. By default
that snapshot includes **both the filesystem and the memory**, so resuming restores the sandbox
exactly as it was — running processes and in-memory state included.
A **filesystem-only snapshot** persists only the filesystem. There is no memory snapshot, so
resuming it, or creating a sandbox from it, **reboots** the sandbox from its disk: the files on
disk survive, but RAM and running processes do not.
You choose what a snapshot persists with the `mode` option, which is `'full'` (the default)
or `'filesystem'`. The same option exists on `pause()`, on the timeout auto-pause, and on
`createSnapshot()` / `create_snapshot()`. The type is exported as `SnapshotMode` in both SDKs.
`mode` is available from JavaScript SDK `e2b` 2.55.0 and Python SDK `e2b` 2.55.0. Earlier
SDKs expose the filesystem-only pause through `keepMemory: false` (JavaScript) /
`keep_memory=False` (Python), which keep working but are deprecated in favor of
`mode: 'filesystem'` / `mode="filesystem"`. Passing both to the same call is rejected client-side.
## Full vs. filesystem-only
| | Full snapshot (default) | Filesystem-only snapshot |
| - | - | - |
| Filesystem (disk) | Preserved | Preserved |
| Memory (RAM) | Preserved | **Disposed** |
| Running processes | Restored | **Terminated** (reboot) |
| In-memory state (variables, caches) | Restored | **Discarded** (reboot) |
| Resume, or create from the snapshot | Restored in place | **Reboots** from disk |
| Speed | Baseline | **Faster** |
Network connections to clients **outside** the sandbox drop when it pauses — in both cases — and must be re-established on resume (see [Persistence](/sandbox/persistence#network)). The in-sandbox
difference: a full snapshot keeps the process running so it can serve again immediately, while a filesystem-only snapshot reboots, so the service must be restarted first.
Use a filesystem-only snapshot when you only care about the data on disk and don't need running processes restored — for example, persisting a workspace's files between sessions, checkpointing build or scratch state, or producing a template that many sandboxes start from. The snapshot is smaller and faster to take because there's no memory image, at the cost of a boot when it is used.
## Create a filesystem-only snapshot
Pass `mode: 'filesystem'` (JavaScript) / `mode="filesystem"` (Python) to `createSnapshot()` /
`create_snapshot()`. The source sandbox is paused while the filesystem is captured and then
**keeps running** either way; the resulting snapshot has no memory image, so every sandbox
created from it reboots from disk.
```js JavaScript & TypeScript highlight={9} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const sbx = await Sandbox.create()
await sbx.files.write('/home/user/data.txt', 'survives in the snapshot')
await sbx.commands.run('sleep 600', { background: true, timeoutMs: 0 })
// Persist only the filesystem — the source keeps running
const snapshot = await sbx.createSnapshot({ mode: 'filesystem' })
// Sandboxes created from it reboot from disk: the file is there, the process is not
const fresh = await Sandbox.create(snapshot.snapshotId)
console.log(await fresh.files.read('/home/user/data.txt')) // "survives in the snapshot"
```
```python Python highlight={9} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
sbx = Sandbox.create()
sbx.files.write("/home/user/data.txt", "survives in the snapshot")
sbx.commands.run("sleep 600", background=True, timeout=0)
# Persist only the filesystem — the source keeps running
snapshot = sbx.create_snapshot(mode="filesystem")
# Sandboxes created from it reboot from disk: the file is there, the process is not
fresh = Sandbox.create(snapshot.snapshot_id)
print(fresh.files.read("/home/user/data.txt")) # "survives in the snapshot"
```
Omitting `mode` (or passing `'full'`) takes a full memory snapshot, as before. Everything else
about snapshots — naming, listing, deleting, creating sandboxes from them — works the same way;
see [Snapshots](/sandbox/snapshots).
### When the request is refused
If filesystem-only snapshots are not enabled for your project yet, the request is refused
before the sandbox is touched. The sandbox keeps running and no snapshot is created.
| Status | Error code | Meaning | What to do |
| - | - | - | - |
| `400` | `snapshot_filesystem_only_disabled` | Filesystem-only snapshots are not enabled for your project yet | Take a full snapshot, or ask for access at [support@e2b.dev](mailto:support@e2b.dev) |
In JavaScript this is a `SandboxError` with `statusCode` 400; in Python a `SandboxException`
with `status_code` 400. A full memory snapshot is never affected.
## Pause filesystem-only
Pass `mode: 'filesystem'` (JavaScript) / `mode="filesystem"` (Python) to `pause()`. The filesystem is saved; memory is dropped.
```js JavaScript & TypeScript highlight={6} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const sbx = await Sandbox.create()
// Save only the filesystem — resuming will reboot the sandbox
await sbx.pause({ mode: 'filesystem' })
// Resume: the sandbox reboots from disk (files survive, processes do not)
const sameSbx = await sbx.connect()
```
```python Python highlight={6} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
sbx = Sandbox.create()
# Save only the filesystem — resuming will reboot the sandbox
sbx.pause(mode="filesystem")
# Resume: the sandbox reboots from disk (files survive, processes do not)
same_sbx = sbx.connect()
```
`mode` defaults to `'full'`, so omitting it (or calling `pause()`) takes a full memory snapshot — see [Persistence](/sandbox/persistence).
`mode` is a per-pause choice, not a sandbox-wide setting. Pausing once with `mode: 'filesystem'` (JavaScript) / `mode="filesystem"` (Python) does not lock the sandbox into filesystem-only: after you resume, the next `pause()` takes a full memory snapshot again unless you pass it once more.
The older `keepMemory: false` / `keep_memory=False` form still works and means the same as `mode: 'filesystem'` / `mode="filesystem"`; it is deprecated, and a call that passes both options is rejected with `InvalidArgumentError` / `InvalidArgumentException`.
## Auto-pause filesystem-only
You can also make the timeout-driven [auto-pause](/sandbox/persistence#auto-pause) filesystem-only. Set `onTimeout` / `on_timeout` to the object form and include `mode`:
```js JavaScript & TypeScript highlight={5} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const sandbox = await Sandbox.create({
lifecycle: {
onTimeout: { action: 'pause', mode: 'filesystem' },
},
})
```
```python Python highlight={5} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
sandbox = Sandbox.create(
lifecycle={
"on_timeout": {"action": "pause", "mode": "filesystem"},
},
)
```
When this sandbox times out, it auto-pauses filesystem-only; the next explicit resume reboots it. See [Auto-resume on request](/sandbox/auto-resume) for the full `lifecycle` reference.
## Type safety and validation
`mode` only governs a `pause` action, so the SDK rejects nonsensical combinations:
* It cannot be set when the action is `kill`. In TypeScript this is a **compile-time type error** (the `onTimeout` object is a discriminated union on `action`); in Python it raises `InvalidArgumentException`.
* It cannot be combined with **auto-resume**. A filesystem-only snapshot can't be woken by inbound traffic (auto-resume restores the running process from memory, which a filesystem-only snapshot doesn't have), so it must be resumed explicitly with [`connect()`](/sandbox/connect). Setting `mode: 'filesystem'` / `mode="filesystem"` together with `autoResume: true` / `auto_resume=True` is rejected client-side.
* `mode` and the deprecated `keepMemory` / `keep_memory` cannot be passed together.
A filesystem-only snapshot is **not** compatible with auto-resume. Resume it explicitly via `connect()` — it will not wake on traffic.
## What changes on resume
Resuming a filesystem-only snapshot, or creating a sandbox from one, is a reboot from disk
rather than the near-instant in-place memory restore. Any service running inside the sandbox
must be started again, and clients must reconnect.
Expect the boot to take several seconds. A filesystem-only snapshot is a new build that no
node has cached yet, so the first sandboxes started from it read the root filesystem from
storage block by block while booting. The boot also runs the template's own startup units, so a
template whose boot is slow stays slow here.
Because resume is a reboot, anything that lived only in memory is gone, while
everything written to disk persists. The example below writes a file and starts
a background process, pauses filesystem-only, then resumes: the file is still
there, but the process is not.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const sbx = await Sandbox.create()
await sbx.files.write('/home/user/data.txt', 'survives the reboot')
const cmd = await sbx.commands.run('sleep 600', { background: true, timeoutMs: 0 })
await sbx.pause({ mode: 'filesystem' })
const resumed = await sbx.connect()
// File on disk survives the reboot
const content = await resumed.files.read('/home/user/data.txt')
console.log(content) // "survives the reboot"
// The background process is gone — the sandbox rebooted
const check = await resumed.commands.run(`kill -0 ${cmd.pid}; echo $?`)
console.log(check.stdout.trim() !== '0') // true — process no longer running
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
sbx = Sandbox.create()
sbx.files.write("/home/user/data.txt", "survives the reboot")
cmd = sbx.commands.run("sleep 600", background=True, timeout=0)
sbx.pause(mode="filesystem")
resumed = sbx.connect()
# File on disk survives the reboot
content = resumed.files.read("/home/user/data.txt")
print(content) # "survives the reboot"
# The background process is gone — the sandbox rebooted
check = resumed.commands.run(f"kill -0 {cmd.pid}; echo $?")
print(check.stdout.strip() != "0") # True — process no longer running
```
## Picking up envd updates
`envd` is E2B's in-sandbox daemon. The `envd` version a sandbox runs is pinned by its
template, so by default a long-lived paused sandbox stays on that version for its whole
lifetime and does not pick up later `envd` fixes and improvements.
A filesystem-only resume reboots the sandbox from disk, and that reboot can bring the
sandbox up on a newer `envd` than its template shipped. The filesystem is untouched. This
is how a long-lived sandbox can adopt `envd` improvements - for example around memory
management, stability, and speed - without losing the data on disk.
To move an existing sandbox onto a newer `envd`:
1. Resume the sandbox.
2. Pause it filesystem-only with `mode: 'filesystem'` (JavaScript) / `mode="filesystem"` (Python).
3. Resume it again. This reboot can bring it up on a newer `envd`.
A sandbox has to be resumable to use this flow. A sandbox whose snapshot already fails to
resume cannot be paused filesystem-only in the first place, so it cannot be upgraded this way.
## Related
* [Snapshots](/sandbox/snapshots) — create snapshots and start new sandboxes from them.
* [Sandbox persistence](/sandbox/persistence) — full (memory + filesystem) pause and resume.
* [Auto-resume on request](/sandbox/auto-resume) — the full `lifecycle` configuration reference.
* [Reboot on resume](/sandbox/resume-without-memory) — skip the memory of a snapshot that has it.
* [Connect to a sandbox](/sandbox/connect) — resume a paused sandbox explicitly.
# Sandbox forking
Source: https://docs.e2b.dev/sandbox/fork
Snapshot a running sandbox and boot new sandboxes from that exact state in a single call.
Forking lets you create running copies of a sandbox in a single call.
The sandbox is snapshotted in place — paused, captured with its full filesystem and memory state, and resumed — and new sandboxes boot from that snapshot.
Each fork is an independent sandbox with its own ID and timeout. It starts with the exact state the original had at the moment of the fork — files, running processes, loaded variables, and data — and diverges from there. The original sandbox keeps running with its ID and expiration untouched.
## Fork flow
```mermaid actions={false} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
graph LR
A[Running Sandbox] -->|fork| B[Snapshotting]
B --> A
B -->|count = N| C[Fork 1]
B --> D[Fork 2]
B --> E[Fork N]
```
The snapshot is captured once regardless of how many forks you request, so forking into many sandboxes costs the same single snapshot of the original.
During the fork, the original sandbox is paused and resumed. The pause duration scales with the amount of disk changes since the last snapshot — write-heavy workloads pause longer. The pause also causes all active connections (e.g. WebSocket, PTY, command streams) to be dropped, so make sure your client handles reconnection properly.
## Fork a sandbox
You can fork a running sandbox instance. The method returns a list with one entry per requested fork.
```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('/home/user/state.txt', 'shared state')
// Fork the sandbox
const [fork] = await sandbox.fork()
if (fork instanceof Sandbox) {
// The fork starts with the original's files, processes, and memory
await fork.commands.run('cat /home/user/state.txt')
}
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
sandbox = Sandbox.create()
sandbox.files.write('/home/user/state.txt', 'shared state')
# Fork the sandbox
fork, = sandbox.fork()
if isinstance(fork, Sandbox):
# The fork starts with the original's files, processes, and memory
fork.commands.run('cat /home/user/state.txt')
```
You can also fork by sandbox ID using the static method.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
// Fork by sandbox ID
const forks = await Sandbox.fork(sandboxId)
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
# Fork by sandbox ID
forks = Sandbox.fork(sandbox_id)
```
You can also fork a sandbox from the terminal with the [E2B CLI](/cli/fork-sandbox). The command prints the ID of each new sandbox on its own line.
```bash Terminal theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
e2b sandbox fork
```
## Create multiple forks
Use `count` to boot several sandboxes from the same snapshot in one call. With the SDKs and the CLI you can request up to 20 forks at once. The count must also be lower than your team's concurrent sandbox limit, because the original sandbox keeps running and uses one slot.
```js JavaScript & TypeScript highlight={5} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const sandbox = await Sandbox.create()
const forks = await sandbox.fork({ count: 3 })
for (const fork of forks) {
if (fork instanceof Sandbox) {
console.log('Forked sandbox:', fork.sandboxId)
}
}
```
```python Python highlight={5} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
sandbox = Sandbox.create()
forks = sandbox.fork(count=3)
for fork in forks:
if isinstance(fork, Sandbox):
print('Forked sandbox:', fork.sandbox_id)
```
## Handle failed forks
Each fork succeeds or fails independently. Instead of throwing on the first failure, the returned list contains a connected sandbox for each fork that started and an error value for each fork that didn't — so a partial failure doesn't throw away the successful forks.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const results = await sandbox.fork({ count: 5 })
const forks = results.filter((r) => r instanceof Sandbox)
const errors = results.filter((r) => !(r instanceof Sandbox))
for (const error of errors) {
console.error('Fork failed:', error.message)
}
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
results = sandbox.fork(count=5)
forks = [r for r in results if isinstance(r, Sandbox)]
errors = [r for r in results if not isinstance(r, Sandbox)]
for error in errors:
print('Fork failed:', error)
```
If the request fails as a whole (for example, the sandbox does not exist), the method throws instead of returning error values.
## Fork timeout
The `timeoutMs` (JavaScript) / `timeout` (Python) option sets how long the new forked sandboxes live and defaults to 15 seconds. It applies to the forks only - the original sandbox's expiration is not changed.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const forks = await sandbox.fork({ count: 2, timeoutMs: 60_000 }) // 60 seconds
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
forks = sandbox.fork(count=2, timeout=60) # 60 seconds
```
## Forking vs. Snapshots
Forking is a one-call shortcut for the common snapshot pattern: capture a running sandbox and immediately boot new sandboxes from the capture.
| | Forking | Snapshots |
| - | - | - |
| Result | Running sandboxes, ready to use | A persistent snapshot ID |
| Reuse | Snapshot is used once, for this call's forks | Snapshot can spawn sandboxes any time later |
| Steps | One call | `createSnapshot()` / `create_snapshot()`, then `Sandbox.create()` per sandbox |
Use forking when you want running copies right now. Use [snapshots](/sandbox/snapshots) when you want a durable checkpoint to create sandboxes from later.
# Git integration
Source: https://docs.e2b.dev/sandbox/git-integration
The sandbox.git module is deprecated. Run git through sandbox.commands.run() instead.
The `sandbox.git` module is deprecated. It still works today, but it will be removed in the next major version of the JavaScript and Python SDKs. Run git through the commands module instead:
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
// Deprecated
await sandbox.git.clone('https://github.com/e2b-dev/E2B.git', { path: 'repo' })
// Use instead
await sandbox.commands.run('git clone https://github.com/e2b-dev/E2B.git repo')
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
# Deprecated
sandbox.git.clone("https://github.com/e2b-dev/E2B.git", path="repo")
# Use instead
sandbox.commands.run("git clone https://github.com/e2b-dev/E2B.git repo")
```
See the [migration guide](/migration/git-deprecation) for more examples.
### 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'
// After you change files, stage all changes and commit them. The default template
// sets no git user.name or user.email, so pass an author here or set one with
// configureUser() first.
await sandbox.git.add(repoPath)
await sandbox.git.commit(repoPath, 'Initial commit', {
authorName: 'E2B Bot',
authorEmail: 'bot@example.com',
})
// 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"
# After you change files, stage all changes and commit them. The default template
# sets no git user.name or user.email, so pass an author here or set one with
# configure_user() first.
sandbox.git.add(repo_path)
sandbox.git.commit(
repo_path,
"Initial commit",
author_name="E2B Bot",
author_email="bot@example.com",
)
# 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)
```
# Monitor sandbox lifecycle events
Source: https://docs.e2b.dev/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 project [API key](/api-key#where-to-find-api-key).
## Retention
Sandbox lifecycle events are retained for **7 days** by default. After the retention window passes, a sandbox's events are no longer available through this API.
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.
Each event's `eventData` carries the same fields as `event_data` in the [webhook payload](/sandbox/lifecycle-events-webhooks#webhook-payload), for example `kill_reason` on killed events, `pause_mode` on paused events and `resume_mode` on resumed events.
```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.sandboxId}`,
{
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 project
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 project
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://docs.e2b.dev/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 project [API key](/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 project 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 project.
```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(resp.json())
```
### Get webhook configuration
Retrieve the current webhook configuration for your project.
```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(
f"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(
f"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(
f"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"}}
{
"id": "",
"version": "v2",
"type": "",
"timestamp": "2025-08-06T20:59:24Z",
"event_category": "lifecycle",
"event_label": "kill",
"event_data": {
"sandbox_metadata": {
"": ""
},
"execution": {
"started_at": "2025-08-06T20:58:24Z",
"vcpu_count": 2,
"memory_mb": 512,
"execution_time": 1000
},
"kill_reason": "request"
},
"sandbox_id": "",
"sandbox_execution_id": "",
"sandbox_template_id": "",
"sandbox_build_id": "",
"sandbox_team_id": "",
"events_ttl_days": 30
}
```
`event_data.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
`event_data.kill_reason` is included on `sandbox.lifecycle.killed` events and describes why the sandbox was terminated. Possible values include:
* `request` - stopped by an explicit kill request
* `timeout` - the sandbox reached its timeout
* `admin` - stopped by an E2B administrative action
* `orphaned` - the sandbox was orphaned and cleaned up
* `base_template_missing` - the sandbox's base template was unavailable
* `seal_failed` - the sandbox could not be persisted during a pause and was killed
* `resume_failed` - the sandbox failed to resume after a snapshot and was killed
* `unknown` - the reason was not recorded
`event_data.pause_mode` is included on `sandbox.lifecycle.paused` events and describes what the pause saved. The values match the `mode` option of `pause()`:
* `full` - the filesystem and the memory were saved; resuming restores the sandbox where it left off
* `filesystem` - only the filesystem was saved; resuming reboots the sandbox from disk. See [Filesystem-only snapshots](/sandbox/filesystem-only-snapshots)
The value describes the snapshot that was taken, not the one that was requested. A memory auto-pause that keeps being refused falls back to a [filesystem-only pause](/sandbox/persistence#auto-pause), and its event says `filesystem`.
`event_data.resume_mode` is included on `sandbox.lifecycle.resumed` events and describes how the sandbox started. The values match the `onResume` option of `connect()`:
* `restore` - the memory snapshot was restored, so running processes continue
* `reboot` - the sandbox booted from its saved filesystem, either because the snapshot has no memory or because it was resumed with `onResume: 'reboot'` / `on_resume="reboot"`. See [Resume without memory](/sandbox/resume-without-memory)
Events recorded before these fields were added don't include them. Treat a missing `pause_mode` or `resume_mode` as unknown, not as `full` or `restore`.
`events_ttl_days` is the number of days the event is retained and remains available through the lifecycle events API.
# 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"}}
import * as crypto from 'node:crypto'
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 a webhook is sent, E2B adds 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. A new value is generated for each retry of the same event
* `e2b-signature-version` - Currently always `v1`, reserved for future use
* `e2b-signature` - Signature for verifying the request authenticity
## Delivery and retries
E2B attempts each delivery **up to 3 times in total**. A delivery attempt is treated as failed when your endpoint returns a 4xx or 5xx status code, the request times out, or it fails with a network error.
Because a delivery can be retried, the same event may be delivered more than once. Make your webhook handler idempotent and use the event `id` field in the payload to deduplicate deliveries you've already processed.
## 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](/sandbox/snapshots) created
# List sandboxes
Source: https://docs.e2b.dev/sandbox/list
You can list sandboxes using the `Sandbox.list()` method.
Once you have information about running sandbox, you can [connect](/sandbox/connect) to it using the `Sandbox.connect()` method.
### Listing sandboxes
The `Sandbox.list()` method supports pagination. In the [advanced pagination](/sandbox/list#advanced-pagination) section, you can find more information about pagination techniques.
```js JavaScript & TypeScript highlight={6,11,14,24-26} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } 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
if (paginator.hasNext) {
const nextPage = await paginator.nextItems()
}
```
```python Python highlight={5,9,12,22-23} 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
if paginator.has_next:
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
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
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",
"userId": "123",
},
)
# List sandboxes that have a `userId` key with value `123` and an `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()
```
Filter sandboxes by the template they were created from, using a template ID or alias.
```js JavaScript & TypeScript highlight={5} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const paginator = Sandbox.list({
query: {
template: 'base',
},
})
const sandboxes = await paginator.nextItems()
```
```python Python highlight={5} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox, SandboxQuery
paginator = Sandbox.list(
query=SandboxQuery(
template="base",
),
)
sandboxes = paginator.next_items()
```
Filter sandboxes by their start time. Only sandboxes started at or after the given timestamp are returned.
```js JavaScript & TypeScript highlight={5} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const paginator = Sandbox.list({
query: {
startedAfter: new Date('2025-01-01T00:00:00Z'),
},
})
const sandboxes = await paginator.nextItems()
```
```python Python highlight={6} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from datetime import datetime, timezone
from e2b import Sandbox, SandboxQuery
paginator = Sandbox.list(
query=SandboxQuery(
started_after=datetime(2025, 1, 1, tzinfo=timezone.utc),
),
)
sandboxes = paginator.next_items()
```
### Sorting sandboxes
Sort sandboxes by their start time using the `order` parameter with `'asc'` or `'desc'`. The default is `'desc'` (newest first). Sorting applies to the complete result set before pagination.
```js JavaScript & TypeScript highlight={4} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const paginator = Sandbox.list({
order: 'asc',
})
const sandboxes = await paginator.nextItems()
```
```python Python highlight={4} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
paginator = Sandbox.list(
order="asc",
)
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={4-5,12} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
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, type SandboxInfo } 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, SandboxInfo, SandboxQuery
paginator = Sandbox.list()
# Loop through all pages
sandboxes: list[SandboxInfo] = []
while paginator.has_next:
items = paginator.next_items()
sandboxes.extend(items)
```
# Sandbox metadata
Source: https://docs.e2b.dev/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](/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](/sandbox/list#filtering-sandboxes).
# Sandbox metrics
Source: https://docs.e2b.dev/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
// memCache: 73539584, // 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
// memCache: 73523200, // 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
# [
# SandboxMetrics(
# 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
# mem_cache=73535488, # in bytes
# timestamp=datetime.datetime(2025, 7, 28, 8, 8, 15, tzinfo=tzutc()),
# ),
# SandboxMetrics(
# 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
# mem_cache=73535488, # 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.
# OTel telemetry export
Source: https://docs.e2b.dev/sandbox/otel-telemetry-export
E2B can export OpenTelemetry (OTel) metrics and logs to any OpenTelemetry-compatible destination with an OTLP HTTP endpoint.
This lets you integrate E2B telemetry with your existing monitoring and observability stack.
## Exported telemetry
Exported telemetry mirrors the metrics and logs available in the E2B dashboard.
### Metrics
E2B sends the following metrics to your endpoint. Each metric retains its name.
* `e2b.sandbox.cpu.used` (`Gauge`) — CPU usage as a percentage.
* `e2b.sandbox.cpu.total` (`Gauge`) — vCPUs allocated to the sandbox.
* `e2b.sandbox.ram.used` (`Gauge`) — memory used, in bytes.
* `e2b.sandbox.ram.total` (`Gauge`) — memory available, in bytes.
* `e2b.sandbox.ram.cache` (`Gauge`) — memory used by the page cache, in bytes.
* `e2b.sandbox.disk.used` (`Gauge`) — disk space used, in bytes.
* `e2b.sandbox.disk.total` (`Gauge`) — disk space available, in bytes.
* `e2b.team.sandbox.created` (`Counter`) — sandboxes started for the team.
* `e2b.team.sandbox.running` (`Gauge`) — number of sandboxes currently running for the team.
Each sandbox metric includes the `sandbox_id`, `team_id`, `build_id`, and `sandbox_type` attributes. Each team metric includes `team_id`.
### Logs
Logs include `service_name: e2b`. They include sandbox lifecycle events, such as sandbox start and stop events, along with other sandbox actions. These logs help you correlate sandbox activity with application behavior and infrastructure events.
E2B sends telemetry using OTLP over HTTP/protobuf. Your endpoint should accept OTLP HTTP metrics and logs traffic, such as `/v1/metrics` and `/v1/logs` paths on an OpenTelemetry Collector.
Refer to your provider's documentation for specific OTLP endpoint details and authentication requirements.
## Setup
During onboarding, provide E2B with the endpoint details for your OTel collector or observability backend:
* OTLP HTTP endpoint URL
* Authentication requirements, such as static headers or bearer tokens
For BYOC deployments, E2B can configure telemetry export from your BYOC environment to your own collector or backend.
## Delivery behavior
Telemetry export is best effort. E2B tries to send metrics and logs continuously and retries whenever possible.
If your endpoint is unavailable, unreachable, or rejects telemetry, some metrics or logs may be delayed or dropped.
## Confirm telemetry delivery
After E2B enables the export, start one or more sandboxes and wait a few minutes for telemetry to reach your destination.
In your collector or observability backend, confirm that:
* Metrics are emitted under the `e2b.*` namespace.
* Logs include `service_name: e2b`.
You can compare the received telemetry with the metrics and logs shown in the E2B dashboard.
If telemetry does not appear, verify the OTLP endpoint URL, authentication headers, and any networking requirements such as allowlists or private connectivity.
# Sandbox persistence
Source: https://docs.e2b.dev/sandbox/persistence
Pause a sandbox and resume it later from the exact state it was in, including the filesystem and the memory, so running processes and loaded variables survive. A paused sandbox is kept indefinitely with no automatic deletion.
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.
A paused sandbox is kept **indefinitely**. There is no time-to-live and no automatic deletion, and E2B never kills a paused sandbox on its own. It is removed only when you explicitly kill it. See [How long does a sandbox live?](/faq/sandbox-lifetime).
## 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](/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)
```
By default a pause saves **both** the filesystem and the memory. To save only the filesystem — a lighter snapshot that reboots on resume — pass `mode: 'filesystem'` (JavaScript) / `mode="filesystem"` (Python). The older `keepMemory: false` / `keep_memory=False` form is deprecated but still works. See [Filesystem-only snapshots](/sandbox/filesystem-only-snapshots).
### When a pause is refused
Rolling out. The refusal behavior below is being enabled region by region. Until it reaches your region, a pause in this situation fails with a `500` error instead. `ServiceBusyError` and `ServiceBusyException` are available in the JavaScript and Python SDKs (the `e2b` package) starting with version `2.48.0`.
A pause can be refused when the node running the sandbox is still finishing the previous snapshot of the same sandbox. Instead of failing the sandbox, the API answers with HTTP `503` and the sandbox **keeps running with its state intact**. Nothing was paused and nothing was lost. The SDKs surface this as `ServiceBusyError` (JavaScript) / `ServiceBusyException` (Python), the same error every other 503 from the API raises, for example when there is no capacity to place a new sandbox.
The right reaction is to retry the pause after a short wait, or simply to keep using the sandbox and pause it later:
```js JavaScript & TypeScript highlight={7-12} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox, ServiceBusyError } from 'e2b'
const sbx = await Sandbox.create()
try {
await sbx.pause()
} catch (error) {
if (error instanceof ServiceBusyError) {
// The sandbox is still running; try again in a few seconds
await new Promise((resolve) => setTimeout(resolve, 5_000))
await sbx.pause()
} else {
throw error
}
}
```
```python Python highlight={8-11} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import time
from e2b import Sandbox, ServiceBusyException
sbx = Sandbox.create()
try:
sbx.pause()
except ServiceBusyException:
# The sandbox is still running; try again in a few seconds
time.sleep(5)
sbx.pause()
```
`ServiceBusyError` / `ServiceBusyException` is not a `SandboxError` / `SandboxException`, so catch it explicitly.
## 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](/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 connecting to a sandbox, the timeout is not reset. `Sandbox.connect()` only ever extends the sandbox's lifetime - the new expiry is the later of the current expiry and now plus the timeout you pass (default 5 minutes). So a sandbox with 20 minutes left stays at 20 minutes even if you connect with the default timeout, while a sandbox with only 1 minute left is bumped up to 5. To set an exact timeout that can also shorten the remaining lifetime, use `setTimeout()` / `set_timeout()` instead.
```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. `onTimeout`/`on_timeout` defaults to `"kill"`, meaning the sandbox is terminated when its timeout expires. Set it to `"pause"` to auto-pause on timeout instead.
```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', // Defaults to 'kill'; set to 'pause' to auto-pause on timeout
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", # Defaults to "kill"; set to "pause" to auto-pause on timeout
"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 the node refuses a memory auto-pause because it is still finishing the sandbox's previous snapshot, E2B keeps the sandbox running and retries the pause for up to about two minutes past its timeout. Only if the node keeps refusing for that whole window is the sandbox paused **filesystem-only**: the disk is preserved and the next resume reboots the sandbox, so the memory state is lost. Under normal conditions this never happens. It protects your sandbox during snapshot backlogs on a node, where the alternative would be losing the sandbox entirely. This behavior is rolling out region by region together with the refusal handling above. To see whether it happened to a sandbox, check its `sandbox.lifecycle.paused` event: `pause_mode` is `filesystem` (see [lifecycle events](/sandbox/lifecycle-events-webhooks#webhook-payload)).
To make the auto-pause filesystem-only — dropping memory so resume reboots the sandbox — use the object form `onTimeout: { action: 'pause', mode: 'filesystem' }` (JavaScript) / `"on_timeout": {"action": "pause", "mode": "filesystem"}` (Python). See [Filesystem-only snapshots](/sandbox/filesystem-only-snapshots).
If you call `.kill()`, the sandbox is permanently deleted and cannot be resumed.
For auto-resume behavior, see [AutoResume](/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
* There is currently **no configurable "auto-kill after N days" option**; a paused sandbox will not expire on its own
* You can resume a paused sandbox at any time
* To remove a paused sandbox, you must kill it explicitly with an API call (`sandbox.kill()` / `Sandbox.kill(sandboxId)`), as shown in [Removing paused sandboxes](#removing-paused-sandboxes)
### Continuous runtime limits
* A sandbox can remain running (without being paused) for:
* **24 hours** on the **Pro tier**
* **1 hour** on the **Hobby tier**
* After a sandbox is paused and resumed, the continuous runtime limit is **reset**
# Interactive terminal (PTY)
Source: https://docs.e2b.dev/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
The SDK asks the sandbox for a keepalive ping every 50 seconds on every stream, so a stream normally stays open for more than an hour even while nothing is written. No single connection is guaranteed that long, so build your client to reconnect. See [Stream connections](/faq/sandbox-lifetime#stream-connections).
## Create a PTY session
Use `sandbox.pty.create()` to start an interactive bash shell. This example runs `echo 'hello world'`, then exits and prints the terminal output.
```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 size in characters
rows: 24,
envs: { MY_VAR: 'hello' }, // Optional environment variables
cwd: '/home/user', // Optional working directory
user: 'root', // Optional user to run as
onData: (data) => {
// Called whenever the terminal outputs data
process.stdout.write(data)
},
})
// terminal.pid contains the process ID
console.log('Terminal PID:', terminal.pid)
// Send input to the PTY. Data must be bytes (Uint8Array), and the trailing newline "presses Enter".
await sandbox.pty.sendInput(terminal.pid, new TextEncoder().encode("echo 'hello world'\n"))
// The PTY runs an interactive login shell that won't exit on its own, so tell it
// to exit — otherwise wait() below blocks forever.
await sandbox.pty.sendInput(terminal.pid, new TextEncoder().encode('exit\n'))
// Wait for the terminal to exit (output is streamed to onData above)
await terminal.wait()
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox, PtySize
sandbox = Sandbox.create()
terminal = sandbox.pty.create(
size=PtySize(rows=24, cols=80), # Terminal size in characters
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)
# Send input to the PTY via the pty module. Data must be bytes, and the trailing newline "presses Enter".
sandbox.pty.send_stdin(terminal.pid, b"echo 'hello world'\n")
# The PTY runs an interactive login shell that won't exit on its own, so tell it
# to exit — otherwise wait() below blocks forever.
sandbox.pty.send_stdin(terminal.pid, b"exit\n")
# Stream output by passing on_pty to wait()
# end='' prevents print from adding an extra newline
terminal.wait(on_pty=lambda data: print(data.decode(), end=''))
```
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) so the SDK does not close the session on its own. If the connection drops, `pty.connect()` reattaches to the same running session. See [Disconnect and reconnect](#disconnect-and-reconnect).
```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, // Disable the SDK session timeout
})
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox, PtySize
sandbox = Sandbox.create()
terminal = sandbox.pty.create(
PtySize(rows=24, cols=80),
timeout=0, # Disable the SDK session timeout
)
# end='' prevents print() from adding an extra newline
# (PTY output already contains newlines)
terminal.wait(on_pty=lambda data: print(data.decode(), end=''))
```
## 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 is delivered to your `onData` callback (JavaScript) or to the `on_pty` callback you pass to `wait()` (Python).
```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, PtySize
sandbox = Sandbox.create()
terminal = sandbox.pty.create(PtySize(rows=24, cols=80))
# 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"\nexit\n')
# Receive output by passing on_pty to wait()
# end='' prevents print from adding an extra newline
terminal.wait(on_pty=lambda data: print(data.decode(), end=''))
```
## 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, PtySize
sandbox = Sandbox.create()
terminal = sandbox.pty.create(PtySize(rows=24, cols=80))
# Resize to new dimensions (in characters)
sandbox.pty.resize(terminal.pid, PtySize(rows=40, cols=120))
```
## 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\nexit\n'))
// Wait for the terminal to exit
await reconnected.wait()
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox, PtySize
sandbox = Sandbox.create()
# Create a PTY session
terminal = sandbox.pty.create(PtySize(rows=24, cols=80))
pid = terminal.pid
# Send a command
sandbox.pty.send_stdin(pid, b'echo hello\n')
# Disconnect - PTY keeps running in the background
terminal.disconnect()
# Later: reconnect to the same session
reconnected = sandbox.pty.connect(pid)
# Continue using the session, then exit
sandbox.pty.send_stdin(pid, b'echo world\nexit\n')
# Wait for the terminal to exit, streaming output via on_pty
reconnected.wait(on_pty=lambda data: print('Handler:', data.decode()))
```
## 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, PtySize
sandbox = Sandbox.create()
terminal = sandbox.pty.create(PtySize(rows=24, cols=80))
# 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, PtySize
sandbox = Sandbox.create()
terminal = sandbox.pty.create(PtySize(rows=24, cols=80))
# Send exit command
sandbox.pty.send_stdin(terminal.pid, b'exit\n')
# Wait for the terminal to exit, streaming output via on_pty
result = terminal.wait(on_pty=lambda data: print(data.decode(), end=''))
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.
# Reboot on resume
Source: https://docs.e2b.dev/sandbox/resume-without-memory
Resume a paused sandbox from its disk state alone, ignoring the memory in its snapshot.
Resuming a paused sandbox restores its memory, so it continues from where it
left off.
A **reboot** resume ignores that memory and boots the sandbox from its disk
instead. Everything written and flushed before the pause is there; nothing that
lived only in memory returns — no running processes, no caches, no in-sandbox
connections. The snapshot itself is left alone, so it can still be restored
normally afterwards.
Pass `onResume: 'reboot'` (JavaScript) / `on_resume="reboot"` (Python) to
[`connect()`](/sandbox/connect):
```js JavaScript & TypeScript highlight={4} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
// The memory snapshot is ignored — the sandbox reboots from disk
const sbx = await Sandbox.connect(sandboxId, { onResume: 'reboot' })
```
```python Python highlight={4} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
# The memory snapshot is ignored — the sandbox reboots from disk
sbx = Sandbox.connect(sandbox_id, on_resume="reboot")
```
`onResume` / `on_resume` defaults to `'restore'`, the memory restore `connect()`
has always done.
This is a **resume-side** choice, and it is not the same as a
[filesystem-only snapshot](/sandbox/filesystem-only-snapshots), which is a
**pause-side** choice. A filesystem-only snapshot never captures memory in the
first place; `onResume: 'reboot'` / `on_resume="reboot"` skips the memory of a
snapshot that *has* it.
## When to reboot instead of restoring
A reboot is the way past a memory image that will not restore cleanly: a sandbox
that ran out of memory before it was paused comes back under the same pressure,
and the resume can hang instead of completing. A reboot never reads that memory,
so it cannot hang on it.
Because the choice is made at resume time, it also applies to snapshots taken
without the caller's involvement: a sandbox
[auto-paused](/sandbox/persistence#auto-pause) on timeout keeps its memory.
A reboot is slower than a restore, not faster: a memory restore takes about a
second, while a reboot costs roughly a fresh boot.
## Restore vs. reboot
| | `'restore'` / `"restore"` (default) | `'reboot'` / `"reboot"` |
| - | - | - |
| Filesystem (disk) | Preserved | Preserved |
| Memory (RAM) | Restored | **Ignored** |
| Running processes | Restored | **Terminated** (reboot) |
| Unflushed writes | Preserved | **Lost** |
| The memory snapshot | Consumed as-is | **Left untouched** |
| Speed | Near-instant | Roughly a fresh boot |
Connections from clients outside the sandbox are not one of the differences:
they drop when the sandbox pauses and have to be re-established on resume either
way (see [Persistence](/sandbox/persistence#network)).
## What you get on disk
A reboot from a memory-inclusive snapshot gives you **crash-recovery
semantics**: the disk image is what a power loss would have left. Filesystem
metadata is repaired as the disk is mounted, but data writes that had not been
flushed when the sandbox was paused are gone.
Expect the same losses you would expect from pulling the power on a machine — a
partially written file, or a database that replays its own log on startup.
Anything written and flushed survives; anything still sitting in the page cache
does not.
Unflushed writes are discarded, not merely at risk. A file written without
`sync` should be assumed lost after a reboot resume.
## Keep a snapshot afterwards
Once the sandbox is running again, the memory in the original snapshot is no
longer reachable. Read or write what you need, then **pause it** — that records
a new snapshot you can resume normally.
```js JavaScript & TypeScript highlight={8} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const sbx = await Sandbox.connect(sandboxId, { onResume: 'reboot' })
const data = await sbx.files.read('/home/user/important.txt')
// Record a new snapshot before the sandbox times out
await sbx.pause()
```
```python Python highlight={8} theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
sbx = Sandbox.connect(sandbox_id, on_resume="reboot")
data = sbx.files.read("/home/user/important.txt")
# Record a new snapshot before the sandbox times out
sbx.pause()
```
Don't just leave the rebooted sandbox running. If it reaches its timeout without
being paused it is discarded and the sandbox reverts to the snapshot you resumed
from, memory and all. Your next plain `connect()` then restores that memory and
puts you back where you started. If the reboot was to get past a memory image
that would not restore, that image is what the next `connect()` restores.
Killing it instead is final: a deleted sandbox has no resume path, so
`connect()` afterwards reports that the sandbox does not exist. In Python,
remember that using the sandbox as a context manager kills it when the block
exits.
## Only a paused sandbox is rebooted
* **Already running?** The option is ignored and you get the running sandbox
back. `connect()` never shortens a sandbox's lifetime: the new expiry is the
later of the current expiry and now plus the timeout you pass
([Persistence](/sandbox/persistence#sandboxs-timeout)). To reboot it,
[`pause()`](/sandbox/persistence) it first and then connect with
`onResume: 'reboot'` / `on_resume="reboot"`.
* **Still starting?** A reboot that would drop memory is refused while a start
is already in flight, because the reboot cannot join a start that is
restoring memory. Let the start finish, then pause, then reboot. This can
happen without you doing anything: with
[auto-resume](/sandbox/auto-resume) enabled, inbound traffic can start a
paused sandbox on its own.
* **Paused without memory?** A
[filesystem-only snapshot](/sandbox/filesystem-only-snapshots) already
reboots on every resume, so `onResume: 'reboot'` / `on_resume="reboot"` changes
nothing for it and is harmless to pass.
## If the reboot fails
A reboot that starts and then fails to boot leaves the sandbox paused with its
snapshot intact, so you can retry the reboot, or connect normally to restore the
memory as before. An attempt that does not succeed consumes nothing.
## Self-hosted deployments
On a control plane that knows the option but does not have it turned on, an
`onResume: 'reboot'` / `on_resume="reboot"` that would actually drop memory fails
with an explicit error rather than quietly restoring the memory, so you can tell
a reboot that did not happen from one that did.
A control plane older than the option does not recognise it at all: it is
ignored, the memory is restored, and the call returns success — so a reboot can
appear to work without having happened. Check your control plane is new enough
before relying on it.
## Related
* [Sandbox persistence](/sandbox/persistence) — pause and resume, and the sandbox state machine.
* [Filesystem-only snapshots](/sandbox/filesystem-only-snapshots) — persist only the filesystem at pause time.
* [Connect to a sandbox](/sandbox/connect) — resume a paused sandbox explicitly.
* [Auto-resume on request](/sandbox/auto-resume) — waking a paused sandbox with inbound traffic.
# Secured access
Source: https://docs.e2b.dev/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.
SDK versions `2.51.0` and above ignore the `secure` option and always create sandboxes with secure access through `Sandbox.create`. To create sandboxes from these templates with these SDK versions, rebuild them first.
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](/filesystem/upload#upload-with-pre-signed-url) and [download](/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.
## Deprecated `secure` option
The `secure` option is deprecated. SDK versions `2.51.0` and above accept it for backward compatibility but ignore it. With these SDK versions, passing `secure: false` does not disable secure access, and every sandbox created with `Sandbox.create` has secure access enabled.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const sandbox = await Sandbox.create({ secure: false }) // Ignored from SDK 2.51.0, secure access stays enabled
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
sandbox = Sandbox.create(secure=False) # Ignored from SDK 2.51.0, secure access stays enabled
```
# Sandbox snapshots
Source: https://docs.e2b.dev/sandbox/snapshots
Create a persistent point-in-time capture of a running sandbox, including filesystem and memory, and spawn new sandboxes from that exact state.
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](/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)
```
### Filesystem-only snapshots
By default a snapshot captures both the filesystem and the memory. Pass `mode: 'filesystem'`
(JavaScript) / `mode="filesystem"` (Python) to capture only the filesystem: the snapshot is
smaller and faster to take, the source sandbox keeps running, and sandboxes created from it
reboot from disk instead of restoring memory. See
[Filesystem-only snapshots](/sandbox/filesystem-only-snapshots) for the details, the
`SnapshotMode` type and the cases in which the request is refused.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
const snapshot = await sandbox.createSnapshot({ mode: 'filesystem' })
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
snapshot = sandbox.create_snapshot(mode="filesystem")
```
## 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](/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.
### Performance
For workloads where either approach would work, **templates are faster and more resource-efficient** than snapshots:
* **Compact memory.** During a template build, the guest OS is restarted before the long-running process is captured. Memory is compact and any setup-time processes that aren't needed at runtime are gone, so sandboxes start with less memory pressure and fewer resources.
* **More effective prefetching.** E2B optimistically prefetches data needed to start a sandbox. For templates this prefetching is highly effective; for snapshots its effectiveness is significantly lower due to memory fragmentation and general memory pressure from the captured live state.
If you can express the state you need as a declarative template build, you'll generally get faster cold starts and lower overhead than capturing the equivalent state as a snapshot. Creating many templates is a supported pattern, so this doesn't need to discourage you from using templates per-customer or per-project. See [Scaling templates](/template/quickstart#scaling-templates).
## 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. To snapshot and spawn copies in a single call, see [Forking](/sandbox/fork).
* **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://docs.e2b.dev/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) │ │
│ └──────────────────┘ │
└───────────────────────────────────────────────────────────┘
```
# Overview
Source: https://docs.e2b.dev/secrets
Store secrets with E2B and inject them into outbound HTTPS requests outside the sandbox.
Secrets let you store credentials such as API keys with E2B and reference them by name.
Rather than passing a key as an environment variable or baking it into a template, where any code running in the sandbox can read it, you store the value once and reference it in a network rule.
The egress proxy injects the real value into matching outbound HTTPS requests outside the sandbox. Your sandbox configuration contains only a reference to the secret.
Secret values are write-only through the management API: create and update accept the value, while responses, reads, lists, and API errors never return it. Manage secrets from your trusted application, keeping its E2B API key outside the sandbox. Secrets belong to the project associated with that key.
Only inject credentials into destinations you trust. The destination receives the value and could expose it in its response to the sandbox.
With the E2B SDK you can:
* [Create a secret.](/secrets/create)
* [Inject secrets into the sandbox's requests.](/secrets/inject)
* [Rotate secrets without restarting sandboxes.](/secrets/rotate)
* [Delete secrets.](/secrets/delete)
* [List secrets and read their metadata.](/secrets/list)
For short-lived tokens minted per sandbox instead of stored credentials, see [workload identity](/iam/workload-identity).
# Create a secret
Source: https://docs.e2b.dev/secrets/create
Store a credential with E2B and reference it by a name you choose.
Create a secret with `Secret.create`. The name is yours to choose; the value is stored encrypted and the response carries metadata only.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Secret } from 'e2b'
const secret = await Secret.create('stripe_api_key', 'sk_live_...', {
metadata: { environment: 'production' },
})
console.log(secret.secretId) // sec_...
console.log(secret.version) // 1
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Secret
secret = Secret.create(
"stripe_api_key",
"sk_live_...",
metadata={"environment": "production"},
)
print(secret.secret_id) # sec_...
print(secret.version) # 1
```
## Secret names
When creating a secret, its name must contain 1–128 ASCII letters, digits, underscores (`_`), or hyphens (`-`), with no whitespace. The API lowercases the name before storing it, so `My-Key` and `my-key` identify the same secret. Names may not start with the reserved `sec_` prefix, in any capitalization. Invalid names, including names with surrounding whitespace, receive a `400` API error that doesn't echo the name back. Creating a name that already exists in the project returns `409`. Use `Secret.update` to rotate its value.
Each secret also receives a stable `sec_...` identifier. Every method that takes a single secret (`Secret.getInfo`, `Secret.exists`, `Secret.update`, and `Secret.destroy`) accepts either the identifier or the name.
# Deleting secrets
Source: https://docs.e2b.dev/secrets/delete
Revoke a stored secret and understand what happens to sandboxes that reference it.
Delete a secret with `Secret.destroy`. It returns `true` when the secret was deleted and `false` when it didn't exist.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Secret } from 'e2b'
await Secret.destroy('stripe_api_key') // true
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Secret
Secret.destroy("stripe_api_key") # True
```
After deletion succeeds, new secrets lookups cannot resolve the secret. Matching HTTPS requests omit headers referencing it, following the [fail-open behavior](/secrets/inject#resolution-failures-are-silent-for-traffic) of unresolved references. Requests that already resolved its value can still use it. Stored versions are cleaned up asynchronously.
Deleting a secret from E2B does not revoke the credential at its provider. Revoke the API key or token there as well when you need to invalidate it.
Markers reference secrets by name, so deleting and recreating a name makes matching requests resolve the new secret's value. The recreated secret receives a new `sec_...` identifier and starts again at version 1.
# Injecting secrets into requests
Source: https://docs.e2b.dev/secrets/inject
Reference a stored secret in a network rule so the egress proxy adds it to matching outbound requests.
Stored secrets are injected into the sandbox's outbound **HTTPS** requests through [per-host request transforms](/network/internet-access#per-host-request-transforms). Plain HTTP requests do not receive injected headers. Use `Secret.fill` to build the header value in a network rule's `transform`:
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox, Secret } from 'e2b'
await Secret.create('stripe_api_key', 'sk_live_...')
const sandbox = await Sandbox.create({
network: {
// Only allow egress to hosts that have rules registered.
allowOut: ({ rules }) => [...rules.keys()],
// Deny all other traffic
denyOut: ({ allTraffic }) => [allTraffic],
rules: {
'api.stripe.com': [
{
transform: {
headers: { Authorization: `Bearer ${Secret.fill('stripe_api_key')}` },
},
},
],
},
},
})
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox, Secret
Secret.create("stripe_api_key", "sk_live_...")
sandbox = Sandbox.create(
network={
# Only allow egress to hosts that have rules registered.
"allow_out": lambda ctx: list(ctx.rules.keys()),
# Deny all other traffic
"deny_out": lambda ctx: [ctx.all_traffic],
"rules": {
"api.stripe.com": [
{
"transform": {
"headers": {"Authorization": f"Bearer {Secret.fill('stripe_api_key')}"},
},
},
],
},
},
)
```
`Secret.fill('stripe_api_key')` is a local formatting helper that returns a reference to the secret without making a network call. Pass the secret's name, not its `sec_...` identifier. The SDK rejects empty names, braces, and control characters, but does not check whether the secret exists or enforce every API naming rule. Always use a valid [secret name](/secrets/create#secret-names).
The reference is stored in the sandbox's network configuration. Each time the egress proxy forwards a matching HTTPS request, it resolves the secret's current value and injects it outside the sandbox. Always build references with `Secret.fill`, since their format is an internal detail that can change.
The example allows outbound traffic only to the named host. Network rules alone do not grant or restrict access, so configure `allowOut` / `allow_out` and `denyOut` / `deny_out` as shown. Choose trusted hosts because they receive the injected credential.
A header value can mix secret markers with static text and [workload identity](/iam/workload-identity) token placeholders. Each header is substituted atomically: either every marker in it resolves, or the header is omitted from the forwarded request.
## Resolution failures are silent for traffic
Referencing a valid secret name that doesn't exist does **not** fail at sandbox creation. Secret existence is checked only at request time by the egress proxy. Invalid network rules can still cause sandbox creation to fail. When a reference can't be resolved (a misspelled name, a deleted secret, or resolution being temporarily unavailable), the proxy fails open for traffic:
* The request is still forwarded to the destination.
* Every header containing an unresolved reference is omitted in full. A failure of the whole secrets lookup omits all headers containing secret references for that request. Static headers and unrelated headers are unaffected by a secret lookup failure.
* A configured transform replaces any header of the same name sent by sandbox code. If resolution fails, the proxy also removes that sandbox-supplied header.
* The unresolved reference is never forwarded to the destination.
Resolved values must also be valid HTTP header values. A value containing a newline, carriage return, or another forbidden control character causes the entire header to be omitted.
The destination may return an authentication error such as `401` or `403`, depending on how it handles the missing header. E2B does not replace the forwarded response with a secret-resolution error. If authentication starts failing, check that the secret exists in the sandbox's project, that the reference uses its name, and that its value is valid for an HTTP header. `Secret.exists` checks existence without changing the secret, but does not test runtime injection.
# Limits
Source: https://docs.e2b.dev/secrets/limits
Size, count, and pagination limits that apply to stored secrets.
* A secret value can be up to **64 KiB**, measured in UTF-8 bytes. Exceeding the limit fails with a `400` error. Values used in headers must also be valid HTTP header values.
* A project can hold up to **10** secrets on the Hobby plan and **100** on paid plans. Exceeding the limit fails with a `409` error. See [Billing & limits](/billing#plans).
* The network rules for one host can reference up to **32** distinct secret names after lowercasing. Repeated references to the same name count once. Exceeding the limit fails network-rule validation with a `400` error.
* A request can resolve up to **256 KiB** of secret values in total, counting each distinct name once. References that exceed this budget cannot be resolved, so their headers are omitted.
* Secret metadata supports up to **32** entries, with keys up to **128 bytes**, values up to **1,024 bytes**, and **8 KiB** in total across all keys and values. Exceeding these limits returns `400`.
* `Secret.list` returns up to **100** entries per page by default. Use `Secret.list({ limit: 50 })` in JavaScript or `Secret.list(limit=50)` in Python for smaller pages. Metadata filtering is not supported.
# Listing secrets
Source: https://docs.e2b.dev/secrets/list
Read one secret's metadata, check whether a name is stored, or page through the project's secrets. No read surface returns a value.
`Secret.getInfo` (JavaScript) / `Secret.get_info` (Python) returns one secret's metadata and throws `SecretNotFoundError` (JavaScript) / `SecretNotFoundException` (Python) when it doesn't exist. `Secret.exists` answers the same question with a boolean instead of an error, which is the quickest way to check that a name you reference in a network rule is really stored. `Secret.list` returns a paginator over the project's secrets.
No read surface returns a secret value.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Secret } from 'e2b'
const info = await Secret.getInfo('stripe_api_key')
// info.secretId -> 'sec_...' stable identifier, new one if you recreate the name
// info.name -> 'stripe_api_key'
// info.version -> 2 the version markers currently resolve to
// info.metadata -> { environment: 'production' }
// info.createdAt -> Date
// info.updatedAt -> Date
await Secret.exists('stripe_api_key') // true
await Secret.exists('typo_api_key') // false
const paginator = Secret.list()
while (paginator.hasNext) {
const secrets = await paginator.nextItems()
console.log(secrets.map((s) => s.name))
}
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Secret
info = Secret.get_info("stripe_api_key")
# info.secret_id -> "sec_..." stable identifier, new one if you recreate the name
# info.name -> "stripe_api_key"
# info.version -> 2 the version markers currently resolve to
# info.metadata -> {"environment": "production"}
# info.created_at -> datetime
# info.updated_at -> datetime
Secret.exists("stripe_api_key") # True
Secret.exists("typo_api_key") # False
paginator = Secret.list()
while paginator.has_next:
secrets = paginator.next_items()
print([s.name for s in secrets])
```
Like every method that takes a single secret, `getInfo` and `exists` accept either the name or the `sec_...` identifier.
`metadata` is yours to use for organizing secrets, for example by environment or owner. It's not secret material, so don't store another credential in it. Create defaults it to an empty map. Update preserves it when omitted and replaces the whole map when supplied, so passing an empty map clears it.
# Rotating secrets
Source: https://docs.e2b.dev/secrets/rotate
Store a new value for a secret and have running sandboxes pick it up on their next request.
Update a secret with `Secret.update`. Each successful update stores a new immutable version and makes it current. Version numbers increase and are never reused within the same secret, but failed updates can leave gaps.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Secret } from 'e2b'
const secret = await Secret.update('stripe_api_key', 'sk_live_new...')
console.log(secret.version) // 2
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Secret
secret = Secret.update("stripe_api_key", "sk_live_new...")
print(secret.version) # 2
```
After the update succeeds, matching HTTPS requests whose secrets lookup starts afterward use the new current value. A request that already resolved the old value can still use it. You don't need to restart the sandbox or update its network rules, and a paused sandbox uses the current value after it resumes.
References always resolve the current version. The public API and SDKs do not support version pinning, reading older values, or rolling back to a previous version. To restore an earlier credential, supply that value again with `Secret.update`.
# Archil
Source: https://docs.e2b.dev/storage/archil
Mount Archil's elastic POSIX file system in your E2B sandbox for high-performance, shared access to S3, R2, and GCS object storage.
[Archil](https://archil.com) is an infinite, elastic, POSIX file system that automatically synchronizes to object storage like S3, R2, and GCS. Use Archil when you need to mount a bucket to your E2B sandbox but want higher out-of-the-box performance than `s3fs` or `gcsfuse` — it's a natural fit for parallel agents that share state.
Key properties:
* Shared SSD read and write caching in front of your object storage bucket for higher performance.
* Mounted as a regular POSIX directory, so any tool, script, or agent in the sandbox can use it.
* Scales to whatever your sandbox writes — you pay only for what you use.
* [Mountable by many sandboxes at once](https://docs.archil.com/concepts/shared-disks) for shared state across parallel agents.
## Build a template
Build a template with the `archil` CLI preinstalled. The Archil package depends on `libfuse2`, which is already preinstalled in the base image.
```ts JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
const template = Template()
.fromBaseImage()
.runCmd("curl -fsSL https://archil.com/install | sh")
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
template = (
Template()
.from_base_image()
.run_cmd("curl -fsSL https://archil.com/install | sh")
)
```
## Mount the disk
At sandbox runtime, mount the disk with a [disk-scoped mount token](https://docs.archil.com/concepts/disk-users#disk-token-authorization) from the [Archil console](https://console.archil.com).
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const sandbox = await Sandbox.create('')
await sandbox.commands.run('mkdir -p /home/user/archil', { user: 'root' })
await sandbox.commands.run(
'archil mount /home/user/archil --region ',
{ user: 'root', envs: { ARCHIL_MOUNT_TOKEN: '' } }
)
await sandbox.commands.run('chown user:user /home/user/archil', { user: 'root' })
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
sandbox = Sandbox.create("")
sandbox.commands.run("mkdir -p /home/user/archil", user="root")
sandbox.commands.run(
"archil mount /home/user/archil --region ",
user="root",
envs={"ARCHIL_MOUNT_TOKEN": ""},
)
sandbox.commands.run("chown user:user /home/user/archil", user="root")
```
`` is either an owner-qualified name (`myorg/my-disk`) or a disk ID (`dsk-0123456789abcdef`); `` is the disk's region (e.g. `aws-us-east-1`). The disk is now mounted at `/home/user/archil` as a regular POSIX directory — any tool, script, or agent in the sandbox can read and write through it.
## Unmount before killing the sandbox
Call `archil unmount` before tearing down the sandbox. It's best practice to release filesystem resources cleanly rather than letting the sandbox kill take them down.
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
await sandbox.commands.run('archil unmount /home/user/archil', { user: 'root' })
await sandbox.kill()
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
sandbox.commands.run("archil unmount /home/user/archil", user="root")
sandbox.kill()
```
## Sharing across sandboxes
The same disk can be mounted by multiple sandboxes concurrently. Add `--shared` to the mount command — see [Shared Disks](https://docs.archil.com/concepts/shared-disks) for the checkout-based concurrency model.
# Cloud buckets
Source: https://docs.e2b.dev/storage/cloud-buckets
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](/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](/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](/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.
# Need help?
Source: https://docs.e2b.dev/support
The fastest way to reach our support team is through **Contact support** in the **Help** menu of your [E2B Console](https://console.e2b.dev/?support=true). The Help menu is the info icon in the top-right corner of the dashboard. In the mobile view, you can find the same options in your avatar menu.
Open the E2B Dashboard to contact our support team directly.
You can also reach us through these channels:
# Base image
Source: https://docs.e2b.dev/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. E2B identifies the image's distribution from its `/etc/os-release`.
**Supported:** Debian/Ubuntu, Fedora and the RHEL family (including Rocky Linux and AlmaLinux), Arch, and Alpine.
**Not supported:**
* Images with no identifiable distribution (no `/etc/os-release`), such as `scratch`, distroless, or pure `nixos/nix`.
* Minimal images missing required packages, such as RHEL UBI-minimal with restricted repositories.
Unsupported images fail the build with a clear error naming the reason.
For NixOS, use the official [`e2bdev/nixos`](#nixos-base-image) image instead of a raw NixOS image.
### Predefined base images
Use convenience methods for common base images with Ubuntu, Debian, Fedora, Alpine, Arch, 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.fromFedoraImage("44"); // fedora:44
template.fromAlpineImage("3.24"); // alpine:3.24
template.fromArchImage("base-devel"); // archlinux:base-devel
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_fedora_image("44") # fedora:44
template.from_alpine_image("3.24") # alpine:3.24
template.from_arch_image("base-devel") # archlinux:base-devel
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
```
`base` is also the template used when you call `Sandbox.create()` without specifying one. It is Debian-based and comes with common tooling pre-installed: Python 3, Node.js, Yarn, `git`, `curl`, `build-essential`, and the GitHub CLI (`gh`). For the exact contents, see the public Dockerfile: [`templates/base/e2b.Dockerfile`](https://github.com/e2b-dev/E2B/blob/main/templates/base/e2b.Dockerfile).
### NixOS base image
Arbitrary [NixOS](https://nixos.org/) images (such as `nixos/nix`) aren't supported directly, because they don't expose the `/etc/os-release` metadata E2B needs to identify the distribution. To build NixOS-based sandboxes, use the official `e2bdev/nixos` image, which E2B prepares to work with the sandbox environment:
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
template.fromImage("e2bdev/nixos");
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
template.from_image("e2bdev/nixos")
```
### Default resources
A sandbox gets its CPU and memory from the template it's built on. A sandbox built from `base` starts with **2 vCPU and 512 MiB of RAM**.
You can override CPU and memory when you [build a template](/template/build) (`cpuCount`/`cpu_count`, `memoryMB`/`memory_mb`). Disk size is not a default you set per sandbox; it's determined by your project's tier limit and applied at build time (see [Build limits](/template/quickstart#build-limits)).
### Build from existing template
Extend an existing template from your project or organization:
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
template.fromTemplate("my-template"); // Your project'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 project'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://docs.e2b.dev/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
minFreeDiskMb: 2048, // Minimum free disk space after the build, in MiB
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
min_free_disk_mb=2048, # Minimum free disk space after the build, in MiB
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)
```
## Minimum free disk space
Use `minFreeDiskMb` in JavaScript or `min_free_disk_mb` in Python to request minimum free disk space after the template's build steps finish. This option is available in the JavaScript and Python SDKs starting with version `2.49.0`, including Python's sync and async build methods.
* Omit the option to use your team's default.
* Set it to `0` to request no minimum free disk growth.
* Set a positive integer to request that much free space, in MiB.
With CLI version `2.19.0` or later, use:
```sh theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
e2b template create my-template --min-free-disk-mb 2048
```
This is a free-space target for a template build, not a total disk-size setting or an option on `Sandbox.create`. The resulting filesystem includes space used by the template as well as its free space. `diskSizeMB` is an API response field, not a build input. See [Build limits](/template/quickstart#build-limits) for tier limits.
## 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://docs.e2b.dev/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 project, 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://docs.e2b.dev/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?](/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://docs.e2b.dev/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://docs.e2b.dev/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](/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://docs.e2b.dev/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://docs.e2b.dev/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://docs.e2b.dev/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://docs.e2b.dev/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://docs.e2b.dev/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://docs.e2b.dev/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](/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](/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](/template/user-and-workdir) section.
## Caching
To learn more about caching, please refer to the [Caching](/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://docs.e2b.dev/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
```
# Template names
Source: https://docs.e2b.dev/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')
```
## Name format
Before a name is used, it's trimmed of surrounding whitespace and lowercased. The result must match the pattern `^[a-z0-9-_]+$`:
* Lowercase letters (`a`–`z`), numbers (`0`–`9`), dashes (`-`), and underscores (`_`)
* Between 1 and 128 characters
* Leading and trailing dashes or underscores are allowed
Uppercase letters are accepted on input and lowercased automatically, so `My-Template` and `my-template` refer to the same name. Any other character (spaces inside the name, dots, slashes, and so on) is rejected.
## Project-local naming
Template names are scoped to your project. This means:
* Your template named `my-app` is stored as `your-project-slug/my-app`
* You can reference it simply as `my-app` within your project
* Other projects can have their own `my-app` template without conflict
* Public templates should be referenced using the full namespaced format (`project-slug/template-name`)
**Backwards Compatibility**: Existing public templates remain accessible without the project slug prefix. New public templates should be referenced using the full namespaced format (`project-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 project 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](/template/tags) for version management (e.g., `myapp:v1`, `myapp:v2`)
3. **Use consistent naming**: Establish a naming convention for your project and stick to it
# Private registries
Source: https://docs.e2b.dev/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://docs.e2b.dev/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](/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://docs.e2b.dev/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.
## Scaling templates
You can create a large number of templates with low overhead, for example one template per customer, per project, or per agent run. There's currently no limit on the number of templates, though pricing for total storage used by templates may be introduced in the future.
The template build environment is a full sandbox environment, so you can do anything during the build that you can do inside a running sandbox, including running Docker containers as part of the setup.
Compared to [snapshots](/sandbox/snapshots), templates start faster and use fewer resources because the guest OS is restarted before the long-running process is captured and prefetching is significantly more effective. If your workload involves spinning up many per-customer or per-project environments, prefer templates over snapshots.
### Layering templates with `fromTemplate`
When you build many similar templates (e.g. a customer-specific template per customer that all share the same base setup), use [`fromTemplate`](/sdk-reference/js-sdk/latest/template#fromtemplate) to start from an existing template instead of rebuilding the shared layers from scratch each time. This keeps per-customer builds fast and reuses the cached base.
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Template } from 'e2b'
// Per-customer template built on top of a shared base template
export const template = Template()
.fromTemplate('my-base-template')
.copy('./customers/acme', '/app/config')
.setEnvs({ CUSTOMER_ID: 'acme' })
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Template
# Per-customer template built on top of a shared base template
template = (
Template()
.from_template("my-base-template")
.copy("./customers/acme", "/app/config")
.set_envs({"CUSTOMER_ID": "acme"})
)
```
## Build limits
Template builds are subject to the following limits:
* **Max build duration**: Builds can run for **up to 1 hour**. If a build exceeds this, it will be terminated.
* **Max vCPUs per build**: 8 on Hobby, 8+ on Pro, custom on Enterprise.
* **Max memory per build**: 8 GiB on Hobby, 8+ GiB on Pro, custom on Enterprise.
* **Max disk size per build**: 10 GiB on Hobby, 20+ GiB on Pro, custom on Enterprise.
* **Concurrent builds**: 20 on Hobby and Pro, custom on Enterprise.
See [Billing & limits](/billing#plans) for the full list of plan limits. Need higher limits? Contact [support@e2b.dev](mailto:support@e2b.dev).
# Start & ready commands
Source: https://docs.e2b.dev/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](/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()`](/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](/template/how-it-works).
## Ready command
The ready command determines when the sandbox is ready before a [snapshot](/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://docs.e2b.dev/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 project
my-template:production // Within your project
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://docs.e2b.dev/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
```
# Run Android
Source: https://docs.e2b.dev/use-cases/android
Start an Android 16 system in an E2B sandbox and drive it with adb for app testing and mobile agents.
The `android-16` template runs Android 16 inside an E2B Sandbox. Android ([redroid](https://github.com/remote-android/redroid-doc), AOSP in a container) is booted when the template is built, so a new sandbox starts with Android already running, the screen on, and `adb` ready to drive it.
Use it when an agent or a test needs a real Android system: install an APK, tap through its UI, read the screen, and kill the sandbox afterwards.
## Quickstart
Create a sandbox, check the Android version, open Settings, and save a screenshot locally. `adb` runs inside the sandbox, so every Android command goes through `sandbox.commands.run()`.
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { writeFileSync } from 'node:fs'
import { Sandbox } from 'e2b'
const sandbox = await Sandbox.create('android-16', { timeoutMs: 5 * 60_000 })
const adb = 'adb -s 127.0.0.1:5555'
try {
await sandbox.commands.run('adb connect 127.0.0.1:5555')
const version = await sandbox.commands.run(`${adb} shell getprop ro.build.version.release`)
console.log(`Android ${version.stdout.trim()}`)
await sandbox.commands.run(`${adb} shell am start -a android.settings.SETTINGS`)
await sandbox.commands.run('sleep 2')
const png = await sandbox.commands.run(`${adb} exec-out screencap -p | base64 -w0`)
writeFileSync('screenshot.png', Buffer.from(png.stdout, 'base64'))
} finally {
await sandbox.kill()
}
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import base64
from e2b import Sandbox
sandbox = Sandbox.create("android-16", timeout=5 * 60)
adb = "adb -s 127.0.0.1:5555"
try:
sandbox.commands.run("adb connect 127.0.0.1:5555")
version = sandbox.commands.run(f"{adb} shell getprop ro.build.version.release")
print(f"Android {version.stdout.strip()}")
sandbox.commands.run(f"{adb} shell am start -a android.settings.SETTINGS")
sandbox.commands.run("sleep 2")
png = sandbox.commands.run(f"{adb} exec-out screencap -p | base64 -w0")
with open("screenshot.png", "wb") as f:
f.write(base64.b64decode(png.stdout))
finally:
sandbox.kill()
```
## Install and drive your app
Upload the APK into the sandbox with the filesystem API, install it with `adb install`, then launch it and interact with it. The `-g` flag grants the app's runtime permissions at install, so no permission dialog covers the app on first launch.
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { readFileSync } from 'node:fs'
import { Sandbox } from 'e2b'
const sandbox = await Sandbox.create('android-16', { timeoutMs: 10 * 60_000 })
const adb = 'adb -s 127.0.0.1:5555'
try {
await sandbox.commands.run('adb connect 127.0.0.1:5555')
await sandbox.files.write('/tmp/app.apk', readFileSync('app.apk').buffer)
await sandbox.commands.run(`${adb} install -g /tmp/app.apk`)
// Find the app's launcher activity and start it. Replace with your app's package name.
const activity = await sandbox.commands.run(
`${adb} shell cmd package resolve-activity --brief -c android.intent.category.LAUNCHER com.example.app | tail -1`,
)
await sandbox.commands.run(`${adb} shell am start -W -n ${activity.stdout.trim()}`)
// Tap, type, and read the UI tree.
await sandbox.commands.run(`${adb} shell input tap 360 640`)
await sandbox.commands.run(`${adb} shell input text hello`)
await sandbox.commands.run(`${adb} shell uiautomator dump /sdcard/ui.xml`)
const ui = await sandbox.commands.run(`${adb} shell cat /sdcard/ui.xml`)
console.log(ui.stdout)
} finally {
await sandbox.kill()
}
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
sandbox = Sandbox.create("android-16", timeout=10 * 60)
adb = "adb -s 127.0.0.1:5555"
try:
sandbox.commands.run("adb connect 127.0.0.1:5555")
with open("app.apk", "rb") as f:
sandbox.files.write("/tmp/app.apk", f)
sandbox.commands.run(f"{adb} install -g /tmp/app.apk")
# Find the app's launcher activity and start it. Replace with your app's package name.
activity = sandbox.commands.run(
f"{adb} shell cmd package resolve-activity --brief -c android.intent.category.LAUNCHER com.example.app | tail -1"
)
sandbox.commands.run(f"{adb} shell am start -W -n {activity.stdout.strip()}")
# Tap, type, and read the UI tree.
sandbox.commands.run(f"{adb} shell input tap 360 640")
sandbox.commands.run(f"{adb} shell input text hello")
sandbox.commands.run(f"{adb} shell uiautomator dump /sdcard/ui.xml")
ui = sandbox.commands.run(f"{adb} shell cat /sdcard/ui.xml")
print(ui.stdout)
finally:
sandbox.kill()
```
These are the `adb` commands an agent or test typically needs:
| Action | Command |
| - | - |
| Take a screenshot | `adb -s 127.0.0.1:5555 exec-out screencap -p` |
| Tap a point | `adb -s 127.0.0.1:5555 shell input tap ` |
| Swipe | `adb -s 127.0.0.1:5555 shell input swipe ` |
| Type text | `adb -s 127.0.0.1:5555 shell input text ` |
| Start an app | `adb -s 127.0.0.1:5555 shell am start -W -n /` |
| Press a key | `adb -s 127.0.0.1:5555 shell input keyevent KEYCODE_BACK` |
| Dump the UI hierarchy | `adb -s 127.0.0.1:5555 shell uiautomator dump /sdcard/ui.xml` |
| Read the device log | `adb -s 127.0.0.1:5555 logcat -d` |
An agent can send the screenshot and the UI hierarchy to a model, then act on its answer with taps, swipes, and key events.
## Pause and resume
By default, pausing saves the sandbox's memory as well as its filesystem. When you resume it with `connect()`, Android is still booted, installed apps are still installed, and the app that was in the foreground is still open. See [Sandbox persistence](/sandbox/persistence) for how pausing works.
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
await sandbox.pause()
// Later
const resumed = await Sandbox.connect(sandbox.sandboxId)
await resumed.commands.run('adb -s 127.0.0.1:5555 shell getprop sys.boot_completed') // 1
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
sandbox.pause()
# Later
resumed = Sandbox.connect(sandbox.sandbox_id)
resumed.commands.run("adb -s 127.0.0.1:5555 shell getprop sys.boot_completed") # 1
```
## What's in the sandbox
* Android 16 (API level 36), an x86\_64 AOSP image, running in a Docker container named `android`.
* `adb` connected to Android at `127.0.0.1:5555`.
* A 720x1280 display at 320 dpi, rendered in software (SwiftShader). There is no GPU.
* 8 vCPUs and 4 GB of RAM, set by the template.
* The start script's log at `/var/log/android-start.log`. Android's own log is in `logcat`.
## Known limitations
* **No Google Play Services or Play Store.** The image is plain AOSP, so apps that depend on Play Services, or that require the device to pass Play Integrity checks, do not work.
* **x86\_64 only.** An app that ships native libraries needs x86\_64 builds of them. An app that ships only arm64 native libraries installs, but fails when it loads those libraries.
* **64-bit only.** The image supports no 32-bit ABIs.
* **Software rendering.** Graphics-heavy apps and games run slower than on a phone with a GPU.
* **Use the template as is.** Android does not start in a custom template built on `android-16`, so its build fails. Install your apps at runtime with `adb install` instead.
# GitHub Actions CI/CD
Source: https://docs.e2b.dev/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://x-access-token:$GITHUB_TOKEN@github.com/${process.env.PR_REPO}.git`
await sandbox.commands.run(
`git clone --depth 1 --branch ${process.env.PR_BRANCH} ${repoUrl} /home/user/repo`,
{ envs: { GITHUB_TOKEN: process.env.GITHUB_TOKEN } }
)
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://x-access-token:$GITHUB_TOKEN@github.com/{os.environ['PR_REPO']}.git"
sandbox.commands.run(
f"git clone --depth 1 --branch {os.environ['PR_BRANCH']} {repo_url} /home/user/repo",
envs={"GITHUB_TOKEN": os.environ["GITHUB_TOKEN"]},
)
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** — `commands.run()` runs `git clone` inside the sandbox, checking out the PR branch with `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](/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
Integrate AI models with sandboxes using tool calling
Build reproducible sandbox environments for your pipelines
# Coding Agents
Source: https://docs.e2b.dev/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](/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](/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](/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 — run it in a sandbox, or use E2B sandboxes as remote workspaces
Build custom TypeScript coding agents with E2B-backed workspaces
## Related Guides
Pause and resume sandboxes to preserve state
Build your own sandbox templates with custom tools and dependencies
# Computer use
Source: https://docs.e2b.dev/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://developers.openai.com/api/docs/guides/tools-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](/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
# Run Kubernetes with k3s
Source: https://docs.e2b.dev/use-cases/k3s
Create a disposable Kubernetes cluster for agents, CI, previews, and integration testing.
The `e2b/k3s` template turns an E2B Sandbox into a preconfigured, single-node [k3s](https://k3s.io) cluster. `kubectl`, cluster DNS, networking, storage, and the default k8s components are configured when the sandbox starts.
Use it when an agent or CI job needs a single-node Kubernetes cluster without provisioning or sharing a permanent cluster.
## Quickstart
Create and connect to a k3s sandbox from the CLI:
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
e2b sbx create e2b/k3s
```
`kubectl` is already configured. On a fresh or resumed sandbox, wait for the node and cluster DNS objects to appear and become ready before deploying:
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
timeout 120s sh -c 'until kubectl get node -o name | grep -q .; do sleep 1; done'
kubectl wait --for=condition=Ready node --all --timeout=120s
timeout 120s sh -c 'until kubectl get pod --namespace=kube-system --selector=k8s-app=kube-dns -o name | grep -q .; do sleep 1; done'
kubectl wait --for=condition=Ready pod \
--namespace=kube-system \
--selector=k8s-app=kube-dns \
--timeout=120s
kubectl get nodes
kubectl get pods --all-namespaces
```
You can also create the sandbox with the standard E2B SDK:
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
// One timeout for the sandbox and for every command run inside it.
const TIMEOUT_MS = 15 * 60_000
const sandbox = await Sandbox.create('e2b/k3s', {
timeoutMs: TIMEOUT_MS,
})
try {
await sandbox.commands.run(
`
set -euo pipefail
timeout 120s sh -c 'until kubectl get node -o name | grep -q .; do sleep 1; done'
kubectl wait --for=condition=Ready node --all --timeout=120s
timeout 120s sh -c 'until kubectl get pod --namespace=kube-system --selector=k8s-app=kube-dns -o name | grep -q .; do sleep 1; done'
kubectl wait --for=condition=Ready pod \
--namespace=kube-system \
--selector=k8s-app=kube-dns \
--timeout=120s
`,
{ timeoutMs: TIMEOUT_MS },
)
const nodes = await sandbox.commands.run('kubectl get nodes -o wide', {
timeoutMs: TIMEOUT_MS,
})
console.log(nodes.stdout)
} finally {
await sandbox.kill()
}
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
# One timeout for the sandbox and for every command run inside it.
TIMEOUT = 15 * 60
sandbox = Sandbox.create("e2b/k3s", timeout=TIMEOUT)
try:
sandbox.commands.run(
"""
set -euo pipefail
timeout 120s sh -c 'until kubectl get node -o name | grep -q .; do sleep 1; done'
kubectl wait --for=condition=Ready node --all --timeout=120s
timeout 120s sh -c 'until kubectl get pod --namespace=kube-system --selector=k8s-app=kube-dns -o name | grep -q .; do sleep 1; done'
kubectl wait --for=condition=Ready pod \
--namespace=kube-system \
--selector=k8s-app=kube-dns \
--timeout=120s
""",
timeout=TIMEOUT,
)
nodes = sandbox.commands.run("kubectl get nodes -o wide", timeout=TIMEOUT)
print(nodes.stdout)
finally:
sandbox.kill()
```
## Deploy and preview an application
This example creates an nginx Deployment and Service, forwards the Service to a sandbox port, and prints an E2B URL that you can open in a browser.
Every command that has to stay alive for the whole session uses the same timeout as the sandbox, including the background port-forward. A command started with a shorter timeout is terminated when that timeout expires, which would close the forwarded port while the sandbox is still running.
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
// One timeout for the sandbox and for every command run inside it,
// including the background port-forward that serves the preview URL.
const TIMEOUT_MS = 15 * 60_000
const sandbox = await Sandbox.create('e2b/k3s', {
timeoutMs: TIMEOUT_MS,
})
await sandbox.commands.run(
`
set -euo pipefail
timeout 120s sh -c 'until kubectl get node -o name | grep -q .; do sleep 1; done'
kubectl wait --for=condition=Ready node --all --timeout=120s
timeout 120s sh -c 'until kubectl get pod --namespace=kube-system --selector=k8s-app=kube-dns -o name | grep -q .; do sleep 1; done'
kubectl wait --for=condition=Ready pod \
--namespace=kube-system \
--selector=k8s-app=kube-dns \
--timeout=120s
kubectl create deployment web --image=nginx:alpine
kubectl scale deployment web --replicas=2
kubectl expose deployment web --port=80
kubectl rollout status deployment/web --timeout=180s
`,
{ timeoutMs: TIMEOUT_MS },
)
await sandbox.commands.run(
'kubectl port-forward --address=0.0.0.0 service/web 8080:80',
{ background: true, timeoutMs: TIMEOUT_MS },
)
await sandbox.commands.run(
'until curl -fsS http://127.0.0.1:8080 >/dev/null; do sleep 1; done',
{ timeoutMs: TIMEOUT_MS },
)
const url = `https://${sandbox.getHost(8080)}`
console.log(`Open the application: ${url}`)
console.log(`Stop it with: e2b sandbox kill ${sandbox.sandboxId}`)
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
# One timeout for the sandbox and for every command run inside it,
# including the background port-forward that serves the preview URL.
TIMEOUT = 15 * 60
sandbox = Sandbox.create("e2b/k3s", timeout=TIMEOUT)
sandbox.commands.run(
"""
set -euo pipefail
timeout 120s sh -c 'until kubectl get node -o name | grep -q .; do sleep 1; done'
kubectl wait --for=condition=Ready node --all --timeout=120s
timeout 120s sh -c 'until kubectl get pod --namespace=kube-system --selector=k8s-app=kube-dns -o name | grep -q .; do sleep 1; done'
kubectl wait --for=condition=Ready pod \
--namespace=kube-system \
--selector=k8s-app=kube-dns \
--timeout=120s
kubectl create deployment web --image=nginx:alpine
kubectl scale deployment web --replicas=2
kubectl expose deployment web --port=80
kubectl rollout status deployment/web --timeout=180s
""",
timeout=TIMEOUT,
)
sandbox.commands.run(
"kubectl port-forward --address=0.0.0.0 service/web 8080:80",
background=True,
timeout=TIMEOUT,
)
sandbox.commands.run(
"until curl -fsS http://127.0.0.1:8080 >/dev/null; do sleep 1; done",
timeout=TIMEOUT,
)
url = f"https://{sandbox.get_host(8080)}"
print(f"Open the application: {url}")
print(f"Stop it with: e2b sandbox kill {sandbox.sandbox_id}")
```
The script keeps running while the port-forward is open, and the sandbox stays available for up to 15 minutes so you can open the URL. Stop the script with `Ctrl+C` when you are done watching it, and remove the sandbox with the command it printed:
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
e2b sandbox kill
```
Stopping the script on its own does not delete the sandbox: it keeps serving the URL until you kill it or the 15-minute timeout expires.
To verify in-cluster DNS and ClusterIP routing, run a client pod against the Service:
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
kubectl run cluster-check \
--rm -i \
--restart=Never \
--image=curlimages/curl:latest \
-- curl -fsS http://web
```
## Check k3s status and logs
k3s runs as a systemd service with automatic restart on crash. Use `sudo`: the sandbox has no user D-Bus session, so unprivileged `systemctl` and `journalctl` cannot connect.
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
sudo systemctl status k3s # unit state
sudo journalctl -u k3s -f # server logs
kubectl get --raw='/readyz?verbose' | tail -5
kubectl get pods --all-namespaces
```
Server flags live in `/etc/rancher/k3s/config.yaml`; edit the file and `sudo systemctl restart k3s` to apply. Workload logs work as usual through `kubectl logs`.
## Where to use it
* Give a coding agent an isolated cluster for a repository that expects Kubernetes.
* Validate Helm charts, operators, CRDs, manifests, Services, and DNS in CI.
* Create a cluster per pull request for integration tests or browser previews.
* Reproduce Kubernetes issues without provisioning EKS, GKE, or AKS.
* Test untrusted workloads without sharing a staging cluster.
The useful production pattern is **cluster per task**: create a sandbox, deploy and test the workload, collect the results, and remove the sandbox. Promote the same images, charts, and manifests to your managed production cluster after validation.
The agent, CI job, or preview service gets its own Firecracker microVM and Kubernetes control plane.
Apply the same container images, Helm charts, CRDs, or manifests that will be promoted later.
Exercise Pods, Services, DNS, storage, and browser-visible endpoints without affecting a shared cluster.
Send validated artifacts to the production cluster, save any required test output, and kill the sandbox.
## What is tested and supported
The template runs stock k3s with Flannel's default VXLAN backend. These workflows are validated end-to-end on every release:
* Deployments, Services with load balancing across replicas (kube-proxy iptables and nftables modes), and session affinity.
* Ingress (Traefik), NodePort, LoadBalancer (ServiceLB), and `hostPort`.
* NetworkPolicy, both ingress and egress, with the built-in controller.
* StatefulSets with local-path PersistentVolumeClaims, Jobs, CronJobs, and HPA with metrics-server.
* Alternative CNIs that use VXLAN, such as Calico with `CALICO_IPV4POOL_VXLAN=Always`.
* Sandbox pause and resume: the cluster comes back immediately, the clock is corrected, and CronJobs keep their schedule.
## Known limitations
* The template runs one Kubernetes node in one sandbox. It does not provide high availability, and multi-node clusters across sandboxes are not supported.
* A sandbox is disposable. Do not treat its local Kubernetes storage as a durable system of record.
* Cilium and other eBPF dataplanes do not work: the sandbox kernel has no BPF JIT.
* Service meshes with iptables interception (Istio, Linkerd) do not work: the kernel lacks the `owner` and `TPROXY` netfilter extensions their sidecars need.
* Calico's default IPIP mode is not supported; use its VXLAN mode. GRE tunnels and SCTP Services are also unavailable.
* IPVS kube-proxy mode and nested-virtualization runtimes such as Kata Containers or KubeVirt are not supported.
* `kubectl port-forward service/` connects to one Pod selected when the forward starts; use in-cluster requests to exercise load balancing across replicas.
For production application hosting, use a managed or otherwise highly available Kubernetes cluster with durable storage, backups, monitoring, and load balancing. Use E2B k3s to develop and validate what you deploy there.
# Cloud browser
Source: https://docs.e2b.dev/use-cases/remote-browser
Use Kernel cloud browsers with E2B sandboxes to give your agents access to the internet.
This guide covers **cloud browsers** powered by [Kernel](https://www.kernel.sh/) — cloud Chromium instances your code controls via [CDP](https://chromedevtools.github.io/devtools-protocol/) or the [Kernel SDK](https://www.kernel.sh/docs/sdk/overview). For **local browser automation** using a virtual desktop, see [Computer use](/use-cases/computer-use).
Cloud browsers run on Kernel's managed infrastructure, not inside your sandbox. This gives you sub-second browser startup, [persistent sessions you can pause and resume](https://www.onkernel.com/docs/browsers/persistence) across hours or days, and reusable browser state. Your agent connects to them over the network via CDP, BiDi, or their CUA API.
Kernel handles CAPTCHA solving, residential proxies, authentication, and observability out of the box.
## Prerequisites
* An E2B API key
* A [Kernel API key](https://dashboard.onkernel.com/api-keys)
* Python 3.10+ / Node.js 18+
```bash JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
npm i e2b @onkernel/sdk playwright-core
```
```bash Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
pip install e2b kernel playwright
```
Set your keys in the environment:
```bash .env theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
E2B_API_KEY=e2b_***
KERNEL_API_KEY=kernel_***
```
E2B provides a pre-built sandbox template with the Kernel SDK and Playwright already installed:
| Template | What's included | Best for |
| - | - | - |
| `kernel-browser` | Kernel SDK, Playwright, Browser Use | Screenshots, scraping, app previews, autonomous agents |
## Examples
Here are three common patterns for using cloud browsers with E2B sandboxes.
Deploy a web app in a sandbox, screenshot every route
Let an LLM autonomously browse and extract data
Watch the browser in real time via Kernel's live view
***
## Screenshot app endpoints
Deploy a web app inside an E2B sandbox, get a public URL, then use a Kernel cloud browser to screenshot every route.
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
// A minimal FastAPI app with three routes to screenshot
const FASTAPI_APP = `
from fastapi import FastAPI
from fastapi.responses import HTMLResponse
app = FastAPI()
@app.get("/")
def home():
return HTMLResponse("Home
Welcome to the app.
")
@app.get("/about")
def about():
return HTMLResponse("About
About this app.
")
@app.get("/dashboard")
def dashboard():
return HTMLResponse("Dashboard
Your dashboard.
")
`
const sandbox = await Sandbox.create('kernel-browser', {
envs: { KERNEL_API_KEY: process.env.KERNEL_API_KEY! },
timeoutMs: 300_000,
})
await sandbox.files.write('/home/user/app.py', FASTAPI_APP)
await sandbox.commands.run(
'pip install fastapi uvicorn',
{ timeoutMs: 60_000 },
)
await sandbox.commands.run(
'uvicorn app:app --host 0.0.0.0 --port 8000',
{ background: true, cwd: '/home/user' },
)
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import os
from e2b import Sandbox
# A minimal FastAPI app with three routes to screenshot
FASTAPI_APP = """
from fastapi import FastAPI
from fastapi.responses import HTMLResponse
app = FastAPI()
@app.get("/")
def home():
return HTMLResponse("Home
Welcome to the app.
")
@app.get("/about")
def about():
return HTMLResponse("About
About this app.
")
@app.get("/dashboard")
def dashboard():
return HTMLResponse("Dashboard
Your dashboard.
")
"""
sandbox = Sandbox.create(
"kernel-browser",
envs={"KERNEL_API_KEY": os.environ["KERNEL_API_KEY"]},
timeout=300,
)
sandbox.files.write("/home/user/app.py", FASTAPI_APP)
sandbox.commands.run(
"pip install fastapi uvicorn",
timeout=60,
)
sandbox.commands.run(
"uvicorn app:app --host 0.0.0.0 --port 8000",
background=True,
cwd="/home/user",
)
```
E2B exposes any sandbox port as a public HTTPS endpoint. Write a browsing script into the sandbox that creates a Kernel browser and screenshots each route.
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
const host = sandbox.getHost(8000)
const appUrl = `https://${host}`
const BROWSE_SCRIPT = `
import sys
from kernel import Kernel
from playwright.sync_api import sync_playwright
app_url = "${appUrl}"
routes = ["/", "/about", "/dashboard"]
kernel = Kernel()
kb = kernel.browsers.create()
with sync_playwright() as pw:
browser = pw.chromium.connect_over_cdp(kb.cdp_ws_url)
page = browser.new_page()
page.set_viewport_size({"width": 1280, "height": 720})
for route in routes:
page.goto(f"{app_url}{route}", wait_until="networkidle")
name = "home" if route == "/" else route.strip("/")
page.screenshot(path=f"/home/user/{name}.png")
print(f"Captured {route}")
browser.close()
`
await sandbox.files.write('/home/user/browse.py', BROWSE_SCRIPT)
const result = await sandbox.commands.run(
'python3 /home/user/browse.py',
{ timeoutMs: 60_000 },
)
console.log(result.stdout)
await sandbox.kill()
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
host = sandbox.get_host(8000)
app_url = f"https://{host}"
BROWSE_SCRIPT = f'''
from kernel import Kernel
from playwright.sync_api import sync_playwright
app_url = "{app_url}"
routes = ["/", "/about", "/dashboard"]
kernel = Kernel()
kb = kernel.browsers.create()
with sync_playwright() as pw:
browser = pw.chromium.connect_over_cdp(kb.cdp_ws_url)
page = browser.new_page()
page.set_viewport_size({{"width": 1280, "height": 720}})
for route in routes:
page.goto(f"{{app_url}}{{route}}", wait_until="networkidle")
name = "home" if route == "/" else route.strip("/")
page.screenshot(path=f"/home/user/{{name}}.png")
print(f"Captured {{route}}")
browser.close()
'''
sandbox.files.write("/home/user/browse.py", BROWSE_SCRIPT)
result = sandbox.commands.run("python3 /home/user/browse.py", timeout=60)
print(result.stdout)
sandbox.kill()
```
Full example:
```typescript JavaScript & TypeScript expandable theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
// A minimal FastAPI app with three routes to screenshot
const FASTAPI_APP = `
from fastapi import FastAPI
from fastapi.responses import HTMLResponse
app = FastAPI()
@app.get("/")
def home():
return HTMLResponse("Home
Welcome to the app.
")
@app.get("/about")
def about():
return HTMLResponse("About
About this app.
")
@app.get("/dashboard")
def dashboard():
return HTMLResponse("Dashboard
Your dashboard.
")
`
// 1. Create the sandbox and start the app
const sandbox = await Sandbox.create('kernel-browser', {
envs: { KERNEL_API_KEY: process.env.KERNEL_API_KEY! },
timeoutMs: 300_000,
})
await sandbox.files.write('/home/user/app.py', FASTAPI_APP)
await sandbox.commands.run(
'pip install fastapi uvicorn',
{ timeoutMs: 60_000 },
)
await sandbox.commands.run(
'uvicorn app:app --host 0.0.0.0 --port 8000',
{ background: true, cwd: '/home/user' },
)
// 2. Screenshot each route with a Kernel browser
const host = sandbox.getHost(8000)
const appUrl = `https://${host}`
const BROWSE_SCRIPT = `
import sys
from kernel import Kernel
from playwright.sync_api import sync_playwright
app_url = "${appUrl}"
routes = ["/", "/about", "/dashboard"]
kernel = Kernel()
kb = kernel.browsers.create()
with sync_playwright() as pw:
browser = pw.chromium.connect_over_cdp(kb.cdp_ws_url)
page = browser.new_page()
page.set_viewport_size({"width": 1280, "height": 720})
for route in routes:
page.goto(f"{app_url}{route}", wait_until="networkidle")
name = "home" if route == "/" else route.strip("/")
page.screenshot(path=f"/home/user/{name}.png")
print(f"Captured {route}")
browser.close()
`
await sandbox.files.write('/home/user/browse.py', BROWSE_SCRIPT)
const result = await sandbox.commands.run(
'python3 /home/user/browse.py',
{ timeoutMs: 60_000 },
)
console.log(result.stdout)
await sandbox.kill()
```
```python Python expandable theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import os
from e2b import Sandbox
# A minimal FastAPI app with three routes to screenshot
FASTAPI_APP = """
from fastapi import FastAPI
from fastapi.responses import HTMLResponse
app = FastAPI()
@app.get("/")
def home():
return HTMLResponse("Home
Welcome to the app.
")
@app.get("/about")
def about():
return HTMLResponse("About
About this app.
")
@app.get("/dashboard")
def dashboard():
return HTMLResponse("Dashboard
Your dashboard.
")
"""
# 1. Create the sandbox and start the app
sandbox = Sandbox.create(
"kernel-browser",
envs={"KERNEL_API_KEY": os.environ["KERNEL_API_KEY"]},
timeout=300,
)
sandbox.files.write("/home/user/app.py", FASTAPI_APP)
sandbox.commands.run(
"pip install fastapi uvicorn",
timeout=60,
)
sandbox.commands.run(
"uvicorn app:app --host 0.0.0.0 --port 8000",
background=True,
cwd="/home/user",
)
# 2. Screenshot each route with a Kernel browser
host = sandbox.get_host(8000)
app_url = f"https://{host}"
BROWSE_SCRIPT = f'''
from kernel import Kernel
from playwright.sync_api import sync_playwright
app_url = "{app_url}"
routes = ["/", "/about", "/dashboard"]
kernel = Kernel()
kb = kernel.browsers.create()
with sync_playwright() as pw:
browser = pw.chromium.connect_over_cdp(kb.cdp_ws_url)
page = browser.new_page()
page.set_viewport_size({{"width": 1280, "height": 720}})
for route in routes:
page.goto(f"{{app_url}}{{route}}", wait_until="networkidle")
name = "home" if route == "/" else route.strip("/")
page.screenshot(path=f"/home/user/{{name}}.png")
print(f"Captured {{route}}")
browser.close()
'''
sandbox.files.write("/home/user/browse.py", BROWSE_SCRIPT)
result = sandbox.commands.run("python3 /home/user/browse.py", timeout=60)
print(result.stdout)
sandbox.kill()
```
***
## Agent data extraction
Use [Browser Use](https://docs.browser-use.com/) to let an LLM autonomously browse a website and extract data. The agent sees the page via screenshots and decides what to click, type, and navigate.
This requires an additional LLM API key:
```bash .env theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
ANTHROPIC_API_KEY=sk-ant-***
```
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const sandbox = await Sandbox.create('kernel-browser', {
envs: {
KERNEL_API_KEY: process.env.KERNEL_API_KEY!,
ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY!,
},
timeoutMs: 300_000,
})
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import os
from e2b import Sandbox
sandbox = Sandbox.create(
"kernel-browser",
envs={
"KERNEL_API_KEY": os.environ["KERNEL_API_KEY"],
"ANTHROPIC_API_KEY": os.environ["ANTHROPIC_API_KEY"],
},
timeout=300,
)
```
The agent script runs inside the sandbox. Browser Use and its dependencies come pre-installed in the `kernel-browser` template. The script creates a Kernel browser, connects Browser Use, and completes the task autonomously.
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
const AGENT_SCRIPT = `
import asyncio
from kernel import Kernel
from browser_use import Agent, Browser, ChatAnthropic
async def main():
kernel = Kernel()
kb = kernel.browsers.create()
browser = Browser(cdp_url=kb.cdp_ws_url)
agent = Agent(
task="Go to https://news.ycombinator.com, find the top 5 stories, and return their titles and point counts as JSON",
llm=ChatAnthropic(model="claude-sonnet-4"),
browser=browser,
)
result = await agent.run()
print(result)
asyncio.run(main())
`
await sandbox.files.write('/home/user/agent_task.py', AGENT_SCRIPT)
const result = await sandbox.commands.run(
'python3 /home/user/agent_task.py',
{ timeoutMs: 180_000 },
)
console.log(result.stdout)
await sandbox.kill()
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
AGENT_SCRIPT = '''
import asyncio
from kernel import Kernel
from browser_use import Agent, Browser, ChatAnthropic
async def main():
kernel = Kernel()
kb = kernel.browsers.create()
browser = Browser(cdp_url=kb.cdp_ws_url)
agent = Agent(
task="Go to https://news.ycombinator.com, find the top 5 stories, and return their titles and point counts as JSON",
llm=ChatAnthropic(model="claude-sonnet-4"),
browser=browser,
)
result = await agent.run()
print(result)
asyncio.run(main())
'''
sandbox.files.write("/home/user/agent_task.py", AGENT_SCRIPT)
result = sandbox.commands.run("python3 /home/user/agent_task.py", timeout=180)
print(result.stdout)
sandbox.kill()
```
Full example:
```typescript JavaScript & TypeScript expandable theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
// 1. Create the sandbox with API keys
const sandbox = await Sandbox.create('kernel-browser', {
envs: {
KERNEL_API_KEY: process.env.KERNEL_API_KEY!,
ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY!,
},
timeoutMs: 300_000,
})
// 2. Write and run the Browser Use agent
const AGENT_SCRIPT = `
import asyncio
from kernel import Kernel
from browser_use import Agent, Browser, ChatAnthropic
async def main():
kernel = Kernel()
kb = kernel.browsers.create()
browser = Browser(cdp_url=kb.cdp_ws_url)
agent = Agent(
task="Go to https://news.ycombinator.com, find the top 5 stories, and return their titles and point counts as JSON",
llm=ChatAnthropic(model="claude-sonnet-4"),
browser=browser,
)
result = await agent.run()
print(result)
asyncio.run(main())
`
await sandbox.files.write('/home/user/agent_task.py', AGENT_SCRIPT)
const result = await sandbox.commands.run(
'python3 /home/user/agent_task.py',
{ timeoutMs: 180_000 },
)
console.log(result.stdout)
await sandbox.kill()
```
```python Python expandable theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import os
from e2b import Sandbox
# 1. Create the sandbox with API keys
sandbox = Sandbox.create(
"kernel-browser",
envs={
"KERNEL_API_KEY": os.environ["KERNEL_API_KEY"],
"ANTHROPIC_API_KEY": os.environ["ANTHROPIC_API_KEY"],
},
timeout=300,
)
# 2. Write and run the Browser Use agent
AGENT_SCRIPT = '''
import asyncio
from kernel import Kernel
from browser_use import Agent, Browser, ChatAnthropic
async def main():
kernel = Kernel()
kb = kernel.browsers.create()
browser = Browser(cdp_url=kb.cdp_ws_url)
agent = Agent(
task="Go to https://news.ycombinator.com, find the top 5 stories, and return their titles and point counts as JSON",
llm=ChatAnthropic(model="claude-sonnet-4"),
browser=browser,
)
result = await agent.run()
print(result)
asyncio.run(main())
'''
sandbox.files.write("/home/user/agent_task.py", AGENT_SCRIPT)
result = sandbox.commands.run("python3 /home/user/agent_task.py", timeout=180)
print(result.stdout)
sandbox.kill()
```
***
## Live browser preview
Kernel provides a live view URL for every browser session — you can watch the browser in real time or embed it in your app. This is useful for debugging, demos, or letting users see what the agent is doing.
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const sandbox = await Sandbox.create('kernel-browser', {
envs: { KERNEL_API_KEY: process.env.KERNEL_API_KEY! },
timeoutMs: 300_000,
})
const LIVE_VIEW_SCRIPT = `
from kernel import Kernel
kernel = Kernel()
browser = kernel.browsers.create()
# Print the live view URL — accessible from any browser
print(browser.browser_live_view_url)
`
await sandbox.files.write('/home/user/live_view.py', LIVE_VIEW_SCRIPT)
const result = await sandbox.commands.run(
'python3 /home/user/live_view.py',
{ timeoutMs: 30_000 },
)
const liveViewUrl = result.stdout.trim()
console.log('Watch the browser:', liveViewUrl)
// Embed in your app as an iframe
//
// Read-only mode (no mouse/keyboard interaction)
// liveViewUrl + '?readOnly=true'
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import os
from e2b import Sandbox
sandbox = Sandbox.create(
"kernel-browser",
envs={"KERNEL_API_KEY": os.environ["KERNEL_API_KEY"]},
timeout=300,
)
LIVE_VIEW_SCRIPT = '''
from kernel import Kernel
kernel = Kernel()
browser = kernel.browsers.create()
# Print the live view URL — accessible from any browser
print(browser.browser_live_view_url)
'''
sandbox.files.write("/home/user/live_view.py", LIVE_VIEW_SCRIPT)
result = sandbox.commands.run("python3 /home/user/live_view.py", timeout=30)
live_view_url = result.stdout.strip()
print("Watch the browser:", live_view_url)
# Embed in your app as an iframe
#
# Read-only mode (no mouse/keyboard interaction)
# live_view_url + '?readOnly=true'
```
The live view URL stays active until the browser is deleted or times out. For more details, see the [Kernel live view documentation](https://www.kernel.sh/docs/browsers/live-view).
## Related guides
Local browser automation with virtual desktops
Create, manage, and control sandbox lifecycle
Run terminal commands inside the sandbox
# Overview
Source: https://docs.e2b.dev/volumes
Persistent storage that exists independently of sandboxes and can be mounted across multiple sandboxes.
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.](/volumes/manage)
* [Mount volumes to sandboxes.](/volumes/mount)
* [Read and write files to a volume.](/volumes/read-write)
* [Get file and directory metadata.](/volumes/info)
* [Upload data to a volume.](/volumes/upload)
* [Download data from a volume.](/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,
},
)
```
## Known limitations
Volumes are in private beta and have limitations worth knowing about before you build on them, including file locking, performance on many small files, and file ownership. See [Volumes beta limitations](/faq/volumes-beta-limitations).
# Download data from volume
Source: https://docs.e2b.dev/volumes/download
Download files from a volume to your local filesystem with the E2B SDK.
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://docs.e2b.dev/volumes/info
Get metadata, check existence, and update permissions for files and directories in a volume.
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://docs.e2b.dev/volumes/manage
Create, connect to, list, inspect, and destroy volumes with the E2B SDK.
## 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
```
# Migrating data between volumes
Source: https://docs.e2b.dev/volumes/migrate
Copy all data from one E2B volume to another by mounting both into a sandbox and using rsync.
When you need to move data from an old volume to a new one, the simplest approach is to mount both volumes into a single sandbox and copy the files across. This page walks through a script that does exactly that: it spins up a sandbox from the **base** template, mounts a source and a destination volume, and copies all data from source to destination.
* The **source** volume must already exist. If it doesn't, the script fails.
* The **destination** volume is reused if it already exists, or created if it doesn't.
## Prerequisites
You need an E2B account with your API key set as `E2B_API_KEY` in your environment, and the E2B SDK installed.
```bash JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
npm install e2b
```
```bash Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
pip install e2b
```
## The script
The script first resolves both volumes by name, connecting to the source and connecting to (or creating) the destination. It then mounts both into a sandbox and uses [rsync](https://rsync.samba.org/) to copy the data across. Reading the volume type from the token is optional, but it's a handy sanity check that you're copying between the volumes you expect.
Save this as `copy-volume.mts` (JavaScript & TypeScript) or `copy_volume.py` (Python):
```ts JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox, Volume } from 'e2b'
const [sourceName, destName] = process.argv.slice(2)
if (!sourceName || !destName) {
throw new Error('Usage: copy-volume ')
}
async function findVolumeByName(name: string) {
const volumes = await Volume.list()
return volumes.find((v) => v.name === name) ?? null
}
const sourceInfo = await findVolumeByName(sourceName)
if (!sourceInfo) {
throw new Error(`Source volume "${sourceName}" does not exist`)
}
const sourceVolume = await Volume.connect(sourceInfo.volumeId)
const destInfo = await findVolumeByName(destName)
const destVolume = destInfo
? await Volume.connect(destInfo.volumeId)
: await Volume.create(destName)
function voltype(token: string): string {
// JWT payloads are base64url and often omit padding, so normalize before decoding
let segment = token.split('.')[1].replace(/-/g, '+').replace(/_/g, '/')
segment += '='.repeat((4 - (segment.length % 4)) % 4)
const payload = JSON.parse(atob(segment))
return payload.voltype
}
console.log(`Source volume type: ${voltype(sourceVolume.token)}`)
console.log(`Destination volume type: ${voltype(destVolume.token)}`)
const sandbox = await Sandbox.create({
volumeMounts: {
'/mnt/source': sourceVolume,
'/mnt/dest': destVolume,
},
})
try {
const install = await sandbox.commands.run(
'sudo apt-get update && sudo apt-get install -y rsync',
)
if (install.exitCode !== 0) {
throw new Error(`rsync install failed: ${install.stderr}`)
}
const result = await sandbox.commands.run('rsync -a /mnt/source/ /mnt/dest/')
if (result.exitCode !== 0) {
throw new Error(`Copy failed: ${result.stderr}`)
}
const listing = await sandbox.commands.run('ls -la /mnt/dest')
console.log(listing.stdout)
} finally {
await sandbox.kill()
}
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import base64
import json
import sys
from e2b import Sandbox, Volume
args = sys.argv[1:]
if len(args) != 2:
raise SystemExit('Usage: copy_volume ')
source_name, dest_name = args
def find_volume_by_name(name):
return next((v for v in Volume.list() if v.name == name), None)
def voltype(token):
payload = token.split('.')[1]
payload += '=' * (-len(payload) % 4) # JWT segments omit base64 padding
return json.loads(base64.urlsafe_b64decode(payload))['voltype']
source_info = find_volume_by_name(source_name)
if source_info is None:
raise SystemExit(f'Source volume "{source_name}" does not exist')
source_volume = Volume.connect(source_info.volume_id)
dest_info = find_volume_by_name(dest_name)
dest_volume = (
Volume.connect(dest_info.volume_id) if dest_info else Volume.create(dest_name)
)
print(f'Source volume type: {voltype(source_volume.token)}')
print(f'Destination volume type: {voltype(dest_volume.token)}')
sandbox = Sandbox.create(
volume_mounts={
'/mnt/source': source_volume,
'/mnt/dest': dest_volume,
},
)
try:
install = sandbox.commands.run(
'sudo apt-get update && sudo apt-get install -y rsync',
)
if install.exit_code != 0:
raise RuntimeError(f'rsync install failed: {install.stderr}')
result = sandbox.commands.run('rsync -a /mnt/source/ /mnt/dest/')
if result.exit_code != 0:
raise RuntimeError(f'Copy failed: {result.stderr}')
listing = sandbox.commands.run('ls -la /mnt/dest')
print(listing.stdout)
finally:
sandbox.kill()
```
## Usage
Run the script with the source and destination volume names. For example, to copy everything from `prod-data` into `prod-data-backup`:
```bash JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
export E2B_API_KEY=your-api-key
npx tsx copy-volume.mts prod-data prod-data-backup
```
```bash Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
export E2B_API_KEY=your-api-key
python copy_volume.py prod-data prod-data-backup
```
On success it prints a listing of the destination so you can confirm the data landed. The sandbox is always shut down afterward, but both volumes (and their data) persist.
# Mounting volumes
Source: https://docs.e2b.dev/volumes/mount
Mount one or more volumes to a sandbox at custom paths when creating it.
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://docs.e2b.dev/volumes/read-write
Read, write, list, and remove files and directories in a volume with the SDK.
## 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://docs.e2b.dev/volumes/upload
Upload single files or entire directories from your local filesystem to a volume.
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)
```
# Agno
Source: https://docs.e2b.dev/agents/agno
Run Agno agents with E2B's secure Python sandbox.
[Agno](https://agno.com/) is a Python framework for building agents, teams, and workflows. Its official [`E2BTools`](https://docs.agno.com/tools/toolkits/others/e2b) toolkit gives an Agno agent an E2B sandbox for Python execution, file operations, commands, and web servers.
## Install the dependencies
Install Agno, the E2B Code Interpreter SDK, and the OpenAI provider used in the example:
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
uv pip install agno e2b_code_interpreter openai
```
Set the API keys for E2B and your model provider:
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
export E2B_API_KEY="e2b_..."
export OPENAI_API_KEY="..."
```
## Run an agent in an E2B sandbox
Create an `E2BTools` instance and pass it to the agent. The agent can use the toolkit to run Python code in an isolated E2B sandbox instead of on the machine running Agno.
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from agno.agent import Agent
from agno.models.openai import OpenAIResponses
from agno.tools.e2b import E2BTools
e2b_tools = E2BTools(
timeout=600,
sandbox_options={"allow_internet_access": False},
include_tools=["run_python_code"],
)
try:
agent = Agent(
name="Code Execution Sandbox",
id="e2b-sandbox",
model=OpenAIResponses(id="gpt-5.4-mini"),
tools=[e2b_tools],
markdown=True,
instructions=[
"Use the E2B sandbox to execute Python code for calculations.",
"Verify the result before responding.",
],
)
agent.print_response(
"Write Python code to generate the first 10 Fibonacci numbers."
)
finally:
e2b_tools.shutdown_sandbox()
```
## Choose an explicit tool allowlist
Without an explicit allowlist, `E2BTools` can expose tools for code execution, file operations, filesystem access, commands, internet access, and sandbox management. Start with only the capabilities the agent needs:
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
e2b_tools = E2BTools(
timeout=600,
sandbox_options={"allow_internet_access": False},
include_tools=[
"run_python_code",
"list_files",
"read_file_content",
"write_file_content",
],
)
```
An `include_tools` allowlist limits callable function names, not their arguments or paths. The complete toolkit includes host-to-sandbox and sandbox-to-host file-transfer helpers: `upload_file(file_path, sandbox_path=None)` accepts a local path on the machine running Agno, while `download_file_from_sandbox(sandbox_path, local_path=None)` can write sandbox content to a local path on that machine. Do not expose these helpers to an untrusted agent unless your application enforces an allowlist of host paths or provides a safe wrapper that fixes or validates every host path.
## Sandbox lifecycle
An `E2BTools` instance creates one sandbox when it is initialized and reuses it for subsequent tool calls. Set `timeout` to a bounded value appropriate for the workload, and call `shutdown_sandbox()` when the agent no longer needs the sandbox. If you use `include_tools`, keep `shutdown_sandbox` in the allowlist or call it directly from your application during cleanup.
Python code and commands run inside the E2B sandbox, but the toolkit also contains host-side file-transfer helpers. Treat model-generated code and tool output as untrusted data, do not expose host secrets or unrestricted host paths, validate outputs before passing them to trusted systems, and prefer an explicit `include_tools` allowlist.
The tool allowlist does not control outbound network access from code running in the sandbox. Set `sandbox_options={"allow_internet_access": False}` when the workload does not need the internet. When it does, enable access deliberately and apply [network restrictions](/network/internet-access).
## Learn more
* [Agno E2B toolkit documentation](https://docs.agno.com/tools/toolkits/others/e2b)
* [Agno E2B tools cookbook](https://github.com/agno-agi/agno/blob/main/cookbook/91_tools/e2b_tools.py)
* [E2B sandbox documentation](/sandbox)
# Amp
Source: https://docs.e2b.dev/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](/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. The sandbox isolates the agent from your host machine, but sandboxes can reach the open internet by default — restrict outbound traffic with [network rules](/network/internet-access). Amp uses its own API key from [ampcode.com/settings](https://ampcode.com/settings).
Auto-approving tool calls is contained by the sandbox: the agent cannot touch your host machine, local files, or credentials. It can still make outbound network requests — internet access is enabled by default. To limit where an auto-approved agent can connect, configure [outbound network rules](/network/internet-access) (`allowInternetAccess`, plus allow/deny lists by CIDR or hostname). Hostname rules apply to HTTP(S) traffic only; use CIDR rules for other protocols.
```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.commands.run(
'git clone --depth 1 https://x-access-token:$GITHUB_TOKEN@github.com/your-org/your-repo.git /home/user/repo',
{ envs: { GITHUB_TOKEN: process.env.GITHUB_TOKEN } }
)
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.commands.run(
"git clone --depth 1 https://x-access-token:$GITHUB_TOKEN@github.com/your-org/your-repo.git /home/user/repo",
envs={"GITHUB_TOKEN": os.environ["GITHUB_TOKEN"]},
)
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
Connect to the sandbox via SSH for interactive sessions
# Claude Code
Source: https://docs.e2b.dev/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](/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. The sandbox isolates the agent from your host machine, but sandboxes can reach the open internet by default — restrict outbound traffic with [network rules](/network/internet-access) if the agent processes untrusted input or handles secrets.
Auto-approving tool calls is contained by the sandbox: the agent cannot touch your host machine, local files, or credentials. It can still make outbound network requests — internet access is enabled by default. To limit where an auto-approved agent can connect, configure [outbound network rules](/network/internet-access) (`allowInternetAccess`, plus allow/deny lists by CIDR or hostname). Hostname rules apply to HTTP(S) traffic only; use CIDR rules for other protocols.
```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.commands.run(
'git clone --depth 1 https://x-access-token:$GITHUB_TOKEN@github.com/your-org/your-repo.git /home/user/repo',
{ envs: { GITHUB_TOKEN: process.env.GITHUB_TOKEN } }
)
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.commands.run(
"git clone --depth 1 https://x-access-token:$GITHUB_TOKEN@github.com/your-org/your-repo.git /home/user/repo",
envs={"GITHUB_TOKEN": os.environ["GITHUB_TOKEN"]},
)
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](/mcp-gateway) 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
# Claude Managed Agents
Source: https://docs.e2b.dev/agents/claude-managed-agents
Use E2B as the sandbox runtime for Claude Managed Agents self-hosted environments.
[Claude Managed Agents](https://platform.claude.com/docs/en/managed-agents/overview) can use a self-hosted environment when you want tool calls to run in your own infrastructure. Claude runs the agentic loop and reasoning process; E2B provides isolated sandbox infrastructure for the environment where tool calls execute. The reusable `E2B/claude-managed-agents-webhooks` template receives webhooks and routes each Claude Managed Agents session to a persistent E2B worker sandbox.
This separation is intentional for security: Claude decides what work to do, while the sandbox contains the execution environment, filesystem, tools, network access, and runtime logs.
For the full source, local setup scripts, and app-owned routing examples, see the [Claude Managed Agents cookbook](https://github.com/e2b-dev/e2b-cookbook/tree/main/examples/anthropic-managed-agents).
## Install dependencies
```bash JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
npm install e2b @anthropic-ai/sdk
```
```bash Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
pip install e2b anthropic
```
Export the values you will pass to the webhook sandbox:
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
# E2B dashboard API keys: https://console.e2b.dev/?tab=keys
export E2B_API_KEY="..."
# Claude Console API keys: https://console.anthropic.com/settings/keys
export ANTHROPIC_API_KEY="..."
# Claude Console environments: https://platform.claude.com/workspaces/default/environments
export ANTHROPIC_ENVIRONMENT_ID="..."
# Open the self-hosted environment in the Claude Console and click Generate environment key.
export ANTHROPIC_ENVIRONMENT_KEY="..."
```
## Start the webhook sandbox
Start the public template with auto-resume enabled. The template starts a webhook server on port `8000`.
Because the signing key only appears after you register a webhook endpoint, write the router config now and add the signing key to the same sandbox after registration.
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const sandbox = await Sandbox.create('E2B/claude-managed-agents-webhooks', {
lifecycle: { onTimeout: 'pause', autoResume: true },
})
await sandbox.files.write([
{
path: '/opt/anthropic-managed-agents-js/config/e2b-api-key',
data: `${process.env.E2B_API_KEY}\n`,
},
{
path: '/opt/anthropic-managed-agents-js/config/anthropic-api-key',
data: `${process.env.ANTHROPIC_API_KEY}\n`,
},
{
path: '/opt/anthropic-managed-agents-js/config/anthropic-environment-id',
data: `${process.env.ANTHROPIC_ENVIRONMENT_ID}\n`,
},
{
path: '/opt/anthropic-managed-agents-js/config/anthropic-environment-key',
data: `${process.env.ANTHROPIC_ENVIRONMENT_KEY}\n`,
},
])
console.log(`Webhook URL: https://${sandbox.getHost(8000)}/webhook`)
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import os
from e2b import Sandbox
sandbox = Sandbox.create(
"E2B/claude-managed-agents-webhooks",
lifecycle={"on_timeout": "pause", "auto_resume": True},
)
sandbox.files.write(
"/opt/anthropic-managed-agents-js/config/e2b-api-key",
f"{os.environ['E2B_API_KEY']}\n",
)
sandbox.files.write(
"/opt/anthropic-managed-agents-js/config/anthropic-api-key",
f"{os.environ['ANTHROPIC_API_KEY']}\n",
)
sandbox.files.write(
"/opt/anthropic-managed-agents-js/config/anthropic-environment-id",
f"{os.environ['ANTHROPIC_ENVIRONMENT_ID']}\n",
)
sandbox.files.write(
"/opt/anthropic-managed-agents-js/config/anthropic-environment-key",
f"{os.environ['ANTHROPIC_ENVIRONMENT_KEY']}\n",
)
print(f"Webhook URL: https://{sandbox.get_host(8000)}/webhook")
```
At this point `/health` returns `200`, but `/webhook` returns `503` until the signing key is configured. Keep this sandbox running or paused; registering a different sandbox URL means writing the signing key into that sandbox instead.
## Register the webhook
In the [Claude Console webhooks settings](https://platform.claude.com/settings/workspaces/default/webhooks), create a webhook endpoint using the printed URL:
```text theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
https:///webhook
```
Subscribe it to:
```text theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
session.status_run_started
```
Save the generated signing key, export it locally, then write it into the same webhook sandbox.
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
export ANTHROPIC_WEBHOOK_SIGNING_KEY="whsec_..."
```
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
await sandbox.files.write(
'/opt/anthropic-managed-agents-js/config/anthropic-webhook-signing-key',
`${process.env.ANTHROPIC_WEBHOOK_SIGNING_KEY}\n`,
)
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
sandbox.files.write(
"/opt/anthropic-managed-agents-js/config/anthropic-webhook-signing-key",
f"{os.environ['ANTHROPIC_WEBHOOK_SIGNING_KEY']}\n",
)
```
## Run an end-to-end smoke test
Create or select a Claude Managed Agents agent in the [Claude Console](https://platform.claude.com/workspaces/default/agents), then create a session with that agent and the same `ANTHROPIC_ENVIRONMENT_ID` you wrote into the webhook sandbox. Creating the session does not start work; the `user.message` event does.
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
export ANTHROPIC_AGENT_ID="agent_..."
```
Use a small shell task for the first smoke test:
```text theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
Use bash to echo webhook-smoke-ok. Answer only with that text.
```
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import Claude from '@anthropic-ai/sdk'
const client = new Claude({
apiKey: process.env.ANTHROPIC_API_KEY,
})
const session = await client.beta.sessions.create({
agent: process.env.ANTHROPIC_AGENT_ID!,
environment_id: process.env.ANTHROPIC_ENVIRONMENT_ID!,
title: 'E2B webhook smoke',
})
await client.beta.sessions.events.send(session.id, {
events: [
{
type: 'user.message',
content: [
{
type: 'text',
text: 'Use bash to echo webhook-smoke-ok. Answer only with that text.',
},
],
},
],
})
console.log(`Session ID: ${session.id}`)
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import os
from anthropic import Anthropic as Claude
client = Claude(api_key=os.environ["ANTHROPIC_API_KEY"])
session = client.beta.sessions.create(
agent=os.environ["ANTHROPIC_AGENT_ID"],
environment_id=os.environ["ANTHROPIC_ENVIRONMENT_ID"],
title="E2B webhook smoke",
)
client.beta.sessions.events.send(
session.id,
events=[
{
"type": "user.message",
"content": [
{
"type": "text",
"text": "Use bash to echo webhook-smoke-ok. Answer only with that text.",
},
],
},
],
)
print(f"Session ID: {session.id}")
```
Claude should answer:
```text theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
webhook-smoke-ok
```
Then check the webhook sandbox:
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
const health = await fetch(`https://${sandbox.getHost(8000)}/health`)
console.log(await health.json())
const logs = await sandbox.commands.run(
'tail -200 /opt/anthropic-managed-agents-js/webhook.log || true',
)
console.log(logs.stdout)
const assignments = await sandbox.commands.run(
'cat /opt/anthropic-managed-agents-js/.managed-agent-sandbox-store.json || true',
)
console.log(assignments.stdout)
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import json
import urllib.request
with urllib.request.urlopen(f"https://{sandbox.get_host(8000)}/health") as response:
print(json.loads(response.read()))
logs = sandbox.commands.run(
"tail -200 /opt/anthropic-managed-agents-js/webhook.log || true",
)
print(logs.stdout)
assignments = sandbox.commands.run(
"cat /opt/anthropic-managed-agents-js/.managed-agent-sandbox-store.json || true",
)
print(assignments.stdout)
```
A healthy run has:
* `/health` returning `ok: true`.
* Router logs showing `routing work` and `assigned session`.
* A session-to-sandbox assignment in the router's assignment store.
* The assigned worker sandbox's `worker.log` showing the shell tool execution and successful results posted back to Claude.
* The Claude Managed Agents session returning to `session.status_idle`.
The assignment store records the worker sandbox ID for each routed session. Connect to that sandbox and check `/opt/anthropic-managed-agents-js/worker.log` when you need the tool execution logs.
If the session stays at `requires_action`, check `/opt/anthropic-managed-agents-js/webhook.log` in the router sandbox first, then check `/opt/anthropic-managed-agents-js/worker.log` in the assigned worker sandbox. Stale environment keys, missing signing keys, archived sessions, and failed tool-result posts show up there.
## Runtime behavior
The worker sandbox runs tool calls with `/mnt/session` as its workdir. File tools are constrained to that workdir, skills are downloaded under `/mnt/session/skills//`, and generated artifacts should be written under `/mnt/session/outputs`.
The webhook sandbox is the router. It keeps a session-to-sandbox assignment store and starts worker sandboxes with E2B auto-resume and pause-on-timeout settings.
## Session-scoped sandboxes
By default, the public webhook template gives each Claude Managed Agents session its own E2B worker sandbox. Follow-up turns for the same session reconnect to the same worker and reuse its `/mnt/session` filesystem. The sandbox is the isolated execution environment, not the place where Claude's reasoning loop runs.
Use [cloud buckets](/storage/cloud-buckets), [Archil](/storage/archil), or [volumes](/volumes) when files need to outlive a sandbox or be shared across many sandboxes.
If you want to run that router in your own service instead of in E2B, use the cookbook's [`app-webhooks/` example](https://github.com/e2b-dev/e2b-cookbook/tree/main/examples/anthropic-managed-agents/javascript/app-webhooks). It receives webhooks in your app, claims work there, and routes each session to its own E2B sandbox by default.
## Clean up
Remove the webhook endpoint in the Claude Console before deleting the router sandbox. Then kill the router sandbox and any assigned worker sandboxes you no longer need.
## Related guides
Full JavaScript and Python examples for polling workers, sandbox-hosted webhooks, and app-owned routing.
Pause and resume sandboxes to preserve filesystem state between runs.
Store generated files outside the sandbox filesystem.
# Claude Toolsets
Source: https://docs.e2b.dev/agents/claude-toolsets
Run Claude Sonnet 5.5 browser use and computer use on private E2B desktop sandboxes.
Claude Toolsets is a browser use and computer use package that lets you run Claude Sonnet 5.5, trained specifically for Anthropic's browser and computer use toolsets, on E2B sandboxes. It ships special tools for both: `E2BBrowserToolset` drives Chrome through the page itself (elements, forms, tabs, navigation), and `E2BComputerToolset` drives the whole Linux desktop through screenshots, mouse and keyboard, so Claude can work in any desktop app, from a terminal to a LibreOffice spreadsheet. Each sandbox is a private cloud desktop that you can watch live while Claude works.
## How it works
The Claude SDK defines the toolsets (`browser_toolset_20260801` and `computer_toolset_20260801`): it parses the model's input, asks for confirmation, and renders each result for the model. Claude Toolsets is the E2B driver underneath, which executes every action inside an E2B desktop sandbox instead of on your machine.
1. **Create a toolset.** It creates a desktop sandbox, or attaches to one you pass in.
2. **Pass it in `tools`.** The toolset instance is the `tools[]` entry; there is no wrapper.
3. **Run the tool runner.** The SDK's tool runner calls the toolset for every action Claude takes.
4. **Close it.** The tool runner never closes a toolset. Use `try`/`finally` in TypeScript and `with` in Python.
| Toolset | Works on | How |
| - | - | - |
| `E2BBrowserToolset` | Websites in Chrome | The page itself: accessibility tree, elements, forms, tabs |
| `E2BComputerToolset` | The whole desktop | Screenshots, mouse and keyboard |
## Install
```bash JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
npm i @e2b/claude-toolsets @anthropic-ai/sdk @e2b/desktop
```
```bash Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
pip install e2b-claude-toolsets anthropic e2b-desktop
```
Set `E2B_API_KEY` ([get one](https://e2b.dev/dashboard?tab=keys)) and `ANTHROPIC_API_KEY` in your environment.
## Browser use
The browser toolset starts Chrome on an E2B desktop sandbox. Without a `sandbox` option it creates one and kills it on `close()`.
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import Anthropic from '@anthropic-ai/sdk'
import { E2BBrowserToolset, allowHosts } from '@e2b/claude-toolsets'
const hosts = ['github.com', 'githubassets.com', 'githubusercontent.com']
const browser = await E2BBrowserToolset.create({
allowOut: hosts.flatMap((host) => [host, `*.${host}`]), // what the sandbox can reach (E2B firewall)
urlPolicy: allowHosts(hosts), // where the model may navigate (SDK check)
})
try {
const answer = await new Anthropic().beta.messages.toolRunner({
model: 'claude-sonnet-5-5',
max_tokens: 1024,
tools: [browser],
messages: [{ role: 'user', content: 'How many stars does github.com/e2b-dev/E2B have?' }],
})
for (const block of answer.content) if (block.type === 'text') console.log(block.text)
} finally {
await browser.close()
}
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from anthropic import Anthropic
from e2b_claude_toolsets import E2BBrowserToolset, allow_hosts
hosts = ["github.com", "githubassets.com", "githubusercontent.com"]
with E2BBrowserToolset.create(
allow_out=[h for host in hosts for h in (host, f"*.{host}")], # what the sandbox can reach (E2B firewall)
url_policy=allow_hosts(hosts), # where the model may navigate (SDK check)
) as browser:
answer = Anthropic().beta.messages.tool_runner(
model="claude-sonnet-5-5",
max_tokens=1024,
tools=[browser],
messages=[{"role": "user", "content": "How many stars does github.com/e2b-dev/E2B have?"}],
).until_done()
print(next(block.text for block in answer.content if block.type == "text"))
```
GitHub serves its CSS, scripts and images from `githubassets.com` and `githubusercontent.com`, so those hosts are allowed too.
## Computer use
The computer toolset borrows a desktop sandbox you create. It gives Claude screenshots and mouse and keyboard actions, so it can use any app on the desktop. `liveView` streams that desktop to a local URL so you can watch.
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import Anthropic from '@anthropic-ai/sdk'
import { Sandbox } from '@e2b/desktop'
import { E2BComputerToolset, liveView } from '@e2b/claude-toolsets'
const desktop = await Sandbox.create({
resolution: [1280, 800], // screenshots are sent at this size
timeoutMs: 600_000,
network: { allowPublicTraffic: false }, // keeps the desktop stream private
})
const view = await liveView(desktop)
console.log('Watch live:', view.url)
try {
const computer = await E2BComputerToolset.create(desktop, {
confirm: () => true, // required for key, type and hold_key: approves every call (unattended)
})
try {
const answer = await new Anthropic().beta.messages.toolRunner({
model: 'claude-sonnet-5-5',
max_tokens: 4096,
tools: [computer],
messages: [{ role: 'user', content: 'Open a text editor and write a short note about E2B.' }],
})
for (const block of answer.content) if (block.type === 'text') console.log(block.text)
} finally {
await computer.close() // leaves the desktop running
}
} finally {
await view.stop()
await desktop.kill()
}
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from anthropic import Anthropic
from e2b_desktop import Sandbox
from e2b_claude_toolsets import E2BComputerToolset, live_view
desktop = Sandbox.create(
resolution=(1280, 800), # screenshots are sent at this size
timeout=600,
network={"allow_public_traffic": False}, # keeps the desktop stream private
)
try:
with live_view(desktop) as view:
print("Watch live:", view.url)
# confirm is required for key, type and hold_key: approves every call (unattended)
with E2BComputerToolset.create(desktop, confirm=lambda context: True) as computer:
answer = Anthropic().beta.messages.tool_runner(
model="claude-sonnet-5-5",
max_tokens=4096,
tools=[computer],
messages=[{"role": "user", "content": "Open a text editor and write a short note about E2B."}],
).until_done()
print("\n".join(block.text for block in answer.content if block.type == "text"))
finally:
desktop.kill()
```
The `confirm` hook is where an application can ask a person before Claude types or presses keys. Returning `true` approves every call, which suits unattended runs.
## Watch live
`liveView(desktop)` (`live_view` in Python) starts the desktop's VNC stream and serves it on a random loopback URL on your machine. The E2B traffic token stays in your process, so the sandbox keeps `allowPublicTraffic: false` and only you can watch. Stopping the view closes the stream but never kills the desktop.
The live view needs a fresh desktop created with `allowPublicTraffic: false`. It refuses a desktop that already streams, because stopping a stream is sandbox-wide. Do not share the URL.
## Use both toolsets on one desktop
Pass both toolsets to the same tool runner and Claude picks the right one per step: the browser toolset for web apps, which is faster and more precise than clicking pixels, and the computer toolset for desktop apps and Chrome's own popups, which the page cannot see. To share one desktop, create it yourself and pass it to the browser toolset as `sandbox`.
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
const desktop = await Sandbox.create({
resolution: [1280, 800],
network: {
allowPublicTraffic: false, // required: keeps Chrome's DevTools port and the desktop stream private
maskRequestHost: 'localhost:${PORT}', // required when the browser toolset attaches
allowOut: ['example.com'],
denyOut: ['0.0.0.0/0'],
},
})
const browser = await E2BBrowserToolset.create({ sandbox: desktop, urlPolicy: allowHosts(['example.com']) })
const computer = await E2BComputerToolset.create(desktop, { confirm: () => true })
const answer = await new Anthropic().beta.messages.toolRunner({
model: 'claude-sonnet-5-5',
max_tokens: 4096,
tools: [browser, computer],
messages: [{ role: 'user', content: task }],
})
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
desktop = Sandbox.create(
resolution=(1280, 800),
network={
"allow_public_traffic": False, # required: keeps Chrome's DevTools port and the desktop stream private
"mask_request_host": "localhost:${PORT}", # required when the browser toolset attaches
"allow_out": ["example.com"],
"deny_out": ["0.0.0.0/0"],
},
)
with E2BBrowserToolset.create(sandbox=desktop, url_policy=allow_hosts(["example.com"])) as browser, \
E2BComputerToolset.create(desktop, confirm=lambda context: True) as computer:
answer = Anthropic().beta.messages.tool_runner(
model="claude-sonnet-5-5",
max_tokens=4096,
tools=[browser, computer],
messages=[{"role": "user", "content": task}],
).until_done()
```
For a full run, see the [Excel to OrangeHRM example](https://github.com/e2b-dev/e2b-cookbook/tree/main/examples/anthropic-computer-use-orangehrm-js) in the E2B cookbook: Claude reads new hires from a spreadsheet in LibreOffice Calc with computer use, enters each one into the OrangeHRM web app with browser use, and writes the assigned Employee Ids back into the spreadsheet.
## Network control
Use both layers. They check different things.
| Layer | Option | Enforced by | What it limits |
| - | - | - | - |
| Sandbox egress | `allowOut` / `allow_out`, or `network.allowOut` on your own sandbox | E2B, outside the browser | Every connection the sandbox makes |
| URL policy | `urlPolicy: allowHosts([...])` / `url_policy=allow_hosts([...])` | The Claude SDK, before each navigation | Where the model may navigate |
Egress is fixed when the sandbox is created; a URL policy does not change it. `allowHosts` permits HTTP(S) hosts and their subdomains. It is a sample policy, not a DNS firewall, so keep sandbox egress restrictive for sensitive browsing.
## Sandbox ownership
You close what you create:
* **Toolset creates the sandbox** (no `sandbox` option): `close()` kills it. Creation options are `apiKey`, `template` (default `desktop`), `allowOut`, `timeoutMs` (default 10 minutes; `timeout` in seconds in Python) and `metadata`.
* **You pass a sandbox**: the toolset borrows it, and `close()` stops only the Chrome it started and leaves the sandbox running. The sandbox must already have `allowPublicTraffic: false` and `maskRequestHost: 'localhost:${PORT}'`; creation options are refused.
On a desktop sandbox, Chrome opens visibly so it shows in the live view. Pass `headless: true` to hide it.
## Optional browser actions
Four browser actions are off by default: `file_upload`, `javascript_exec`, `read_console` and `read_network`. Enable them through the SDK's `configs` option. The SDK refuses to enable them without a `confirm` hook.
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
const browser = await E2BBrowserToolset.create({
allowOut: ['github.com', '*.github.com', 'githubassets.com', '*.githubassets.com'],
urlPolicy: allowHosts(['github.com']),
configs: { javascript_exec: { enabled: true } }, // the model can run any script on the page
confirm: () => true, // required with javascript_exec: approves every call (unattended)
})
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
browser = E2BBrowserToolset.create(
allow_out=["github.com", "*.github.com", "githubassets.com", "*.githubassets.com"],
url_policy=allow_hosts(["github.com"]),
configs={"javascript_exec": {"enabled": True}}, # the model can run any script on the page
confirm=lambda context: True, # required with javascript_exec: approves every call (unattended)
)
```
## Python async
Python has native asyncio drivers next to the sync ones: `AsyncE2BBrowserToolset` and `AsyncE2BComputerToolset`. They accept the same options and borrow an `e2b.AsyncSandbox`.
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from anthropic import AsyncAnthropic
from e2b_claude_toolsets import AsyncE2BBrowserToolset, allow_hosts
async def browse():
async with await AsyncE2BBrowserToolset.create(
allow_out=["example.com"], url_policy=allow_hosts(["example.com"]),
) as browser, AsyncAnthropic() as client:
return await client.beta.messages.tool_runner(
model="claude-sonnet-5-5",
max_tokens=1024,
tools=[browser],
messages=[{"role": "user", "content": "Read https://example.com"}],
).until_done()
```
## Supported actions
| Toolset | Actions |
| - | - |
| Browser: navigation and capture | `navigate` (URL, back, forward, reload), `screenshot`, `zoom` |
| Browser: mouse | `left_click`, `right_click`, `middle_click`, `double_click`, `triple_click`, `hover`, `left_click_drag`, `left_mouse_down`, `left_mouse_up`, `mouse_move`, `scroll`, `scroll_to` |
| Browser: input and reading | `type`, `key`, `hold_key`, `form_input`, `read_page`, `find`, `get_page_text`, `wait` |
| Browser: tabs | `new_tab`, `list_tabs`, `switch_tab`, `close_tab` |
| Browser: optional | `file_upload`, `javascript_exec`, `read_console`, `read_network` |
| Computer | `screenshot`, `cursor_position`, `mouse_move`, `left_click`, `right_click`, `middle_click`, `double_click`, `triple_click`, `left_mouse_down`, `left_mouse_up`, `left_click_drag`, `scroll`, `key`, `hold_key`, `type`, `wait` |
## Limits
* **Screen size:** browser viewport and desktop resolution up to 2560×1440 pixels in total, default 1280×800. Larger sizes are refused rather than silently scaled, because the API would shrink the screenshots and clicks would miss. At 1280×800 each screenshot costs about 1,400 tokens.
* **Fixed resolution:** keep the desktop resolution fixed for the whole run. Coordinates are screenshot pixels.
* **Input:** waits and held keys are limited to 30 seconds, key repeats to 100. Input assumes a US keyboard layout.
* **Downloads** stay in the sandbox. Their bytes are never added to the conversation.
## Related guides
Claude uses both toolsets on one desktop to copy new hires into an HR system
How computer use agents drive E2B Desktop sandboxes
Control sandbox egress with allow and deny lists
# Codex
Source: https://docs.e2b.dev/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](/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. The sandbox isolates the agent from your host machine, but sandboxes can reach the open internet by default — restrict outbound traffic with [network rules](/network/internet-access). Pass `--skip-git-repo-check` to bypass git directory ownership checks inside the sandbox. Pass `CODEX_API_KEY` as an environment variable.
Auto-approving tool calls is contained by the sandbox: the agent cannot touch your host machine, local files, or credentials. It can still make outbound network requests — internet access is enabled by default. To limit where an auto-approved agent can connect, configure [outbound network rules](/network/internet-access) (`allowInternetAccess`, plus allow/deny lists by CIDR or hostname). Hostname rules apply to HTTP(S) traffic only; use CIDR rules for other protocols.
```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.commands.run(
'git clone --depth 1 https://x-access-token:$GITHUB_TOKEN@github.com/your-org/your-repo.git /home/user/repo',
{ envs: { GITHUB_TOKEN: process.env.GITHUB_TOKEN } }
)
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.commands.run(
"git clone --depth 1 https://x-access-token:$GITHUB_TOKEN@github.com/your-org/your-repo.git /home/user/repo",
envs={"GITHUB_TOKEN": os.environ["GITHUB_TOKEN"]},
)
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()
```
## Resume a session
Codex persists sessions (rollout files under `~/.codex/sessions` in the sandbox) that can be resumed with follow-up tasks using `codex exec resume`. Run the first task with `--json` and capture the `thread_id` from the `thread.started` event.
```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,
})
// Start a new session
const initial = await sandbox.commands.run(
`codex exec --full-auto --skip-git-repo-check --json "Create plan.md with a 3-step plan for a TODO CLI app"`
)
// The first --json event is thread.started
const threadId = JSON.parse(initial.stdout.trim().split('\n')[0]).thread_id
// Continue in the same session
await sandbox.commands.run(
`codex exec resume ${threadId} --full-auto --skip-git-repo-check "Now implement step 1 of the plan"`,
{ onStdout: (data) => process.stdout.write(data) }
)
const plan = await sandbox.commands.run('cat /home/user/plan.md')
console.log(plan.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("codex", envs={
"CODEX_API_KEY": os.environ["CODEX_API_KEY"],
}, timeout=600)
# Start a new session
initial = sandbox.commands.run(
'codex exec --full-auto --skip-git-repo-check --json "Create plan.md with a 3-step plan for a TODO CLI app"',
)
# The first --json event is thread.started
thread_id = json.loads(initial.stdout.strip().splitlines()[0])["thread_id"]
# Continue in the same session
sandbox.commands.run(
f'codex exec resume {thread_id} --full-auto --skip-git-repo-check "Now implement step 1 of the plan"',
on_stdout=lambda data: print(data, end=""),
)
plan = sandbox.commands.run("cat /home/user/plan.md")
print(plan.stdout)
sandbox.kill()
```
To simply continue the most recent session in the working directory, use `codex exec resume --last "follow-up task"` — no thread ID needed. When working in a cloned repo, pass `-C` before the `resume` subcommand (`codex exec -C /home/user/repo resume "..."`); it is not accepted after it.
## 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
Connect to the sandbox via SSH for interactive sessions
# Crabbox with E2B
Source: https://docs.e2b.dev/agents/crabbox
Run Crabbox test and execution workflows on E2B sandboxes.
[Crabbox](https://github.com/openclaw/crabbox) is a remote software testing and execution control plane for maintainers and AI agents. Use its E2B provider when you want Crabbox to create an E2B Linux sandbox, sync your working tree, run a command, stream output, and clean up from one CLI command.
## Install
Install Crabbox locally.
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
brew install openclaw/tap/crabbox
crabbox --version
```
## Configure E2B
Export your E2B API key before running Crabbox.
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
export E2B_API_KEY=e2b_...
```
You can select E2B per command with `--provider e2b`, or set it in a repo-level Crabbox config.
```yaml .crabbox.yaml theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
provider: e2b
target: linux
e2b:
template: base
workdir: crabbox
```
`template` is the E2B template to create. `workdir` is the directory where Crabbox syncs and runs your project inside the sandbox.
## Run tests
Run a command in a fresh E2B sandbox.
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
crabbox run --provider e2b -- echo crabbox-e2b-ok
```
Use an existing warm sandbox for repeated runs.
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
crabbox warmup --provider e2b --e2b-template base
lease=""
crabbox run --provider e2b --id "$lease" -- echo crabbox-e2b-ok
crabbox status --provider e2b --id "$lease" --wait
crabbox stop --provider e2b "$lease"
```
Run in a custom E2B workdir.
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
crabbox run --provider e2b --e2b-workdir repo -- echo crabbox-e2b-ok
```
## How it works
With `provider: e2b`, Crabbox uses E2B sandbox lifecycle, file upload, and process APIs instead of SSH. Crabbox owns local config, sync manifests, command orchestration, output streaming, and normalized `list` / `status` behavior. E2B owns the sandbox runtime and command transport.
## Limitations
* E2B is a Linux delegated-run provider for Crabbox.
* Crabbox SSH commands such as `ssh`, `vnc`, `code`, and SSH-based Actions hydration are not available with `provider: e2b`.
* `--class` and `--type` are rejected because sandbox resources come from the selected E2B template.
* `--e2b-template`, `--e2b-workdir`, `--e2b-user`, `--e2b-api-url`, and `--e2b-domain` override matching config values.
For the full Crabbox command and provider reference, see the [Crabbox repository](https://github.com/openclaw/crabbox).
# CrewAI
Source: https://docs.e2b.dev/agents/crewai
Give CrewAI agents an isolated E2B sandbox for running Python code.
[CrewAI](https://www.crewai.com/) is a framework for building agents and
multi-agent workflows. Its native
[`E2BPythonTool`](https://docs.crewai.com/en/tools/ai-ml/e2bsandboxtools)
runs agent-generated Python in an isolated E2B Code Interpreter sandbox instead
of executing it on your host.
## Install the dependencies
Install CrewAI with its E2B tool dependencies:
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
pip install "crewai-tools[e2b]" python-dotenv
```
Set API keys for E2B and your model provider:
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
export E2B_API_KEY="e2b_..."
export OPENAI_API_KEY="..."
```
## Run a data analysis agent
Create one persistent tool instance and give it to the agent. Persistent mode
lets the Python kernel retain imports and variables if the agent needs to retry
its work.
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import os
from crewai import Agent, Crew, Process, Task
from crewai_tools import E2BPythonTool
python_tool = E2BPythonTool(
persistent=True,
sandbox_timeout=600,
)
analyst = Agent(
role="Data Analyst",
goal="Use sandboxed Python to produce accurate, reproducible analysis",
backstory=(
"You verify every numerical answer by running code in an isolated "
"E2B sandbox."
),
tools=[python_tool],
llm=os.getenv("MODEL", "openai/gpt-4.1-mini"),
allow_delegation=False,
max_iter=8,
verbose=True,
)
task = Task(
description="""
Use the E2B Sandbox Python tool to run Python code. Create `random.Random(23)`,
generate 10,000 integers with `randint(1, 1000)`, and return a JSON object with
their count, mean, population standard deviation, p95, and SHA-256 digest.
Inspect the tool result's `error` field and correct and rerun failed code.
Do not estimate the answer yourself.
""",
expected_output="A JSON object containing the computed statistics.",
agent=analyst,
)
crew = Crew(
agents=[analyst],
tasks=[task],
process=Process.sequential,
verbose=True,
)
try:
result = crew.kickoff()
print(result)
finally:
python_tool.close()
```
The complete runnable version, including environment validation and locked
dependencies, is available in the
[E2B cookbook](https://github.com/e2b-dev/e2b-cookbook/tree/main/examples/crewai-python).
## Sandbox lifecycle
By default, an E2B CrewAI tool creates and closes a sandbox for every tool call.
Use `persistent=True` only when an agent needs state across calls. A persistent
tool creates its sandbox lazily and reuses it until it is closed.
Call `close()` in `finally` so cleanup runs after both successful and failed
crew executions. If you pass an existing `sandbox_id`, your application owns
that sandbox and the tool will not close it.
Keep `sandbox_timeout` as short as the workload allows. CrewAI applies it when
the tool creates or connects to the sandbox and does not refresh it after each
tool call. The tool's per-execution `timeout` separately controls how long an
individual code execution may run.
## Results and errors
`E2BPythonTool` returns structured data:
```json theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
{
"text": "...",
"stdout": [],
"stderr": [],
"error": null,
"results": [],
"execution_count": 1
}
```
Tell the agent to check `error` before accepting an answer. If code execution
fails, the agent can inspect the error, correct the code, and run it again in
the same persistent sandbox.
## Security considerations
E2B isolates agent-generated code from the machine running CrewAI. The sandbox
still processes model-generated inputs and produces untrusted output:
* do not expose API keys or other host secrets to the sandbox unless required;
* use persistent mode only when state must survive between calls;
* explicitly close sandboxes owned by the tool;
* use bounded sandbox and execution timeouts;
* validate tool output before using it in another trusted system.
Learn more about [E2B sandbox lifecycle](/sandbox).
# Cursor
Source: https://docs.e2b.dev/agents/cursor
Run Cursor Self-Hosted Machines in isolated E2B sandboxes.
The public `cursor-agents` template connects
[Cursor Self-Hosted Machines](https://cursor.com/docs/cloud-agent/bring-your-own-machine)
to E2B. A dispatcher watches one Cursor
[pool](https://cursor.com/docs/cloud-agent/bring-your-own-machine/pools) and
creates a dedicated `cursor-agents-worker` sandbox for each request. Cursor
runs the agent loop in its cloud, while tool calls execute in the sandbox.
Create the dispatcher and connect to its terminal with the E2B CLI:
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
e2b sbx create cursor-agents
```
When the dispatcher is ready, the terminal prints its authenticated setup UI
URL. You can also launch it from the
[E2B console](https://console.e2b.dev/terminal?template=cursor-agents), or
from TypeScript:
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from "e2b"
const sandbox = await Sandbox.create("cursor-agents", {
timeoutMs: 60 * 60_000,
domain: process.env.E2B_DOMAIN,
lifecycle: { onTimeout: "pause", autoResume: true },
})
const { stdout } = await sandbox.commands.run(
'node /opt/cursor-agents/create-launch-url.mjs "$E2B_DOMAIN"',
{ envs: { E2B_DOMAIN: process.env.E2B_DOMAIN ?? "e2b.app" } },
)
console.log(stdout.trim())
```
## Prerequisites
* an E2B API key
* a Cursor Enterprise plan with Self-Hosted Machines enabled
* a Cursor **service-account** API key from **Settings → Service accounts**
* the assigned repository URL when using a repository-scoped service account
* optionally, a git token for private repositories
Personal, team-admin, and organization keys cannot start pool workers. See
[Cursor's Self-Hosted Machines guide](https://cursor.com/docs/cloud-agent/bring-your-own-machine)
for current key and pool requirements.
## Set up the dispatcher
Run the SDK example or open the console link above. Open the setup URL
printed by the sandbox. It works once and expires after 10 minutes, so do
not share it or leave it in logs.
Enter the Cursor service-account API key and choose **Verify account**.
Select an existing pool or enter a name to create one. For a
repository-scoped service account, enter its exact repository URL.
Enter the E2B API key and select its domain: `e2b.app` for US/default,
`e2b-juliett.dev` for EU, or `e2b-tango.dev` for APAC. Keep
`cursor-agents-worker` unless you have a custom worker template.
Under **Advanced**, set the maximum concurrent workers and enable computer
use if workers should control Chromium. Hibernation is enabled by default,
so idle workers pause and resume for follow-up messages.
Add a git token for private repositories, or choose **Skip - public repo**.
Choose **Save & start dispatcher** and wait until it is watching the pool.
## Run a task
Use **Start your first session** in the dispatcher to test the configuration.
For regular work, send a task to the configured pool from Cursor, Slack,
GitHub, Linear, or the Cursor API.
1. The dispatcher claims the request and creates a `cursor-agents-worker`
sandbox.
2. The worker clones the request's repositories under `/workspace` and
connects to Cursor.
3. Follow the session in Cursor or use the dispatcher dashboard to view its
sandbox and status.
Each request gets an isolated worker. When the worker becomes idle, it pauses
and resumes for a follow-up. If hibernation is disabled, the dispatcher removes
the worker instead.
The dispatcher claims requests up to **Max concurrent workers** (default 20).
Additional requests remain queued in Cursor until capacity is available.
## Private repositories
The dispatcher supplies git credentials through the E2B egress proxy instead
of writing them to the worker. Scope the token to only the repositories the
pool needs.
Use `x-access-token` as the git username for GitHub or `oauth2` for GitLab.
## Lifecycle
Keep the dispatcher running while it needs to accept tasks. Cursor queue
traffic cannot wake a paused dispatcher. The dispatcher renews its E2B timeout
while it watches the pool and reconnects to the queue after a restart or
resume.
Stop the dispatcher and let active sessions finish before changing the pool,
credentials, E2B domain, worker template, hibernation, or git settings.
## Troubleshooting
Confirm the key is an Enterprise **service-account** API key. If it is
repository-scoped, enter its exact repository URL during setup.
Confirm the dispatcher is running and the task targets the same pool. Also
check whether the dispatcher has reached its worker limit.
Confirm the E2B API key can create the public `cursor-agents-worker`
template. Custom templates must belong to the same E2B project as the key.
Confirm the git token can access the repository and the git username
matches the provider.
Open its terminal and run:
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
tail -200 /home/user/cursor-supervisor.log
jq '{state, detail, activeCount, provisioningCount}' \
/home/user/.config/cursor-agents/worker-status.json
```
# LangChain Deep Agents
Source: https://docs.e2b.dev/agents/deep-agents
Use E2B sandboxes as the execution backend for LangChain Deep Agents.
[Deep Agents](https://docs.langchain.com/oss/python/deepagents/overview) is LangChain's agent harness for building LLM-powered agents, built on the [LangGraph](https://langchain-ai.github.io/langgraph/) runtime. In Deep Agents, a [sandbox backend](https://docs.langchain.com/oss/python/deepagents/sandboxes) defines the environment where the agent operates — E2B is a supported backend via the [`langchain-e2b`](https://pypi.org/project/langchain-e2b/) package.
With the E2B backend, the agent's built-in tools all execute inside an isolated sandbox instead of your machine:
* **Filesystem tools** — `ls`, `read_file`, `write_file`, `edit_file`, `glob`, and `grep` operate on the sandbox filesystem.
* **Shell tool** — `execute` runs arbitrary shell commands in the sandbox.
## Install the dependencies
Set API keys for E2B and your model provider:
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
export E2B_API_KEY="e2b_***" # get yours at https://console.e2b.dev
export ANTHROPIC_API_KEY="sk-ant-***"
```
Then install the packages:
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
pip install deepagents langchain-e2b e2b
```
## Basic example
Create an E2B sandbox, wrap it in the `E2BSandbox` backend, and pass it to `create_deep_agent`. You manage the sandbox lifecycle yourself — create it before the agent runs and kill it when you're done.
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from deepagents import create_deep_agent
from e2b import Sandbox
from langchain_e2b import E2BSandbox
sandbox = Sandbox.create(timeout=600)
agent = create_deep_agent(
model="anthropic:claude-sonnet-5",
system_prompt="You are a coding agent working in an isolated Linux sandbox.",
backend=E2BSandbox(sandbox=sandbox),
)
result = agent.invoke({
"messages": [{
"role": "user",
"content": "Create hello.py that prints 'Hello from E2B', run it, and show the output.",
}]
})
print(result["messages"][-1].content)
sandbox.kill()
```
The agent's file operations and shell commands all run inside the sandbox — nothing touches your machine. Because the backend wraps the native E2B SDK sandbox, you can configure everything the SDK supports: [custom templates](/template/quickstart), timeouts, [persistence](/sandbox/persistence), or reconnecting to an existing sandbox by ID.
## Deep Agents Code (dcode)
[Deep Agents Code](https://docs.langchain.com/oss/python/deepagents/sandboxes#deep-agents-code) is LangChain's terminal coding agent built on Deep Agents. `langchain-e2b` ships a sandbox provider for it, so every `dcode` session can run in an E2B sandbox:
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
dcode --install langchain-e2b --package
export E2B_API_KEY="e2b_***"
dcode --sandbox e2b
```
Requires `dcode` >= 0.1.19 and `langchain-e2b` >= 0.0.4. Unlike the library path, `dcode` manages the sandbox lifecycle for you — it creates the sandbox on start and deletes it on exit. Configure it with:
* `E2B_TEMPLATE` — custom sandbox template to start from
* `E2B_SANDBOX_TIMEOUT` — sandbox timeout in seconds
## Custom templates
By default, sandboxes start from the E2B base template. To pre-install languages, frameworks, or tools your agent needs — and cut setup time per run — build a [custom template](/template/quickstart) and pass it when creating the sandbox:
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
sandbox = Sandbox.create(template="your-template-id-or-name", timeout=600)
```
## Deep Agents vs. Open SWE
[Open SWE](/agents/open-swe) is LangChain's coding agent framework built on top of Deep Agents — both use the same `langchain-e2b` backend under the hood, but they sit at different layers:
| | **Deep Agents** (this page) | **[LangChain Open SWE](/agents/open-swe)** |
| - | - | - |
| What it is | A Python library you embed in your own agent | A deployable internal coding agent you fork and run |
| How you use E2B | Create the sandbox yourself and pass `E2BSandbox` to `create_deep_agent` | Set `SANDBOX_TYPE="e2b"` — sandboxes are created and managed for you |
| Sandbox lifecycle | Yours to manage (`Sandbox.create()` / `sandbox.kill()`) | Per-thread persistent sandbox, reconnect by ID, auto-recreate |
| Use it when | Building any custom agent that needs isolated code execution | You want a GitHub/Slack/Linear-driven coding agent for your org |
Use this page's approach for custom agents; if you're deploying a coding agent for your team, start from [Open SWE](/agents/open-swe) instead. If you are writing the graph yourself rather than using a harness, see [LangGraph](/agents/langgraph) — it covers keeping one sandbox alive across graph steps, checkpoints and interrupts.
## Learn more
* [Deep Agents sandboxes](https://docs.langchain.com/oss/python/deepagents/sandboxes) — sandbox backends overview
* [E2B integration reference](https://docs.langchain.com/oss/python/integrations/sandboxes/e2b) — LangChain's E2B provider page
* [`langchain-e2b` on PyPI](https://pypi.org/project/langchain-e2b/)
## Related guides
Build custom sandbox templates with pre-installed dependencies
Auto-pause, resume, and manage sandbox lifecycle
LangChain's coding agent framework with E2B built in
E2B as a tool inside a graph you write yourself
# Devin
Source: https://docs.e2b.dev/agents/devin
Run Devin for Terminal in a secure E2B sandbox.
[Devin for Terminal](https://devin.ai/terminal) is Cognition's coding agent for working directly from a terminal. E2B provides a pre-built `devin` template with Devin already installed.
To run Devin cloud sessions in isolated E2B sandboxes, see [Devin Outposts](/agents/devin-outposts).
## CLI
Create a sandbox with the [E2B CLI](/cli).
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
e2b sbx create devin
```
Once inside the sandbox, start Devin and follow the interactive login flow. You can create a Devin account during sign up if you don't have one.
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
devin
```
## Usage
After signing in, use `devin -p` for non-interactive mode and `--permission-mode dangerous` to auto-approve tool calls.
Auto-approving tool calls is contained by the sandbox: the agent cannot touch your host machine, local files, or credentials. It can still make outbound network requests — internet access is enabled by default. To limit where an auto-approved agent can connect, configure [outbound network rules](/network/internet-access) (`allowInternetAccess`, plus allow/deny lists by CIDR or hostname). Hostname rules apply to HTTP(S) traffic only; use CIDR rules for other protocols.
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
mkdir -p /home/user/project
cd /home/user/project
devin --permission-mode dangerous -p "Create a hello world HTTP server in Go"
```
## Example: work on a cloned repository
After signing in, clone a repository and run Devin from the project directory.
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
git clone https://github.com/your-org/your-repo.git /home/user/repo
cd /home/user/repo
devin --permission-mode dangerous -p "Add error handling to all API endpoints"
```
Devin can edit files, run commands, and work on code inside the sandbox without accessing your local machine.
# Devin Outposts
Source: https://docs.e2b.dev/agents/devin-outposts
Run Devin sessions in isolated E2B sandboxes.
The public `devin-outposts` template runs a dispatcher in E2B. The dispatcher
watches one Devin outpost and creates a dedicated `devin-outposts-worker`
sandbox for each session.
Create the dispatcher and connect to its terminal with the E2B CLI:
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
e2b sbx create devin-outposts
```
When the dispatcher is ready, the terminal prints its authenticated Outposts UI
URL. You can also launch it directly from the
[E2B console](https://console.e2b.dev/terminal?template=devin-outposts), or
from TypeScript:
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from "e2b"
const sandbox = await Sandbox.create("devin-outposts", {
timeoutMs: 60 * 60_000,
lifecycle: { onTimeout: "pause", autoResume: true },
})
const { stdout } = await sandbox.commands.run(
'node /opt/devin-outposts/create-launch-url.mjs "$E2B_DOMAIN"',
{ envs: { E2B_DOMAIN: process.env.E2B_DOMAIN ?? "e2b.app" } },
)
console.log(stdout.trim())
```
## Prerequisites
* an E2B API key
* a Devin Enterprise organization
* a Devin v3 service-user token beginning with `cog_`
Give the primary service user both **Use outpost machine** and **Manage
outposts**. Create it under **Settings → Devin API → Service users**, then copy
the token when Devin displays it. If you configure a separate worker token, it
only needs **Use outpost machine**.
See [Cognition's Outposts guide](https://docs.devin.ai/cloud/outposts/overview)
for the current permission and token flow.
## Set up the dispatcher
Run the SDK example or open the dashboard link above. Open the exact
setup URL printed by the sandbox. Its token works once and expires after 10
minutes. Treat the URL as a credential until it has been used: do not share
it or leave it in shared logs.
Enter the primary service-user token and choose **Check Devin account**.
Select the organization and enter its full browser URL, including
`/org/`, if it was not detected.
Select an existing outpost or create one. Keep `devin-outposts-worker` as
the worker template unless you have prepared a custom template or snapshot.
Enter the E2B API key that should create the worker sandboxes.
Choose **Save and start dispatcher**. Wait until its status shows that it is
listening before sending a task.
## Run a session
1. Under **Tasks**, enter the task and choose **Review task in Devin**.
2. In Devin, open **Configuration → Virtual environment** and select the
configured outpost.
3. Send the task.
4. Return to **Tasks** in the dispatcher to follow the session. Use **View in
Devin** to open the original conversation.
The virtual environment must be selected before sending. A session sent to the
wrong environment cannot be moved; start a new session with the correct
outpost.
Each Devin session gets its own E2B worker sandbox and writable `/mnt/repos`
workspace. The dispatcher removes the worker after the session finishes.
## Lifecycle
Do not pause the dispatcher while it needs to accept sessions. Devin queue
traffic cannot wake it. With `lifecycle.autoResume` (JavaScript) /
`lifecycle.auto_resume` (Python) enabled, opening its E2B URL resumes it manually.
## Troubleshooting
Confirm that Devin is using the organization selected during setup and that
the primary service user has both required permissions.
Confirm that the dispatcher is listening and that the task was sent to the
same outpost.
Confirm that the E2B API key can create the public
`devin-outposts-worker` template. A custom template must be visible to the
same E2B project as the key.
Open its terminal and run:
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
tail -200 /home/user/devin-supervisor.log
jq '{state, detail, activeCount, provisioningCount}' \
/home/user/.config/devin-outposts/worker-status.json
```
# Droid
Source: https://docs.e2b.dev/agents/droid
Run Factory's Droid in a secure E2B sandbox with full filesystem, terminal, and git access.
[Droid](https://factory.ai/product/cli) is Factory's coding agent and CLI. E2B provides a pre-built `droid` template with Droid already installed.
## CLI
Create a sandbox with the [E2B CLI](/cli).
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
e2b sbx create droid
```
Once inside the sandbox, start Droid.
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
droid
```
## Run headless
Use `droid exec` for non-interactive mode and `--auto low` to let Droid make safe file edits and run non-destructive commands inside the isolated sandbox. Droid authenticates with an API key from [Factory settings](https://app.factory.ai/settings/api-keys) via the `FACTORY_API_KEY` environment variable.
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const sandbox = await Sandbox.create('droid', {
envs: { FACTORY_API_KEY: process.env.FACTORY_API_KEY },
})
const result = await sandbox.commands.run(
`droid exec --auto low "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("droid", envs={
"FACTORY_API_KEY": os.environ["FACTORY_API_KEY"],
})
result = sandbox.commands.run(
'droid exec --auto low "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('droid', {
envs: { FACTORY_API_KEY: process.env.FACTORY_API_KEY },
timeoutMs: 600_000,
})
await sandbox.commands.run(
'git clone --depth 1 https://x-access-token:$GITHUB_TOKEN@github.com/your-org/your-repo.git /home/user/repo',
{ envs: { GITHUB_TOKEN: process.env.GITHUB_TOKEN } }
)
const result = await sandbox.commands.run(
`cd /home/user/repo && droid exec --auto low "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("droid", envs={
"FACTORY_API_KEY": os.environ["FACTORY_API_KEY"],
}, timeout=600)
sandbox.commands.run(
"git clone --depth 1 https://x-access-token:$GITHUB_TOKEN@github.com/your-org/your-repo.git /home/user/repo",
envs={"GITHUB_TOKEN": os.environ["GITHUB_TOKEN"]},
)
result = sandbox.commands.run(
'cd /home/user/repo && droid exec --auto low "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()
```
## 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 `droid` template.
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
// template.ts
import { Template } from 'e2b'
export const template = Template()
.fromTemplate('droid')
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
# template.py
from e2b import Template
template = (
Template()
.from_template("droid")
)
```
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
// build.ts
import { Template, defaultBuildLogger } from 'e2b'
import { template as droidTemplate } from './template'
await Template.build(droidTemplate, 'my-droid', {
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 droid_template
Template.build(droid_template, "my-droid",
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
Connect to the sandbox via SSH for interactive sessions
# DSPy RLMs
Source: https://docs.e2b.dev/agents/dspy
Run DSPy RLM-generated Python in E2B with persistent CPython, SQLite, and host-side model tools.
[DSPy RLM](https://dspy.ai/api/modules/RLM/) lets a model explore inputs with Python, call sub-models on selected passages, and submit a typed answer. The [E2B cookbook example](https://github.com/e2b-dev/e2b-cookbook/tree/main/examples/dspy-rlms-python) provides an `E2BInterpreter` for DSPy 3.4.0's native `interpreter_factory` interface.
## Why use E2B for DSPy RLMs
DSPy's default Python interpreter already isolates code in local Deno/Pyodide WASM and lets the model inspect inputs without putting them in the prompt. E2B supplies remote Linux with full CPython, including `sqlite3` and `subprocess`, plus an enforced execution deadline: if generated code loops forever, the adapter kills its sandbox. DSPy 3.4.0's default Python interpreter has no code-execution timeout. This example uses the network-off E2B base environment, without pandas/numpy or a custom template.
## Install the example
Use Python 3.10–3.14 and [uv](https://docs.astral.sh/uv/getting-started/installation/):
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
git clone https://github.com/e2b-dev/e2b-cookbook.git
cd e2b-cookbook/examples/dspy-rlms-python
uv sync --frozen
cp .env.example .env
```
Set these values in the ignored `.env`:
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
E2B_API_KEY=e2b_...
OPENAI_API_KEY=...
MODEL=openai/gpt-5.6-luna
```
The default model is [GPT-5.6 Luna](https://developers.openai.com/api/docs/models/gpt-5.6-luna). The LM settings are chosen for OpenAI reasoning models: `temperature=None` and `max_tokens=16000`. `MODEL` changes only the identifier; other models may need different LM parameters in `main.py`. Compatibility with other models is not established.
`E2BInterpreter` comes from this locally installed project, with DSPy pinned to 3.4.0 and core E2B 2.53.1 in its lockfile. It is not a native DSPy import or a separately published PyPI package. To use it elsewhere, install the local project with `pip install /path/to/e2b-cookbook/examples/dspy-rlms-python`.
Keys remain on your host. E2B receives generated code and selected inputs; the model provider receives prompts and passages sent to sub-queries. Pass only data those services may process. The sandbox itself has no outbound network access.
## Check persistent SQLite without a model
Save the following as `check_sqlite.py` in the example directory and run `uv run check_sqlite.py`. This calls E2B and needs only `E2B_API_KEY`, with no model request:
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from dotenv import load_dotenv
from dspy_e2b import E2BInterpreter
load_dotenv()
interpreter = E2BInterpreter()
try:
interpreter.execute("import sqlite3; db = sqlite3.connect(':memory:')")
interpreter.execute("db.execute('CREATE TABLE incidents(region TEXT)')")
interpreter.execute("db.executemany('INSERT INTO incidents VALUES (?)', [('eu',), ('eu',), ('us',)])")
counts = interpreter.execute("dict(db.execute('SELECT region, count(*) FROM incidents GROUP BY region'))")
assert counts == {"eu": 2, "us": 1}
print(counts)
finally:
interpreter.shutdown()
```
The connection survives across execution steps. `shutdown()` deletes the owned sandbox, including its processes and files.
## Run the incident-analysis RLM
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
uv run main.py
```
This uses E2B and your model provider. `main.py` configures `dspy.LM`, loads the incident records, and uses this typed signature and interpreter factory:
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import dspy
from dspy_e2b import E2BInterpreter
class IncidentAnalysis(dspy.Signature):
records: list[dict] = dspy.InputField()
query: str = dspy.InputField()
candidate_ids: list[str] = dspy.OutputField(desc="Exact SQL-filtered IDs, sorted")
queue_incident_ids: list[str] = dspy.OutputField(desc="IDs classified as queue buildup, sorted")
counts_by_region: dict[str, int] = dspy.OutputField(desc="SQL counts for queue_incident_ids")
summary: str = dspy.OutputField(desc="Short explanation grounded in the selected records")
def interpreter_factory():
return E2BInterpreter(execution_timeout=120, sandbox_timeout=600)
interpreter_factory.execution_instructions = E2BInterpreter.execution_instructions
rlm = dspy.RLM(
IncidentAnalysis,
interpreter_factory=interpreter_factory,
max_iters=6,
max_llm_calls=4,
max_output_chars=4000,
)
```
`main.py` then calls the RLM with the records and query. Each factory call returns a fresh interpreter; DSPy supplies its tools and output-field metadata and calls `shutdown()` in `finally` when the invocation ends.
## How the RLM loop works
The action model writes Python to select resolved, high-severity inference incidents in SQLite, classify candidate passages with sub-queries, and compute region counts. Execution output returns to DSPy's trajectory and informs the next action. Imports, generated variables and the SQLite connection survive between steps; the supplied input variables are freshly assigned each step.
Inside the sandbox, `llm_query` and `llm_query_batched` are Python wrappers for registered host callables. A wrapper sends a JSON tool request with an ID through the worker's stdout and waits for a matching reply. The host interpreter validates the request and runs the callable; for built-in model tools, DSPy sends the provider requests from the host. The reply returns through worker stdin, and generated Python continues with the returned value.
The successful tool-call and submission path is:
```mermaid theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
sequenceDiagram
participant H as Host: DSPy and interpreter
participant S as E2B: worker and Python state
participant P as Model provider
H->>S: Execute Python (stdin)
S->>H: Tool request + ID (stdout)
H->>H: Validate and call host tool
H->>P: Model prompts
P-->>H: Model responses
H->>S: Tool result + ID (stdin)
S->>S: Continue Python, then SUBMIT
S-->>H: Final output fields
H->>H: Validate fields and shut down
```
The generated code eventually calls `SUBMIT` with computed fields. The worker ends that code block and returns a final-output message; DSPy parses and validates the fields against `IncidentAnalysis` before returning a prediction. A validation error can become feedback for another step. The interpreter protocol carries both execution and tool messages, so guest networking and provider credentials are unnecessary. Host tools are still capabilities: each callable must validate its arguments, restrict host operations and use its own deadlines.
## Explore a larger input
The eight-record seed is a small first-run check. To explore a larger input, generate 50,000 low-severity background records locally from that seed:
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
uv run main.py --background-records 50000
```
No downloads are needed. The four high-severity candidates stay the same, so the sub-query budget stays at four. RLM inspects the data through generated code instead of putting the entire dataset in its action prompt. This mode still calls E2B and your model provider. It exercises exploration of a larger input with the same four candidates; it does not establish arbitrary long-context quality or provider superiority.
## Inspect the trajectory and observed results
`result.json` contains `answer`, `fixture_assessment` and the native trajectory. Inspect the generated code, reasoning and execution output to see whether the model used SQLite and sub-queries.
The seed has four SQL candidates and three labelled queue incidents (`INC-001`, `INC-004`, `INC-006`), with counts `{"eu": 2, "us": 1}`. `exact_ids_and_counts` checks the candidate filter, submitted-ID structure and counts for those submitted IDs. `fixture_semantic_labels_match` separately reports agreement with the fixture's queue labels. A structural check can pass even when semantic labels differ; neither check establishes model reliability. If a deterministic check fails, the run raises an error but still saves and prints the answer, trajectory and assessment error.
With `openai/gpt-5.6-luna`, one run of each configuration produced matching candidate IDs, region counts and fixture semantic labels:
| Input | Elapsed time | RLM steps | Observed behavior |
| - | - | - | - |
| Eight-record seed | 19 s | 3 | Inspect records; filter in SQLite and call `llm_query_batched` once; aggregate in SQL and SUBMIT from variables. |
| Seed plus 50,000 background records | 30 s | 5 | Inspect type/count/first/last record; filter four candidates in SQLite; call `llm_query_batched` once; aggregate in SQL; SUBMIT from variables. |
Both runs used Python 3.14, DSPy 3.4.0, E2B 2.53.1, `temperature=None`, `max_tokens=16000`, and a 20-second per-request timeout. Each configuration was run once; these results do not estimate reliability or guarantee timings.
Excerpt from the larger-input trajectory, with the SQLite connection and per-candidate prompts prepared in earlier code:
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
selected = conn.execute("""
SELECT id, region, summary
FROM incidents
WHERE service = 'inference'
AND status = 'resolved'
AND severity = 'high'
ORDER BY rowid
""").fetchall()
print("selected count:", len(selected))
print("selected IDs:", [x[0] for x in selected])
# After building one prompt per selected record:
judgments = llm_query_batched(prompts)
```
The filter printed:
```text theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
selected count: 4
selected IDs: ['INC-001', 'INC-002', 'INC-004', 'INC-006']
```
The batched answers classified `INC-002` as NO because model loading failed before requests could enter the scheduler. The final answer from that run was:
```json theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
{
"candidate_ids": [
"INC-001",
"INC-002",
"INC-004",
"INC-006"
],
"queue_incident_ids": [
"INC-001",
"INC-004",
"INC-006"
],
"counts_by_region": {
"eu": 2,
"us": 1
},
"summary": "Four resolved high-severity inference incidents were candidates. Three involved actual request queue buildup (INC-001, INC-004, INC-006); INC-002 did not because requests could not enter the scheduler. Queue incidents by region: eu=2, us=1."
}
```
### Request and execution budgets
The example caps action iterations at six and sub-queries at four. DSPy may make one additional extraction request if the model does not submit. Provider calls have a 20-second timeout, no SDK retries or adapter fallback retries and a 16,000 completion-token budget, including reasoning tokens. The example factory gives each code execution a 120-second deadline, including host-tool waiting; the adapter default remains 60 seconds. Prefer `llm_query_batched` for independent prompts, and use computed variables in `SUBMIT`. These instructions do not guarantee model compliance.
## Lifecycle and limits
### State and invocation isolation
DSPy uses one owned sandbox per invocation and calls `shutdown()` in `finally`. Every factory call must return a new interpreter. Imports, generated variables and files persist within that invocation; inputs are freshly assigned each step. Concurrent invocations use separate sandboxes. Manual callers use `try/finally` as in the SQLite check.
### Deadlines and execution failures
The execution deadline includes host-tool waiting. The default 600-second sandbox TTL is refreshed at each execution boundary, and model thinking time between executions counts toward it. Expiry fails explicitly, without recreation or replay. Syntax and generated-code errors are recoverable so RLM can try another step; setup, transport, worker death and deadline errors are terminal and trigger sandbox deletion. Oversized printed output is truncated. An oversized ordinary expression result is recoverable, so Python state remains available to inspect a smaller value. Oversized tool arguments are rejected before invoking the host callable and produce recoverable code feedback. Oversized structured `SUBMIT` output is terminal and ends the session; final fields are never truncated. Protocol and input limits remain enforced.
### Host tools and credentials
Tools and provider keys remain on the host. Killing the sandbox cannot stop an already running host callable or provider request; set their deadlines independently. Generated code and children receive EOF on standard input; worker protocol input uses a separate descriptor. This example exposes no guest network or package installation, and uses no pandas/numpy or custom template.
### Cleanup and diagnostics
The `dspy_e2b` INFO logger records `sandbox_id` and `dspy_session`. Cleanup errors carry those IDs plus original-failure/submission diagnostics. After a host crash, use the exact session tag to reconcile resources. A TTL is a backstop, not confirmation of deletion.
See the [cookbook README](https://github.com/e2b-dev/e2b-cookbook/blob/main/examples/dspy-rlms-python/README.md) for input/frame limits, host-tool concurrency, async behavior, cleanup commands and troubleshooting.
# Vercel Eve
Source: https://docs.e2b.dev/agents/eve
Run Vercel Eve agent sandboxes on E2B with the @e2b/eve-sandbox backend.
[Vercel Eve](https://vercel.com/docs/eve) is a filesystem-first framework for durable backend AI agents: you author an agent as files under `agent/`, and Eve compiles them into an app that runs on Vercel Functions. Every Eve agent has exactly one [sandbox](https://eve.dev/docs/sandbox) — the isolated bash environment rooted at `/workspace` that backs the built-in `bash`, `read_file`, `write_file`, `glob`, and `grep` tools.
The [`@e2b/eve-sandbox`](https://github.com/e2b-dev/eve-sandbox) package is an E2B backend for that sandbox — the E2B counterpart to Eve's built-in `vercel()`, `docker()`, `microsandbox()`, and `justbash()` backends. It implements the public `SandboxBackend` interface from `eve/sandbox`, so Eve itself needs no changes and no fork.
`@e2b/eve-sandbox` is experimental, and Eve is in beta. Both APIs may change.
## Why E2B as the backend
| | E2B backend | Eve's local backends |
| - | - | - |
| Where it runs | E2B cloud microVMs, from local dev and production alike | Docker daemon, local VM, or a simulated shell on the host |
| Startup | \~150 ms from a snapshot or [custom template](/template/quickstart) | Image pull / VM boot on the developer machine |
| Environment | Any [E2B template](/template/quickstart) you build | Eve's `ghcr.io/vercel/eve` image, or a Docker image you supply |
| Persistence | [Auto-pause and resume](/sandbox/persistence) — filesystem survives days of idle | Container/VM tied to the machine that started it |
| Egress control | E2B firewall: domain allow-lists and header injection | Domain policies on `vercel()`/`microsandbox()`; `docker()` is all-or-nothing |
Use it when you want the same sandbox behavior in local dev, CI, and production, on infrastructure you control the image for.
## Prerequisites
* Node.js 22 or later
* An Eve project (`npx eve@latest init my-agent`)
* An E2B API key
## Install
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
npm i @e2b/eve-sandbox e2b
```
`eve` (>= 0.27) and `ai` (>= 7) are peer dependencies already present in an Eve project.
Set your key — the backend reads `E2B_API_KEY` unless you pass `apiKey`:
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
export E2B_API_KEY="e2b_***"
```
## Configure the backend
Author `defineSandbox` and pass `e2b()` as the backend:
```typescript title="agent/sandbox.ts" theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { defineSandbox } from 'eve/sandbox'
import { e2b } from '@e2b/eve-sandbox'
export default defineSandbox({
backend: () => e2b({ template: 'base' }),
})
```
That is the whole integration. The agent's `bash` tool now runs commands in an E2B sandbox, its file tools read and write the sandbox filesystem, and nothing executes on your app runtime.
Prefer the **factory form** (`backend: () => e2b({...})`) over `backend: e2b({...})`: it defers reading environment variables until first use and memoizes the backend, so its prewarmed-snapshot cache survives across calls.
Use the folder layout (`agent/sandbox/sandbox.ts`) instead if you also seed files from `agent/sandbox/workspace/**`.
## Run the agent
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
pnpm dev # or: npx eve dev
```
Ask the agent to run a command and it executes in a real E2B sandbox. Sandboxes created this way appear in your E2B console.
## Bootstrap and per-session setup
Eve has two lifecycle hooks, and the E2B backend maps each onto a different E2B primitive:
```typescript title="agent/sandbox/sandbox.ts" theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { defineSandbox } from 'eve/sandbox'
import { e2b } from '@e2b/eve-sandbox'
export default defineSandbox({
backend: () =>
e2b({
template: 'base',
timeoutMs: 30 * 60 * 1000,
envs: { NODE_ENV: 'production' },
}),
// Build time, once per template — baked into a reusable E2B snapshot.
async bootstrap({ use }) {
const sandbox = await use()
await sandbox.run({ command: 'sudo apt-get install -y jq' })
},
// Once per durable session — tighten egress for the turn.
async onSession({ use }) {
await use({ networkPolicy: { allow: ['*.npmjs.org', 'github.com'] } })
},
})
```
* **`bootstrap`** runs during Eve's build-time `prewarm()`. The backend creates a sandbox, runs your bootstrap, writes your `workspace/` seed files, then captures an **E2B snapshot**. Later sessions fork that snapshot, so installs are paid once, not per session.
* **`onSession`** runs once per durable session against a live sandbox, and is the right place for network policy, per-user credentials, and one-time markers.
Snapshots are named from Eve's `templateKey` (which tracks your authored sandbox source, seed contents, and `revalidationKey`) plus a hash of the snapshot-affecting backend options — base template, baked `envs`, and network policy. Change any of those and the next build captures a fresh snapshot instead of reusing a stale one.
## Session persistence
E2B sandboxes are created with `lifecycle: { onTimeout: 'pause', autoResume: true }`, and the default timeout is **30 minutes** (E2B's own 5-minute default would expire mid-turn). On timeout the sandbox [pauses rather than dies](/sandbox/persistence): the filesystem is preserved and the next message resumes it.
On the next turn the backend reattaches in this order:
1. The `sandboxId` persisted by Eve for that session.
2. A still-running or paused sandbox whose E2B metadata matches `eveBackend: "e2b"` and the session's `eveSessionKey`.
3. Otherwise a fresh sandbox, forked from the prewarmed snapshot.
Two consequences worth knowing:
* **The backend's `shutdown()` is intentionally a no-op.** Killing the sandbox on server shutdown would drop background work and force Eve to recreate it without rerunning `onSession`. Sandboxes are left paused and expire on their own — kill them from the console or the [SDK](/sandbox) if you want them gone sooner. Opt out with `autoPause: false` to kill on timeout instead.
* **Reconnects do not re-apply the configured `networkPolicy`.** A resumed sandbox keeps its live policy, including any tightening `onSession` applied. Re-stamping the create-time default could silently loosen a locked-down session.
## Network policy
Egress rules go on the factory (applied to each fresh session, before authored `bootstrap` runs) or in `onSession`'s `use()`. The backend translates Eve's `SandboxNetworkPolicy` into an [E2B firewall](/network/internet-access) update:
| Eve policy | E2B update |
| - | - |
| `"allow-all"` (default) | `allowInternetAccess: true` |
| `"deny-all"` | `allowInternetAccess: false` |
| `{ allow: ['github.com', '*.npmjs.org'] }` | `allowOut: [...]` + `denyOut: ['0.0.0.0/0']` |
| `{ allow: ['*'], subnets: { deny: ['10.0.0.0/8'] } }` | `denyOut: ['10.0.0.0/8']` |
| `{ allow: [] }` | `allowInternetAccess: false` |
Restricting egress to an allow-list requires the catch-all in `denyOut` — E2B gives allow rules absolute precedence over deny rules, so the listed hosts pass and everything else is blocked. The backend adds that for you.
Because allow beats deny in E2B, some Eve policies cannot be expressed faithfully. The backend **throws instead of silently weakening them**:
* `subnets.deny` combined with an allow-list — the "denied" hosts would stay reachable. Use `{ allow: ['*'], subnets: { deny: [...] } }` for deny-lists, or drop `subnets.deny` (anything outside an allow-list is already blocked).
* A per-rule `match` condition — E2B applies header transforms to every request to a host.
* `forwardURL` request proxying — no E2B equivalent.
* A header `transform` alongside a catch-all `"*"` allow — a transform host must be allow-listed, which an allow-all cannot also be.
### Credential brokering
E2B can inject a header at the firewall so a secret authenticates egress without ever entering the sandbox. Eve expresses this as a per-domain `transform`:
```typescript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
async onSession({ use }) {
await use({
networkPolicy: {
allow: {
'github.com': [{ transform: [{ headers: { authorization: 'Basic your_base64_credentials_here' } }] }],
'api.example.com': [],
},
},
})
}
```
List every host explicitly. Eve's documented `"*": []` catch-all pattern is rejected by this backend for the reason above — pair transforms with an explicit allow-list instead.
To change policy mid-turn, call `sandbox.setNetworkPolicy(...)` on the live handle from any authored tool.
## Custom templates
`template` accepts any E2B template ID or alias. Build a [custom template](/template/quickstart) with your runtimes, system packages, and toolchain pre-installed, and every session starts from it:
```typescript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
export default defineSandbox({
backend: () => e2b({ template: 'your-template-id-or-name' }),
})
```
Templates and snapshots compose: the template is the base image, and Eve's `bootstrap` layers your agent-specific setup into a snapshot on top of it. Put slow, stable work (compilers, system packages) in the template; put agent-specific work (cloning a baseline repo, installing project dependencies) in `bootstrap`.
## Backend options
```typescript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
e2b({
template: 'base', // E2B template ID or alias
timeoutMs: 30 * 60 * 1000, // auto-pause timeout; default 30 minutes
envs: { NODE_ENV: 'production' }, // env vars baked into every sandbox
metadata: { team: 'growth' }, // E2B metadata, create-time only
tags: { tier: 'beta' }, // diagnostic tags, merged into metadata
apiKey: process.env.E2B_API_KEY, // defaults to E2B_API_KEY
domain: 'e2b.dev', // self-hosted or region endpoint
networkPolicy: 'allow-all', // initial policy for each fresh session
autoPause: true, // false → kill on timeout instead of pause
createOptions: {}, // raw E2B SandboxOpts escape hatch
})
```
`createOptions` is merged into every `Sandbox.create()` call, so anything the [E2B JavaScript SDK](/sdk-reference/js-sdk) supports is reachable even when this backend has no dedicated option for it. Explicit options win over `createOptions`.
## How the integration works
| Component | Responsibility |
| - | - |
| Eve runtime | Runs turns, owns durable session state, calls the backend's `prewarm` and `create` |
| `defineSandbox` | Declares the backend plus the `bootstrap` and `onSession` hooks |
| `e2b()` backend | Creates, reconnects, and configures E2B sandboxes; maps network policy |
| E2B snapshot | Captures bootstrap + seed files once, forked per session |
| E2B template | Base OS, runtimes, and pre-installed dependencies |
Paths line up because the backend anchors relative paths to `/workspace`, Eve's cross-backend namespace — E2B's own default working directory is `/home/user`, and the backend creates `/workspace` during base setup. Every E2B API call this backend makes is tagged with an `eve-sandbox/` integration identifier.
## Learn more
* [`@e2b/eve-sandbox` on GitHub](https://github.com/e2b-dev/eve-sandbox) — source, unit tests, and a runnable Next.js example
* [Eve sandbox documentation](https://eve.dev/docs/sandbox) — session handle API, lifecycle hooks, and backends
* [Eve on Vercel](https://vercel.com/docs/eve) — how Eve maps onto Vercel Functions, Workflows, and AI Gateway
## Related guides
Build custom sandbox templates with pre-installed dependencies
Auto-pause, resume, and manage sandbox lifecycle
Restrict sandbox egress with domain allow-lists
# Flue
Source: https://docs.e2b.dev/agents/flue
Give a Flue agent an E2B sandbox with the flue add sandbox e2b blueprint adapter.
[Flue](https://flueframework.com) is a TypeScript agent framework from the Astro team. An agent is a function marked with `'use agent'` and configured with React-like hooks: `useModel()` picks the LLM, `useSandbox()` attaches a workspace. That second hook is what gives the agent its `read`, `write`, `edit`, `bash`, `grep`, and `glob` tools — without it the agent has no filesystem and no shell at all.
Flue's [E2B adapter](https://flueframework.com/docs/ecosystem/sandboxes/e2b/) points those tools at an E2B sandbox. It ships as a **blueprint**: `flue add sandbox e2b` prints a Markdown implementation guide that your coding agent applies, leaving the adapter in your repo as `src/sandboxes/e2b.ts`. There is no `@e2b/flue` package — the only E2B dependency is the [`e2b`](/sdk-reference/js-sdk) SDK.
Flue adapters are deliberately thin. Your application creates the E2B sandbox and decides when it dies; Flue wraps the handle you hand it and never destroys provider infrastructure.
## Why E2B as the sandbox
Flue ships two built-in environments — an in-memory [just-bash](https://github.com/vercel-labs/just-bash) sandbox and `local()`, which binds the agent to your host.
| | E2B | `bash()` virtual sandbox | `local()` |
| - | - | - | - |
| Where commands run | Firecracker microVM in E2B's cloud | Emulated in your Node.js process | Your host, as real processes |
| Isolation | Full VM boundary | No host access, but no real processes either | None, by design |
| Toolchain | Debian 12, `apt`, any runtime you install or bake into a [template](/template/quickstart) | The unix subset just-bash implements | Whatever the host already has |
| Filesystem lifetime | Lives with the sandbox: up to 24 hours running (1 hour on the Base tier), indefinitely once [paused](/sandbox/persistence) | Rebuilt empty every time the agent initializes | The host disk |
| Per-conversation workspaces | One sandbox keyed on the agent instance id | Ephemeral | Every conversation shares one host |
Use E2B when agent-written code must not touch your host, when the work needs a real Linux toolchain, or when each conversation needs a workspace that outlives the process that started it.
## Prerequisites
* Node.js 22.19 or later
* An E2B API key
* A model provider key — this guide uses Anthropic
## Create a project
Skip to the next section if you already have a Flue project.
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
mkdir flue-e2b-agent && cd flue-e2b-agent
npx @flue/cli@latest init . --target node
npm install
npm install e2b
```
`flue init` writes `flue.config.ts`, `package.json`, `tsconfig.json`, `.env`, `src/agents/hello.ts`, `src/db.ts`, `AGENTS.md`, and `README.md`. It does not install anything, hence the separate `npm install`.
Put both keys in `.env` — `flue run` loads it automatically, and the E2B SDK reads `E2B_API_KEY` from the environment:
```bash title=".env" theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
E2B_API_KEY="e2b_***"
ANTHROPIC_API_KEY="sk-ant-***"
```
## Add the adapter
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
npx flue add sandbox e2b --print | claude
```
`flue add` is not a package installer. It fetches the blueprint from Flue's registry and prints it; the coding agent you pipe it into writes the file. Swap `claude` for `codex`, `opencode`, or whatever you use — or read the guide yourself with `npx flue add sandbox e2b --print | less`. Inside a coding agent session the CLI detects that and writes to stdout without `--print`.
The result is `src/sandboxes/e2b.ts`, tagged `// flue-blueprint: sandbox/e2b@1`, exporting `e2b(sandbox)` — a `SandboxFactory` rooted at `/home/user`. It implements Flue's `SandboxDriver` directly against the E2B SDK rather than shelling out:
| Flue driver operation | E2B call |
| - | - |
| `readFile`, `writeFile` | `sandbox.files.read`, `sandbox.files.write` |
| `readdir`, `exists`, `stat` | `sandbox.files.list`, `.exists`, `.getInfo` |
| `mkdir` | `sandbox.files.makeDir` (always recursive on E2B) |
| `rm` | `sandbox.files.remove` — throws on `recursive` or `force`, which E2B's direct remove API has no equivalent for |
| `exec` | `sandbox.commands.run`, with `timeoutMs` forwarded unchanged |
To pull in a later revision of the adapter, pipe `npx flue update sandbox e2b --print` into your coding agent the same way; the marker comment is how the guide recognizes an existing install.
## Write the agent
The factory is where you own the sandbox: create it with the E2B SDK, then hand it to `e2b()`.
```typescript title="src/agents/analyst.ts" theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
'use agent';
import { useModel, useSandbox } from '@flue/runtime';
import { Sandbox } from 'e2b';
import { e2b } from '../sandboxes/e2b.ts';
export function Analyst() {
useModel('anthropic/claude-sonnet-4-6');
useSandbox({
// Lazy: createSandbox() runs once, when the runtime initializes the
// agent — not on every render.
async createSandbox(options) {
const sandbox = await Sandbox.create({
timeoutMs: 10 * 60_000,
metadata: { framework: 'flue', agent: 'analyst' },
});
console.error(`[e2b] sandbox ${sandbox.sandboxId}`);
return e2b(sandbox).createSandbox(options);
},
});
return 'You work in a Linux sandbox. Use bash for anything about the machine or its files, and quote the exact command output in your answer.';
}
```
Two details that matter:
* **`createSandbox` is called once per agent initialization**, never on a re-render. Creating the sandbox inside it — not in the module body — is what keeps one sandbox per conversation instead of one per render.
* **`timeoutMs` is E2B's, not Flue's.** The default sandbox timeout is 5 minutes; a conversational agent usually wants more. Nothing in Flue extends it for you.
## Run it
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
npx flue run src/agents/analyst.ts --message "Which Linux kernel is this box running, and how much free disk does /home/user have? Run the commands."
```
Flue streams tool activity to stderr and the final reply to stdout:
```
[e2b] sandbox ic1av8osjd5gtxrsegyfz
tool bash
tool done bash
1. Linux Kernel: 6.1.158+
2. Free Disk Space for /home/user: 22 GB total, 1.3 GB used, 20 GB available
```
The sandbox is now in your E2B console. The metadata you set at creation is how you find it again from the SDK:
```typescript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const [sandbox] = await Sandbox.list({
query: { metadata: { framework: 'flue' }, state: ['running', 'paused'] },
}).nextItems()
console.log(sandbox.sandboxId, sandbox.state) // ic1av8osjd5gtxrsegyfz running
```
## Keep the same sandbox across turns
Flue conversations persist across `flue run` invocations. Sandboxes do not, unless you make them: the default factory above creates a fresh one every time the agent initializes. `createSandbox(options)` receives `options.id` — the agent instance id — so store it in metadata and look it up first.
```typescript title="src/agents/durable.ts" theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
'use agent';
import { useModel, useSandbox } from '@flue/runtime';
import { Sandbox } from 'e2b';
import { e2b } from '../sandboxes/e2b.ts';
export function Durable() {
useModel('anthropic/claude-sonnet-4-6');
useSandbox({
async createSandbox(options) {
// `options.id` is the agent instance id — key the sandbox on it so
// the next message in this conversation gets the same workspace.
const [existing] = await Sandbox.list({
query: { metadata: { flueInstance: options.id }, state: ['running', 'paused'] },
}).nextItems();
// connect() never shortens the window; passing timeoutMs extends it
// back to a full 10 minutes for this turn.
const sandbox = existing
? await Sandbox.connect(existing.sandboxId, { timeoutMs: 10 * 60_000 })
: await Sandbox.create({
timeoutMs: 10 * 60_000,
metadata: { flueInstance: options.id },
});
console.error(`[e2b] ${existing ? 'reconnected' : 'created'} ${sandbox.sandboxId}`);
return e2b(sandbox).createSandbox(options);
},
});
return 'You work in a Linux sandbox at /home/user. Use bash.';
}
```
Two runs of the same conversation id now share one workspace, across separate processes:
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
npx flue run src/agents/durable.ts --id demo-1 --message "Write the text 'hello from turn one' to /home/user/note.txt, then confirm."
# [e2b] created ivk69fg95t9bndyf9odko
npx flue run src/agents/durable.ts --id demo-1 --message "cat /home/user/note.txt and quote it exactly."
# [e2b] reconnected ivk69fg95t9bndyf9odko
# > hello from turn one
```
Including `'paused'` in the state filter covers longer gaps: `Sandbox.connect()` resumes a [paused](/sandbox/persistence) sandbox with its filesystem intact, so a conversation can go quiet for days and come back to the same disk.
Reconnecting can only ever lengthen a sandbox's window. `connect()` keeps whatever time is left and raises it to the requested timeout — the 5-minute default, or the `timeoutMs` you pass — when the remainder is shorter than that. A sandbox with 20 minutes left keeps its 20 minutes either way.
## Use a custom template
`Sandbox.create()` takes a [template](/template/quickstart) name or ID as its first argument. Bake the language runtimes, system packages, and repo checkout your agent needs into a template so no turn is spent on `apt-get`:
```typescript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
const sandbox = await Sandbox.create('your-template-name', {
timeoutMs: 10 * 60_000,
})
```
## Lifecycle is your job
The adapter never creates or kills anything — it only wraps the sandbox you pass in. What that leaves you:
* **Timeout is the backstop.** `timeoutMs` at creation decides how long an unattended sandbox lives; the sandbox dies at that point unless you [pause](/sandbox/persistence) it.
* **Nothing kills the sandbox when the agent finishes.** `flue run` exits and the sandbox keeps running. Call `sandbox.kill()` from a harness tool, from your server's teardown, or from a sweep keyed on metadata.
```typescript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
for (const sandbox of await Sandbox.list({
query: { metadata: { framework: 'flue' }, state: ['running', 'paused'] },
}).nextItems()) {
await Sandbox.kill(sandbox.sandboxId)
}
```
## Troubleshooting
The blueprint's `exec()` calls `sandbox.commands.run()` without a `try/catch`, and the E2B SDK **throws** `CommandExitError` on any non-zero exit instead of returning a result. Flue turns that throw into a bare tool error, so the model sees `exit status 1` and never sees stderr.
Unwrap the error into the result the `SandboxDriver` contract expects:
```typescript title="src/sandboxes/e2b.ts" theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { CommandExitError } from 'e2b';
// inside E2BSandboxDriver.exec()
try {
const result = await this.sandbox.commands.run(command, {
cwd: options?.cwd,
envs: options?.env,
timeoutMs: options?.timeoutMs,
});
return {
stdout: result.stdout ?? '',
stderr: result.stderr ?? '',
exitCode: result.exitCode ?? 0,
};
} catch (error) {
if (error instanceof CommandExitError) {
return {
stdout: error.stdout ?? '',
stderr: error.stderr ?? '',
exitCode: error.exitCode ?? 1,
};
}
throw error;
}
```
The same `cat` of a missing file then reaches the model as `cat: /home/user/does-not-exist.txt: No such file or directory` plus the exit code — which is what it needs to fix its own command.
```
AuthenticationError: API key is required, please visit the API Keys tab at
https://console.e2b.dev/?tab=keys to get your API key.
```
The E2B SDK reads `E2B_API_KEY` from `process.env`. `flue run` loads the project's `.env` for you (`--env ` picks a different one). Anywhere else — a dev server, a built server, CI — put the key in the process environment yourself, or pass it explicitly with `Sandbox.create({ apiKey: process.env.MY_KEY })`.
The blueprint's driver class uses a TypeScript parameter property (`constructor(private sandbox: E2BSandbox) {}`), which Node.js's built-in type stripping refuses to compile — including on Node 24:
```
ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX
```
`flue run` compiles the file properly, so agents are unaffected. A standalone script that imports the adapter directly needs `node --experimental-transform-types`.
```
SandboxOperationUnsupportedError: E2B does not support rm with "recursive".
```
E2B's `files.remove()` has no recursive or force flags, so the adapter rejects both rather than silently ignoring them. When a tree has to go, let the agent run `rm -rf` through `bash` instead — that path goes through `commands.run()` and works.
## How the integration works
| Component | Responsibility |
| - | - |
| Flue runtime | Runs turns, persists the conversation, calls `createSandbox()` once per initialization |
| `useSandbox()` | Attaches the environment and adds the file and shell tools to the agent |
| Your factory | Creates or reconnects the E2B sandbox and owns its lifetime |
| `src/sandboxes/e2b.ts` | Maps Flue's `SandboxDriver` onto `sandbox.files` and `sandbox.commands` |
| E2B template | Base OS, runtimes, and pre-installed dependencies |
## Learn more
* [Flue: Sandboxes guide](https://flueframework.com/docs/guide/sandboxes/) — `useSandbox()`, the virtual sandbox, and `local()`
* [Flue: E2B adapter](https://flueframework.com/docs/ecosystem/sandboxes/e2b/) — the blueprint's own reference page
* [Flue: Sandbox Adapter API](https://flueframework.com/docs/reference/sandbox-api/) — the `SandboxDriver` contract the adapter implements
* [`flue add`](https://flueframework.com/docs/cli/add/) and [`flue run`](https://flueframework.com/docs/cli/run/) — the two CLI commands used here
## Related guides
Build custom sandbox templates with pre-installed dependencies
Pause, resume, and manage sandbox lifecycle
The `e2b` package the adapter is built on
# Google ADK
Source: https://docs.e2b.dev/agents/google-adk
Give Google ADK agents a secure E2B sandbox to run code, commands, and files in.
[Google ADK](https://google.github.io/adk-docs/) (Agent Development Kit) is
Google's framework for building agents, with Gemini as the default model. The
[E2B](https://e2b.dev/) integration gives ADK workloads an isolated remote
workspace for running code and commands instead of executing them on your host.
## Choose an integration
Use Google's built-in E2B environment for direct shell and file operations
Give an ADK agent ready-made code, command, file, and background-process tools
Choose the **native environment** when your application directly controls the
sandbox. Choose the **advanced plugin** when the agent itself should decide when
to run code, manage files, or start a service.
## Native: Google ADK environment
Google ADK includes the experimental
[`E2BEnvironment`](https://github.com/google/adk-python/tree/main/src/google/adk/integrations/e2b)
for direct command execution and file access through ADK's environment API. No
additional integration package is required.
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
pip install "google-adk[e2b]>=2.3.0"
```
Create and close the environment explicitly in your application:
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import asyncio
from google.adk.integrations.e2b import E2BEnvironment
async def main() -> None:
env = E2BEnvironment()
await env.initialize()
try:
await env.write_file("hello.py", 'print("Hello from E2B")\n')
result = await env.execute("python hello.py")
print(result.stdout)
finally:
await env.close()
asyncio.run(main())
```
This path exposes the sandbox as a low-level workspace: your application calls
`execute`, `read_file`, and `write_file` itself.
## Advanced: E2B plugin
The [`e2b-adk`](https://pypi.org/project/e2b-adk/) plugin exposes sandbox
operations as tools the model can call. It adds stateful code execution, shell
commands, file management, and background processes backed by the
[E2B Code Interpreter](/). The plugin creates one sandbox lazily, shares it
across tool calls, and closes it with the ADK runner.
To use E2B with ADK:
1. Create an `E2BPlugin`.
2. Hand `plugin.get_tools()` to your `Agent` and register the plugin on the `App`.
3. Run the agent — every tool call executes inside the sandbox, and the plugin
creates and tears the sandbox down for you.
### Install the dependencies
Install the plugin. It pulls in Google ADK and the E2B Code Interpreter SDK.
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
pip install e2b-adk
```
You need an E2B API key for the sandbox and a Gemini key for the model.
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
export E2B_API_KEY="..."
export GOOGLE_API_KEY="..."
```
## Example: Data analysis agent
A good fit for a sandbox is analysis the model shouldn't do in its head. Here the
agent is given a raw dataset and a question. Rather than eyeballing the numbers,
it writes the data to a file, runs real pandas against it, and reports figures it
actually computed — the sandbox is a calculator it can't fool.
### Create the plugin and agent
The plugin owns the sandbox. Pass its tools to the `Agent` and register it on the
`App`; the instruction is what makes the agent *run* code rather than guess.
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from google.adk.agents import Agent
from google.adk.apps import App
from e2b_adk import E2BPlugin
INSTRUCTION = """You are a data analyst. You never guess numbers — you compute
them. Save any data you are given to a file with write_file, analyse it by
running real pandas with run_code, fix and re-run if the code errors, and report
the figures you computed. Never report a number you have not verified by running
code."""
plugin = E2BPlugin()
agent = Agent(
model="gemini-2.5-flash",
name="data_analyst",
instruction=INSTRUCTION,
tools=plugin.get_tools(),
)
app = App(name="data_analysis", root_agent=agent, plugins=[plugin])
```
### Run the analysis
Run the agent inside an `InMemoryRunner`. The sandbox is created lazily on the
first tool call, kept alive while the agent works, and killed when the
`async with` block exits — you never manage it yourself.
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from google.adk.runners import InMemoryRunner
DATASET = """month,region,marketing_spend,revenue
2024-01,North,12000,48000
2024-02,North,15000,61000
2024-03,North,9000,37000
2024-04,North,18000,72000
2024-01,South,8000,26000
2024-02,South,11000,30000
2024-03,South,14000,33000
2024-04,South,17000,38000"""
async with InMemoryRunner(app=app) as runner:
# run_debug prints the conversation as it runs.
await runner.run_debug(
f"Here is marketing spend and revenue as CSV:\n\n{DATASET}\n\n"
"Save it to sales.csv, then tell me which region converts spend into "
"revenue more efficiently and which month performed best."
)
```
The agent writes `sales.csv` with `write_file`, then uses `run_code` to load it
with pandas, compute revenue-per-dollar and per-month totals, and answer in
prose — every number backed by an execution in the sandbox:
```text theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
data_analyst > North converts marketing spend into revenue more efficiently —
about $4.0 of revenue per $1 of spend versus roughly $2.5 for South. The best
single month was 2024-04 in North: $72,000 revenue from $18,000 spend.
```
Pass `verbose=True` to `run_debug` to also print each tool call and its result
as the agent works.
### Full example
```python expandable theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import asyncio
from google.adk.agents import Agent
from google.adk.apps import App
from google.adk.runners import InMemoryRunner
from e2b_adk import E2BPlugin
INSTRUCTION = """You are a data analyst. You never guess numbers — you compute
them. Save any data you are given to a file with write_file, analyse it by
running real pandas with run_code, fix and re-run if the code errors, and report
the figures you computed. Never report a number you have not verified by running
code."""
DATASET = """month,region,marketing_spend,revenue
2024-01,North,12000,48000
2024-02,North,15000,61000
2024-03,North,9000,37000
2024-04,North,18000,72000
2024-01,South,8000,26000
2024-02,South,11000,30000
2024-03,South,14000,33000
2024-04,South,17000,38000"""
async def main() -> None:
plugin = E2BPlugin(metadata={"example": "data-analysis"})
agent = Agent(
model="gemini-2.5-flash",
name="data_analyst",
instruction=INSTRUCTION,
tools=plugin.get_tools(),
)
app = App(name="data_analysis", root_agent=agent, plugins=[plugin])
async with InMemoryRunner(app=app) as runner:
# run_debug prints the conversation as it runs.
await runner.run_debug(
f"Here is marketing spend and revenue as CSV:\n\n{DATASET}\n\n"
"Save it to sales.csv, then tell me which region converts spend "
"into revenue more efficiently and which month performed best."
)
asyncio.run(main())
```
## Example: Code generation agent
The same tools support a code generator that verifies its own work. The
instruction tells the agent to execute every snippet in the sandbox before
returning it, so what you get back has already run and passed its tests — no
untested code reaches the user.
```python expandable theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import asyncio
from google.adk.agents import Agent
from google.adk.apps import App
from google.adk.runners import InMemoryRunner
from e2b_adk import E2BPlugin
INSTRUCTION = """You are a code generator that returns verified, working code.
For every request: (1) write the function, (2) write tests, (3) EXECUTE in the
sandbox with run_code, (4) if it fails, fix and re-run until tests pass,
(5) return ONLY the final function. Never return code you haven't executed."""
async def main() -> None:
plugin = E2BPlugin(metadata={"example": "code-generator"})
agent = Agent(
model="gemini-2.5-flash",
name="codegen",
instruction=INSTRUCTION,
tools=plugin.get_tools(),
)
app = App(name="codegen", root_agent=agent, plugins=[plugin])
async with InMemoryRunner(app=app) as runner:
# run_debug prints the conversation as it runs.
await runner.run_debug(
"Write a Python function group_by(items, key) that groups a list "
"by a key function."
)
asyncio.run(main())
```
## Tools
`plugin.get_tools()` returns six tools, all sharing the plugin's single sandbox
so state persists across calls. Each returns a dict with a `success` flag and
reports failures in the result rather than raising, so a bad call never aborts
the agent run. `success` means the tool *ran*: code that raised or a command
that exited non-zero still returns `success: True` with the failure captured in
`error` / `exit_code` — only a call that could not run at all returns
`success: False`.
| Tool | Does |
| - | - |
| `run_code` | Run code in a stateful kernel (variables persist across calls) |
| `run_command` | Run a shell command |
| `write_file` / `read_file` | Write and read files in the sandbox |
| `list_files` | List a directory |
| `start_background_command` | Start a long-running process, with an optional preview URL for a port |
## Configuration
`E2BPlugin` accepts keyword-only options, all optional. Anything you don't set
falls back to the E2B SDK's own default — the plugin overrides none of them.
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
plugin = E2BPlugin(
api_key=None, # defaults to the E2B_API_KEY env var
template=None, # E2B sandbox template
metadata=None, # dict[str, str] attached to the sandbox
envs=None, # environment variables inside the sandbox
timeout=None, # sandbox timeout in seconds (re-applied on every tool call)
lifecycle=None, # what happens on timeout — see below
# ...every other AsyncSandbox.create() option is forwarded verbatim
)
```
The plugin keeps the sandbox alive while the agent is working: every tool call
pushes the expiry window forward by `timeout` (E2B's default is 300s). An idle
gap longer than `timeout` still expires the sandbox under E2B's default
lifecycle — pass
`lifecycle={"on_timeout": {"action": "pause"}, "auto_resume": True}` to pause
and auto-resume across idle gaps instead. See the
[repository README](https://github.com/e2b-dev/e2b-adk-plugin#configuration)
for the full option list.
## Reference examples
Complete, runnable scripts live in the plugin repository.
Compute real answers from a dataset with pandas in the sandbox
Return only code that has been executed and tested
# Grok Build
Source: https://docs.e2b.dev/agents/grok
Run Grok Build in a secure E2B sandbox with full filesystem, terminal, and git access.
[Grok Build](https://x.ai/cli) is xAI's coding agent and CLI. E2B provides a pre-built `grok` template with Grok Build already installed.
## CLI
Create a sandbox with the [E2B CLI](/cli).
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
e2b sbx create grok
```
Once inside the sandbox, start Grok Build.
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
grok
```
## Run headless
Use `-p` for non-interactive mode and `--always-approve` to auto-approve all tool calls. The sandbox isolates the agent from your host machine, but sandboxes can reach the open internet by default — restrict outbound traffic with [network rules](/network/internet-access). Grok Build authenticates with an API key from the [xAI console](https://console.x.ai) via the `XAI_API_KEY` environment variable.
Auto-approving tool calls is contained by the sandbox: the agent cannot touch your host machine, local files, or credentials. It can still make outbound network requests — internet access is enabled by default. To limit where an auto-approved agent can connect, configure [outbound network rules](/network/internet-access) (`allowInternetAccess`, plus allow/deny lists by CIDR or hostname). Hostname rules apply to HTTP(S) traffic only; use CIDR rules for other protocols.
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const sandbox = await Sandbox.create('grok', {
envs: { XAI_API_KEY: process.env.XAI_API_KEY },
})
const result = await sandbox.commands.run(
`grok --always-approve -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("grok", envs={
"XAI_API_KEY": os.environ["XAI_API_KEY"],
})
result = sandbox.commands.run(
'grok --always-approve -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('grok', {
envs: { XAI_API_KEY: process.env.XAI_API_KEY },
timeoutMs: 600_000,
})
await sandbox.commands.run(
'git clone --depth 1 https://x-access-token:$GITHUB_TOKEN@github.com/your-org/your-repo.git /home/user/repo',
{ envs: { GITHUB_TOKEN: process.env.GITHUB_TOKEN } }
)
const result = await sandbox.commands.run(
`cd /home/user/repo && grok --always-approve -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("grok", envs={
"XAI_API_KEY": os.environ["XAI_API_KEY"],
}, timeout=600)
sandbox.commands.run(
"git clone --depth 1 https://x-access-token:$GITHUB_TOKEN@github.com/your-org/your-repo.git /home/user/repo",
envs={"GITHUB_TOKEN": os.environ["GITHUB_TOKEN"]},
)
result = sandbox.commands.run(
'cd /home/user/repo && grok --always-approve -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()
```
## 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 `grok` template.
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
// template.ts
import { Template } from 'e2b'
export const template = Template()
.fromTemplate('grok')
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
# template.py
from e2b import Template
template = (
Template()
.from_template("grok")
)
```
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
// build.ts
import { Template, defaultBuildLogger } from 'e2b'
import { template as grokTemplate } from './template'
await Template.build(grokTemplate, 'my-grok', {
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 grok_template
Template.build(grok_template, "my-grok",
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
Connect to the sandbox via SSH for interactive sessions
# Hermes Agent
Source: https://docs.e2b.dev/agents/hermes
Run Hermes Agent in a secure E2B sandbox with persistent sessions, memory, and self-improving skills.
[Hermes Agent](https://github.com/NousResearch/hermes-agent) is an open-source, model-agnostic agent with terminal and file tools, persistent memory, resumable sessions, and reusable skills it can create and improve. E2B provides a pre-built `hermes` template with the Hermes CLI and bundled skills already installed.
## CLI
Create a sandbox with the [E2B CLI](/cli).
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
e2b sbx create hermes
```
Once inside the sandbox, configure a provider or start Hermes if your credentials are already available.
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
hermes model
hermes
```
## Run headless
Use `chat -q` for a single non-interactive turn and `-Q` to keep stdout suitable for scripts. `--yolo` lets Hermes use terminal, file, browser, memory, and skill tools without waiting for approval. The sandbox isolates those tools from your host machine, but sandboxes can reach the open internet by default — restrict outbound traffic with [network rules](/network/internet-access) when the agent processes untrusted input or handles secrets.
Hermes supports multiple model providers. The examples below use OpenRouter; pass a different provider key and select it with `--provider` and `--model` if needed.
Auto-approved tool calls can modify the sandbox filesystem and make outbound requests, but cannot access your host files or credentials unless you explicitly copy or pass them into the sandbox. Use [outbound network rules](/network/internet-access) to limit access by CIDR or hostname. Hostname rules apply to HTTP(S) traffic only; use CIDR rules for other protocols.
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const sandbox = await Sandbox.create('hermes', {
envs: { OPENROUTER_API_KEY: process.env.OPENROUTER_API_KEY },
timeoutMs: 600_000,
})
const result = await sandbox.commands.run(
`hermes chat -Q --yolo --provider openrouter --model anthropic/claude-sonnet-4.6 -q "Inspect this workspace and summarize its purpose"`
)
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("hermes", envs={
"OPENROUTER_API_KEY": os.environ["OPENROUTER_API_KEY"],
}, timeout=600)
result = sandbox.commands.run(
'hermes chat -Q --yolo --provider openrouter '
'--model anthropic/claude-sonnet-4.6 '
'-q "Inspect this workspace and summarize its purpose"',
)
print(result.stdout)
sandbox.kill()
```
## Teach Hermes a reusable skill
Hermes can turn a successful procedure into a skill and reuse it in a fresh session. Keep both turns in one E2B sandbox so `~/.hermes`, the workspace, and the created skill remain available.
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const sandbox = await Sandbox.create('hermes', {
envs: { OPENROUTER_API_KEY: process.env.OPENROUTER_API_KEY },
timeoutMs: 900_000,
})
await sandbox.commands.run('mkdir -p /home/user/incidents')
await sandbox.files.write('/home/user/runbook.md', `
# Checkout incident runbook
- Compare error rates by endpoint and release.
- Check dependency latency before proposing a rollback.
- Write findings to /home/user/reports/incident.md.
`)
await sandbox.files.write('/home/user/incidents/first.json', JSON.stringify({
release: 'checkout-42',
errorRate: 0.08,
paymentsP95Ms: 1700,
}))
const common =
'hermes chat -Q --yolo --provider openrouter ' +
'--model anthropic/claude-sonnet-4.6 '
await sandbox.commands.run(
common +
`-q "Study /home/user/runbook.md, apply it to /home/user/incidents/first.json, and create a reusable skill named incident-triage"`,
)
await sandbox.files.write('/home/user/incidents/next.json', JSON.stringify({
release: 'checkout-43',
errorRate: 0.11,
errorsByRelease: { 'checkout-42': 3, 'checkout-43': 417 },
}))
const nextIncident = await sandbox.commands.run(
common +
`--skills incident-triage -q "Use the incident-triage skill to investigate the newest incident"`,
)
console.log(nextIncident.stdout)
await sandbox.kill()
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import json
import os
from e2b import Sandbox
sandbox = Sandbox.create("hermes", envs={
"OPENROUTER_API_KEY": os.environ["OPENROUTER_API_KEY"],
}, timeout=900)
sandbox.commands.run("mkdir -p /home/user/incidents")
sandbox.files.write("/home/user/runbook.md", """
# Checkout incident runbook
- Compare error rates by endpoint and release.
- Check dependency latency before proposing a rollback.
- Write findings to /home/user/reports/incident.md.
""")
sandbox.files.write("/home/user/incidents/first.json", json.dumps({
"release": "checkout-42",
"error_rate": 0.08,
"payments_p95_ms": 1700,
}))
common = (
"hermes chat -Q --yolo --provider openrouter "
"--model anthropic/claude-sonnet-4.6 "
)
sandbox.commands.run(
common
+ '-q "Study /home/user/runbook.md, apply it to '
'/home/user/incidents/first.json, and create a reusable skill named '
'incident-triage"',
)
sandbox.files.write("/home/user/incidents/next.json", json.dumps({
"release": "checkout-43",
"error_rate": 0.11,
"errors_by_release": {"checkout-42": 3, "checkout-43": 417},
}))
next_incident = sandbox.commands.run(
common
+ '--skills incident-triage '
'-q "Use the incident-triage skill to investigate the newest incident"',
)
print(next_incident.stdout)
sandbox.kill()
```
Hermes stores user-created skills under `~/.hermes/skills/` and curated memory under `~/.hermes/memories/`. Both remain available for the lifetime of the sandbox. If you pause and resume the same sandbox, its filesystem state is preserved; killing the sandbox removes it.
## Resume a session
Hermes saves conversation sessions inside the sandbox. Use `-c` to continue the most recent session, or `--resume ` to select one by ID or title.
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
hermes chat -Q --yolo -c -q "Continue the investigation and implement the first fix"
```
## How it works
Hermes itself runs inside E2B. Its local terminal and file tools therefore operate on the E2B filesystem and processes, while E2B owns sandbox lifecycle, isolation, resource limits, and network policy. No separate Hermes terminal-backend integration is required for this setup.
# LangGraph
Source: https://docs.e2b.dev/agents/langgraph
Run code from your own LangGraph graph in an E2B sandbox, and keep that sandbox alive across graph steps, checkpoints and human-in-the-loop interrupts.
[LangGraph](https://docs.langchain.com/oss/python/langgraph/overview) is LangChain's runtime for building agents as explicit graphs — you write the nodes, the edges and the state. With E2B, code execution becomes a tool your graph calls: model-generated Python or shell commands run in an isolated sandbox, and the results flow back into graph state.
This page is for graphs you build yourself. If you want a ready-made harness where E2B is the agent's whole environment, use [Deep Agents](/agents/deep-agents); for a deployable coding agent, use [Open SWE](/agents/open-swe). The [comparison](#langgraph-vs-deep-agents-vs-open-swe) at the end spells out the difference.
## Install the dependencies
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
pip install langgraph langchain langchain-openai e2b-code-interpreter
```
Set API keys for E2B and your model provider:
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
export E2B_API_KEY="e2b_***" # get yours at https://console.e2b.dev
export OPENAI_API_KEY="sk-***"
```
## A code-execution tool
The smallest useful integration is a tool that runs Python in a sandbox, given to a tool-calling agent. The sandbox is not created inside the tool — it is passed in through LangGraph's **runtime context**, so every call in the run reuses the same kernel and the same filesystem:
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from dataclasses import dataclass
from e2b_code_interpreter import Sandbox
from langchain.agents import create_agent
from langchain.tools import ToolRuntime, tool
from langchain_openai import ChatOpenAI
@dataclass
class Context:
sandbox: Sandbox
@tool
def run_python(code: str, runtime: ToolRuntime[Context]) -> str:
"""Run Python in a Jupyter kernel. Returns stdout, the last result, or the error."""
execution = runtime.context.sandbox.run_code(code)
if execution.error:
return f"{execution.error.name}: {execution.error.value}"
return "\n".join(execution.logs.stdout) + (execution.text or "")
agent = create_agent(
ChatOpenAI(model="gpt-5.6-luna", use_responses_api=True, reasoning={"effort": "high"}),
tools=[run_python],
system_prompt="Answer by running Python. Check the tool result for an error before answering.",
context_schema=Context,
)
sandbox = Sandbox.create(timeout=600)
try:
result = agent.invoke(
{"messages": [("human", "How many r's are in 'strawberry'? Use code.")]},
context=Context(sandbox=sandbox),
)
print(result["messages"][-1].text)
finally:
sandbox.kill()
```
`runtime: ToolRuntime[Context]` is injected by LangGraph and never appears in the schema the model sees — the model only sees `code`. You pass `context=` on every `invoke`, which is what makes the lifecycle below possible. Any tool-calling chat model works here; for OpenAI reasoning models use the Responses API (`use_responses_api=True`) and set `reasoning={"effort": ...}` — OpenAI recommends Responses for function calling with reasoning models.
## Sandbox lifecycle across graph steps
A LangGraph run can outlive a single tool call by a lot: nodes run one after another, a checkpointer persists state between them, and `interrupt()` can hold the graph for as long as a human takes to answer. The sandbox has its own lifecycle. Three rules keep the two in step.
### Keep the `Sandbox` in context and the `sandbox_id` in state
Graph state is checkpointed, so everything in it must be serialisable. The `Sandbox` object is not — it holds a live connection. Put the object in `context_schema` and its id in state:
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from typing import TypedDict
from langgraph.checkpoint.memory import InMemorySaver
from langgraph.graph import END, START, StateGraph
from langgraph.runtime import Runtime
class State(TypedDict, total=False):
sandbox_id: str
output: str
def run_step(state: State, runtime: Runtime[Context]) -> State:
sandbox = runtime.context.sandbox
execution = sandbox.run_code("import platform; print(platform.python_version())")
return {"sandbox_id": sandbox.sandbox_id, "output": execution.logs.stdout[0]}
graph = (
StateGraph(State, context_schema=Context)
.add_node("run_step", run_step)
.add_edge(START, "run_step")
.add_edge("run_step", END)
.compile(checkpointer=InMemorySaver())
)
config = {"configurable": {"thread_id": "review-42"}}
graph.invoke({}, config=config, context=Context(sandbox=sandbox))
```
Every node in the run reads `runtime.context.sandbox` and gets the same sandbox. Files written in one node are there for the next; variables defined in one `run_code` call are defined in the next.
### Rebuild the context when you resume
Context is **not** checkpointed. After an `interrupt()`, or when a resume runs in a different process from the original invoke, nodes see `runtime.context` as `None`. The only thing the checkpoint kept is the `sandbox_id` in state — reconnect from it:
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from langgraph.types import Command
result = graph.invoke({}, config=config, context=Context(sandbox=sandbox))
if "__interrupt__" in result:
sandbox = Sandbox.connect(graph.get_state(config).values["sandbox_id"])
result = graph.invoke(
Command(resume=True),
config=config,
context=Context(sandbox=sandbox),
)
```
`Sandbox.connect()` returns a handle to the same sandbox, with its filesystem and running processes intact. If the sandbox was paused in the meantime, `connect()` resumes it.
### Let the sandbox outlive the interrupt
The default sandbox timeout is 300 seconds. A human reviewing a plan may take longer, and a sandbox that hit its timeout is gone. Tell E2B to pause instead of kill, and to wake up when the graph comes back:
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
sandbox = Sandbox.create(
timeout=600,
lifecycle={"on_timeout": "pause", "auto_resume": True},
)
```
On timeout the sandbox is paused with its memory and filesystem preserved, and it stays paused until you kill it. The next SDK call — `Sandbox.connect()`, `run_code()`, `files.read()` — resumes it automatically. See [Auto-resume](/sandbox/auto-resume) and [Persistence](/sandbox/persistence).
### Tear down explicitly
Kill the sandbox in a `finally` block so cleanup runs whether the graph finished, was rejected at the interrupt, or raised. If you would rather tie it to the agent's lifetime, an [`AgentMiddleware`](https://docs.langchain.com/oss/python/langchain/middleware) `after_agent` hook receives the same `runtime` and can call `runtime.context.sandbox.kill()`.
## Plan-and-execute coding agent
The [cookbook example](https://github.com/e2b-dev/e2b-cookbook/tree/main/examples/langgraph-plan-and-execute-python) puts all of this into one hand-wired graph. It repairs a small Python project whose tests fail:
```
START → bootstrap → plan → approve → execute ─┐
▲ │ (more steps)
│ ▼
└──(red, < 3 tries)── verify ──(green)── summarize → END
```
* `bootstrap` uploads the project, installs pytest and records the failing output — in a sandbox created with `on_timeout: pause`.
* `plan` asks the model for an ordered list of file changes as structured output.
* `approve` stops the graph with `interrupt()`; the resume reconnects from `sandbox_id`.
* `execute` runs one plan step per node visit with a small tool-calling agent (`read_file`, `write_file`, `run_command`), always in the same sandbox — pytest installed in `bootstrap` is still there.
* `verify` re-runs the suite; the graph re-plans on red, at most three times, and exits non-zero if the tests never pass.
The example ships with unit tests that run the graph against a fake sandbox, so the resume and re-plan paths are checked without a network.
## Results and errors
`run_code()` returns an `Execution`:
* `logs.stdout` / `logs.stderr` — lists of output lines;
* `results` — rich outputs such as text, charts (`png`), HTML or JSON; `execution.text` is the text of the last one;
* `error` — `None`, or an object with `name`, `value` and `traceback` when the cell raised.
Return the error to the model and tell it to check for one before answering; a wrong answer with an unread traceback is the most common failure mode. For shell commands, `sandbox.commands.run()` raises `CommandExitException` on a non-zero exit — it carries `exit_code`, `stdout` and `stderr`, so a tool can catch it and return all three.
## Security considerations
E2B isolates model-generated code from the machine running your graph. The sandbox still processes untrusted input and produces untrusted output:
* do not expose API keys or other host secrets to the sandbox unless the task needs them;
* keep sandbox and per-call timeouts as short as the workload allows, and use `on_timeout: pause` rather than a very long timeout when a human is in the loop;
* kill sandboxes you own, in `finally`;
* treat files the agent writes as a proposal to review, not a result to trust.
## LangGraph vs. Deep Agents vs. Open SWE
All three are LangChain projects and all three run on the LangGraph runtime. They differ in how much graph you write and in what role the sandbox plays:
| | **LangGraph** (this page) | **[Deep Agents](/agents/deep-agents)** | **[Open SWE](/agents/open-swe)** |
| - | - | - | - |
| What it is | The graph runtime — you write nodes, edges and state | A ready-made agent harness on LangGraph | A deployable coding agent on Deep Agents |
| Role of E2B | A **tool** your graph calls; results land in state | The agent's **environment** — files and shell live in the sandbox | Same as Deep Agents, managed per thread |
| How you use E2B | Pass a `Sandbox` in runtime context, `Sandbox.connect()` on resume | Pass `E2BSandbox(sandbox)` from `langchain-e2b` to `create_deep_agent` | Set `SANDBOX_TYPE="e2b"` |
| Use it when | You need custom control flow: plan-and-execute, approval gates, re-planning | You want a coding agent with filesystem and shell tools out of the box | You want a GitHub/Slack/Linear-driven coding agent for your org |
## Related guides
Pause and resume sandboxes with memory and filesystem intact
Pause on timeout and wake up on the next request
LangChain's agent harness with E2B as the sandbox backend
# Letta
Source: https://docs.e2b.dev/agents/letta
Run Letta custom server tools in isolated E2B sandboxes.
[Letta](https://www.letta.com/) (formerly MemGPT) is a platform for building stateful agents with persistent memory. Letta can execute custom server-side tools in an isolated E2B sandbox instead of running user-defined tool code in the Letta server environment.
This integration uses Letta's [legacy Docker server](https://docs.letta.com/v1-sdk/docker), which is no longer actively maintained. Letta is deprecating the V1 SDK in favor of the [Agent SDK](https://docs.letta.com/agent-sdk) and [App Server](https://docs.letta.com/platform/app-server), which are recommended for new projects. Use this guide to connect E2B to an existing V1 Docker deployment.
## Prerequisites
* Docker
* A [Letta model provider key](https://docs.letta.com/v1-sdk/docker/#enabling-additional-model-providers)
* An E2B API key
* Optional: an E2B [sandbox template](/template/quickstart) ID. Without one, Letta uses the default E2B sandbox.
## Enable E2B tool sandboxing
Create a `.env` file for the Letta container. Do not commit this file:
```dotenv theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
OPENAI_API_KEY=your_openai_api_key
E2B_API_KEY=e2b_your_api_key
# Optional. Omit to use the default E2B sandbox.
E2B_SANDBOX_TEMPLATE_ID=your_template_id
LETTA_SERVER_PASSWORD=choose_a_strong_password
```
Start the Letta Docker server with password protection enabled:
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
docker run \
-v ~/.letta/.persist/pgdata:/var/lib/postgresql/data \
-p 127.0.0.1:8283:8283 \
--env-file .env \
-e SECURE=true \
letta/letta:latest
```
When `E2B_API_KEY` is present, Letta runs custom tools created from source code in a sandboxed environment. Set `E2B_SANDBOX_TEMPLATE_ID` to run those tools in your own template instead of the default E2B sandbox. The `LETTA_SERVER_PASSWORD` value is used to authenticate requests to the local server.
Load the same values into the shell where you run the client and `curl` examples:
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
set -a
source .env
set +a
```
## Create and attach a custom tool
Install the Letta Python client:
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
pip install letta-client
```
Create a source-defined tool with a Google-style docstring so Letta can derive its tool schema:
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import os
from letta_client import Letta
client = Letta(
base_url="http://localhost:8283",
api_key=os.environ["LETTA_SERVER_PASSWORD"],
)
source_code = '''
def inspect_sandbox() -> str:
"""List the entries in the current E2B sandbox directory.
Returns:
A newline-separated list of directory entries.
"""
from pathlib import Path
return "\\n".join(sorted(entry.name for entry in Path(".").iterdir()))
'''
tool = client.tools.create(source_code=source_code)
print(f"Created tool: {tool.id}")
agent = client.agents.create(
model="openai/gpt-4o-mini",
embedding="openai/text-embedding-3-small",
)
print(f"Created agent: {agent.id}")
```
Set `TOOL_ID` and `AGENT_ID` to the IDs printed by the script, then attach the tool:
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
curl --request PATCH \
--url "http://localhost:8283/v1/agents/$AGENT_ID/tools/attach/$TOOL_ID" \
--header "Authorization: Bearer $LETTA_SERVER_PASSWORD"
```
Send a message that asks the agent to use the tool:
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
curl --request POST \
--url "http://localhost:8283/v1/agents/$AGENT_ID/messages" \
--header "Content-Type: application/json" \
--header "Authorization: Bearer $LETTA_SERVER_PASSWORD" \
--data '{
"messages": [
{
"role": "user",
"content": "Use inspect_sandbox and report the entries you find."
}
]
}'
```
Letta sends the source-defined tool to the configured E2B sandbox when the agent calls it, so the tool code does not execute in the Letta API server process.
## Tool lifecycle
The legacy Letta integration creates a fresh E2B sandbox for each tool call and terminates it after the call completes, including failed calls. Files, installed packages, and other runtime state do not persist between calls. Put reusable dependencies in a custom E2B template instead.
E2B tool sandboxing applies to custom server-side tools created from source code. MCP tools execute on their MCP server, and built-in tools such as `memory_insert` are not moved into E2B by these variables. Use client-side tools when code must run in the application that calls Letta.
## Security considerations
* Keep `E2B_API_KEY`, model provider keys, and `LETTA_SERVER_PASSWORD` in a secret store or an untracked `.env` file.
* Treat tool arguments and tool output as untrusted because they may be influenced by an agent or user.
* If you use a custom E2B template, give it only the packages and permissions the tool needs.
* Protect the Letta server with `SECURE=true` before exposing it beyond localhost.
## Learn more
* [Letta tool sandboxing](https://docs.letta.com/v1-sdk/docker/#tool-sandboxing)
* [Letta custom server tools](https://docs.letta.com/v1-sdk/tools/server-tools)
* [Letta Docker server](https://docs.letta.com/v1-sdk/docker)
* [E2B sandbox lifecycle](/sandbox)
* [E2B templates](/template/quickstart)
# Mastra
Source: https://docs.e2b.dev/agents/mastra
Build a Mastra coding agent with secure E2B sandboxes.
[Mastra](https://mastra.ai) is a TypeScript framework for building AI agents and workflows. The [`@mastra/e2b`](https://www.npmjs.com/package/@mastra/e2b) package connects a Mastra workspace to an E2B sandbox, so agent-generated commands run in an isolated cloud environment instead of on your machine.
In this guide, you will build a command-line coding agent that:
1. Starts an E2B sandbox through Mastra's workspace API.
2. Creates and tests a small project inside the sandbox.
3. Streams its progress to your terminal.
4. Destroys the sandbox when the task finishes.
## Prerequisites
* Node.js 22.13.0 or later
* An E2B API key
* An API key for a [model provider supported by Mastra](https://mastra.ai/models)
This guide uses OpenAI, but you can replace the model with any model supported by Mastra.
## Create the project
Create a new Node.js project and install Mastra, the E2B workspace provider, and their required dependencies:
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
mkdir mastra-e2b-agent
cd mastra-e2b-agent
npm init -y
npm pkg set type=module
npm install @mastra/core @mastra/e2b zod
npm install --save-dev tsx typescript @types/node
```
Set your E2B and model provider API keys:
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
export E2B_API_KEY="e2b_***"
export OPENAI_API_KEY="sk-***"
```
## Build the coding agent
Create an `index.ts` file:
```typescript title="index.ts" theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Agent } from '@mastra/core/agent'
import { Workspace } from '@mastra/core/workspace'
import { E2BSandbox } from '@mastra/e2b'
const task =
process.argv.slice(2).join(' ') ||
`Create /home/user/mastra-demo/slugify.js and a test file using
Node.js's built-in test runner. Run the tests and report the exact output.`
const workspace = new Workspace({
sandbox: new E2BSandbox({
id: `mastra-coding-agent-${Date.now()}`, // Mastra's logical sandbox ID
timeout: 600_000, // 10 minutes; Mastra expects milliseconds
}),
})
const agent = new Agent({
id: 'coding-agent',
name: 'Coding Agent',
model: 'openai/gpt-5.4-mini',
instructions: `
You are a software engineer working in an isolated E2B sandbox.
Use shell commands to create, inspect, and edit files inside the sandbox.
Run the relevant tests and verify that they pass before you respond.
Include the files you changed, the verification command, and its output
in your final response.
`,
workspace,
})
try {
await workspace.init()
const stream = await agent.stream(task, { maxSteps: 30 })
for await (const chunk of stream.textStream) {
process.stdout.write(chunk)
}
process.stdout.write('\n')
} finally {
await workspace.destroy()
}
```
The Mastra agent runs in your local Node.js process. Mastra automatically gives it an `execute_command` workspace tool, while `@mastra/e2b` routes those commands to the E2B sandbox through the JavaScript E2B SDK. Under the hood, workspace initialization creates or reconnects to a sandbox, command tools use E2B command execution, and workspace destruction kills the sandbox.
The `id` option is Mastra's logical identifier, stored in E2B metadata so the provider can find the same sandbox again. The provider manages the native E2B sandbox ID for you. This one-shot example includes a timestamp so concurrent runs receive separate sandboxes.
A workspace configured with only a sandbox provides command execution tools, not Mastra's dedicated `read_file` or `write_file` tools. This example uses shell commands to work with files. To add dedicated file tools backed by shared cloud storage, configure an S3, GCS, or Azure Blob mount in the workspace.
## Run the agent
Run the default task:
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
npx tsx index.ts
```
Or pass your own coding task:
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
npx tsx index.ts "Create a small Express API with a health endpoint and test it"
```
The first workspace initialization creates the E2B sandbox. The agent can then install packages, create files, run commands, and start background processes entirely inside the sandbox. The `finally` block calls `workspace.destroy()` so the sandbox is cleaned up even if the agent fails.
## Use a custom template
For repeatable environments and faster startup, pre-install your runtimes and dependencies in an [E2B template](/template/quickstart), then pass its ID or name to `E2BSandbox`:
```typescript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
const workspace = new Workspace({
sandbox: new E2BSandbox({
template: 'your-template-id-or-name',
timeout: 600_000, // 10 minutes; Mastra expects milliseconds
}),
})
```
Use templates when every agent run needs the same language toolchain, system packages, or repository bootstrap files.
## How the integration works
| Component | Responsibility |
| - | - |
| `Agent` | Chooses commands and iterates until the coding task is complete |
| `Workspace` | Adds command and process tools to the agent |
| `E2BSandbox` | Creates the E2B sandbox and executes workspace commands inside it |
| E2B template | Defines the operating system, runtimes, and pre-installed dependencies |
## Learn more
* [Mastra E2BSandbox reference](https://mastra.ai/reference/workspace/e2b-sandbox)
* [Mastra workspace documentation](https://mastra.ai/docs/workspace/overview)
* [E2B sandbox lifecycle](/sandbox)
* [E2B templates](/template/quickstart)
# Muse Code
Source: https://docs.e2b.dev/agents/muse
Run Muse Code in a secure E2B sandbox with full filesystem, terminal, and git access.
[Muse Code](https://dev.meta.ai/docs/muse-code) is Meta's coding agent for the terminal and CI, built on Muse Spark. E2B provides a pre-built `muse` template with Muse Code already installed.
## CLI
Create a sandbox with the [E2B CLI](/cli).
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
e2b sbx create muse
```
Once inside the sandbox, start Muse Code.
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
muse
```
## Run headless
Use `muse exec` for non-interactive mode and `--yolo` to skip approval prompts, disable Muse Code's own OS sandbox, and trust the workspace. The E2B sandbox is the isolation boundary here, so Muse Code's built-in sandbox is not needed. The sandbox isolates the agent from your host machine, but sandboxes can reach the open internet by default; restrict outbound traffic with [network rules](/network/internet-access). Muse Code authenticates with an API key from the [Meta Model API dashboard](https://dev.meta.ai) via the `META_API_KEY` environment variable.
Auto-approving tool calls is contained by the sandbox: the agent cannot touch your host machine, local files, or credentials. It can still make outbound network requests, since internet access is enabled by default. To limit where an auto-approved agent can connect, configure [outbound network rules](/network/internet-access) (`allowInternetAccess`, plus allow/deny lists by CIDR or hostname). Hostname rules apply to HTTP(S) traffic only; use CIDR rules for other protocols.
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const sandbox = await Sandbox.create('muse', {
envs: { META_API_KEY: process.env.META_API_KEY },
})
const result = await sandbox.commands.run(
`muse exec --yolo "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("muse", envs={
"META_API_KEY": os.environ["META_API_KEY"],
})
result = sandbox.commands.run(
'muse exec --yolo "Create a hello world HTTP server in Go"',
)
print(result.stdout)
sandbox.kill()
```
Add `--json` to emit machine-readable JSONL events instead of plain text, and `--max-model-steps` to cap a run that would otherwise loop.
### 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('muse', {
envs: { META_API_KEY: process.env.META_API_KEY },
timeoutMs: 600_000,
})
await sandbox.commands.run(
'git clone --depth 1 https://x-access-token:$GITHUB_TOKEN@github.com/your-org/your-repo.git /home/user/repo',
{ envs: { GITHUB_TOKEN: process.env.GITHUB_TOKEN } }
)
const result = await sandbox.commands.run(
`cd /home/user/repo && muse exec --yolo "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("muse", envs={
"META_API_KEY": os.environ["META_API_KEY"],
}, timeout=600)
sandbox.commands.run(
"git clone --depth 1 https://x-access-token:$GITHUB_TOKEN@github.com/your-org/your-repo.git /home/user/repo",
envs={"GITHUB_TOKEN": os.environ["GITHUB_TOKEN"]},
)
result = sandbox.commands.run(
'cd /home/user/repo && muse exec --yolo "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()
```
## 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 `muse` template.
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
// template.ts
import { Template } from 'e2b'
export const template = Template()
.fromTemplate('muse')
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
# template.py
from e2b import Template
template = (
Template()
.from_template("muse")
)
```
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
// build.ts
import { Template, defaultBuildLogger } from 'e2b'
import { template as museTemplate } from './template'
await Template.build(museTemplate, 'my-muse', {
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 muse_template
Template.build(muse_template, "my-muse",
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
Connect to the sandbox via SSH for interactive sessions
# NVIDIA NeMo Gym
Source: https://docs.e2b.dev/agents/nemo-gym
Run NVIDIA NeMo Gym agent evaluation and training environments in E2B sandboxes.
[NVIDIA NeMo Gym](https://github.com/NVIDIA-NeMo/Gym) is a library for evaluating and training models and agents against environments — a dataset of tasks, an agent harness, a verifier, and per-task execution state. NeMo Gym itself keeps running on your own machine or cluster; its `e2b` sandbox provider moves each task's shell commands and verification script into an isolated E2B sandbox instead of executing them on that host.
## Install
The E2B provider ships in NeMo Gym's `sandbox` extra. Install it from a checkout of the repository:
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
uv sync --extra sandbox
```
The `e2b` provider is not part of a published NeMo Gym release yet — the latest `nemo-gym` on PyPI (0.5.0) does not include it. Install from the repository until a release ships it, after which `pip install "nemo-gym[sandbox]"` works. The provider requires `e2b>=2.36.0,<3.0.0`.
Set your E2B API key:
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
export E2B_API_KEY="e2b_..."
```
## Configure the E2B provider
NeMo Gym ships a ready-to-use provider config at `nemo_gym/sandbox/providers/e2b/configs/e2b.yaml`. The fields that matter most for an E2B run:
```yaml theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
sandbox:
default_metadata:
sandbox-api: e2b
e2b:
connection:
api_key: ${oc.env:E2B_API_KEY,null}
api_url: ${oc.env:E2B_API_URL,null}
sandbox_url: ${oc.env:E2B_SANDBOX_URL,null}
request_timeout_s: 120.0
create:
template: null
template_map: {}
timeout_s: 3600.0
secure: true
allow_internet_access: true
strict_resources: false
exec:
default_timeout_s: 180.0
background: true
reconnect_attempts: 2
```
Leave `api_url` and `sandbox_url` unset for the hosted E2B service. For an E2B-compatible gateway, set both to the endpoints supplied by the gateway operator.
## Wire the provider into a run
Layer the provider config alongside an agent and model config. Every shipped provider config binds the same instance name, `sandbox`, so switching sandbox backends means swapping this one path — any harness that reads `sandbox_provider: sandbox` works the same way:
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
gym env start \
--config responses_api_agents/mini_swe_agent_2/configs/mini_swe_agent_2.yaml \
--config nemo_gym/sandbox/providers/e2b/configs/e2b.yaml \
--config responses_api_models/vllm_model/configs/vllm_model.yaml
```
For each task, NeMo Gym creates an E2B sandbox from the resolved template, runs the agent's commands and file transfers through it, and tears it down when the task finishes. Teardown is best-effort and runs off the critical path, so treat the sandbox lifetime — `create.timeout_s`, or a harness's own `sandbox_spec.ttl_s` where it overrides that — as the backstop that reclaims orphaned sandboxes.
Two provider rules apply before a run reaches E2B. The provider accepts only `template` under `provider_options` and rejects anything else before allocating a sandbox, and it will not start from an OCI image reference that has no `create.template_map` entry. A harness that sets other provider options, or that supplies image references, needs those adjusted in your own config layer.
## Build templates from OCI images
E2B starts a sandbox from a template name or ID, not an OCI image reference directly. NeMo Gym includes a provisioning CLI that builds an E2B template from an OCI image ahead of a run:
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
python -m nemo_gym.sandbox.providers.e2b.build \
--image ghcr.io/acme/task:1.0 \
--cpu-count 8 \
--memory-mb 16384 \
--output template_map.yaml
```
Paste the generated mapping under the provider's `create` block:
```yaml theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
sandbox:
e2b:
create:
template_map:
"ghcr.io/acme/task:1.0": task-1-0__f00ee9d593d9
```
Generated template names are deterministic for the image, CPU count, and memory size, so a matching template is reused rather than rebuilt. Template builds can create E2B resources and are never triggered implicitly by sandbox creation.
## Security considerations
* The shipped config keeps `secure: true`, so the sandbox `envd` service requires an access token.
* Outbound internet access is enabled by default. Set `create.allow_internet_access: false` for offline or untrusted workloads, or use [outbound network rules](/network/internet-access) to restrict traffic by CIDR or hostname.
* `mini_swe_agent_2` strips `e2b.connection.api_key`, `headers`, and `api_headers` from the per-instance worker config it writes to disk and passes them through the worker environment instead, so the API key never lands in a serialized config file.
* NeMo Gym identifies its traffic by appending `nemo-gym/` to the E2B SDK's `User-Agent` through E2B's `set_integration` hook. An explicit custom `User-Agent` in the connection headers takes precedence and hides that attribution.
For the full configuration reference, `SandboxSpec` field mapping, and lifecycle and retry behavior, see the [NeMo Gym E2B provider documentation](https://docs.nvidia.com/nemo/gym/latest/infrastructure/sandbox/e2b).
# LangChain Open SWE
Source: https://docs.e2b.dev/agents/open-swe
Use E2B sandboxes as the execution environment for Open SWE, LangChain's open-source coding agent framework.
[Open SWE](https://github.com/langchain-ai/open-swe) is LangChain's open-source framework for building your org's internal coding agent, built on [LangGraph](https://langchain-ai.github.io/langgraph/) and [Deep Agents](https://github.com/langchain-ai/deepagents). Every task runs in an isolated cloud sandbox where the agent gets full shell access — and E2B is a supported sandbox provider out of the box.
## Get started
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
git clone https://github.com/langchain-ai/open-swe.git
cd open-swe
uv venv
source .venv/bin/activate
uv sync --all-extras
```
Get your API key from the E2B console and create a `.env` file in the repo root — `langgraph dev` loads it automatically:
```bash .env theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
SANDBOX_TYPE="e2b"
E2B_API_KEY="e2b_***" # get yours at https://console.e2b.dev
E2B_TEMPLATE="my-template" # optional: custom sandbox template
# === LLM ===
OPENAI_API_KEY="" # default model is openai:gpt-5.6-sol
ANTHROPIC_API_KEY="" # when using anthropic: models
```
You need an API key for at least one LLM provider — the default model is `openai:gpt-5.6-sol`, and you can switch with `LLM_MODEL_ID` (e.g. `LLM_MODEL_ID="anthropic:claude-sonnet-5"`). Google and Fireworks are also supported — see the [full environment variable reference](https://github.com/langchain-ai/open-swe/blob/main/docs/INSTALLATION.md#6-environment-variables) for LangSmith tracing, GitHub App, and trigger configuration.
No other sandbox changes are needed — Open SWE creates and manages the sandboxes for you.
Open SWE's backend is a LangGraph app served together with its webhook API:
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
langgraph dev
```
The full deployment (GitHub App, webhooks, dashboard) is covered in the [installation guide](https://github.com/langchain-ai/open-swe/blob/main/docs/INSTALLATION.md).
As Open SWE picks up tasks, you can watch it create and reuse sandboxes with the [E2B CLI](/cli):
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
e2b sandbox list --state running
```
## Custom templates
By default, sandboxes start from the E2B base template. To pre-install languages, frameworks, or internal tools your repositories depend on — and cut setup time per agent run — build a [custom template](/template/quickstart) and point Open SWE at it:
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
E2B_TEMPLATE="your-template-id-or-name"
```
## Learn more
* [Open SWE installation guide](https://github.com/langchain-ai/open-swe/blob/main/docs/INSTALLATION.md)
* [Open SWE customization guide](https://github.com/langchain-ai/open-swe/blob/main/docs/CUSTOMIZATION.md#using-a-different-sandbox-provider) — switching sandbox providers
* [Announcement blog post](https://blog.langchain.com/open-swe-an-open-source-framework-for-internal-coding-agents/)
## Related guides
Build custom sandbox templates with pre-installed dependencies
Auto-pause, resume, and manage sandbox lifecycle
# OpenAI Agents API
Source: https://docs.e2b.dev/agents/openai-agents-api
Run OpenAI Agents API sessions in isolated E2B sandboxes.
The [OpenAI Agents API](https://platform.openai.com/docs) (preview) runs the
agent in OpenAI's cloud and keeps its session state. With a self-hosted
environment, the agent's shell commands and file edits run in an E2B sandbox.
OpenAI supports two ways to provision that sandbox:
* **Application-managed**: your application calls the E2B SDK to start,
connect, and stop the sandbox. The **Agents API Workbench** in the E2B
cookbook is built this way.
* **Webhook-managed**: a controller deployed in your E2B account reacts to
OpenAI webhooks and creates a dedicated worker sandbox for each session. Your
application only talks to the Agents API and never imports the E2B SDK. The
[E2B webhook-managed example](https://github.com/e2b-dev/e2b-cookbook/tree/main/examples/openai-agents-api-python-sdk-webhook-managed)
is the E2B variant of the handler OpenAI ships.
Try the application-managed Agents API Workbench first. Create the sandbox and
connect to its terminal with the E2B CLI:
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
e2b sandbox create openai-agents-api-python-sdk
```
When the workbench is ready, the terminal prints a single-use link to its UI.
Enter your OpenAI and E2B keys there and start a chat; each chat gets its own
Agents API session and its own worker sandbox. The rest of this page is the
quick start for the webhook-managed flow.
The Agents API is in preview. Your OpenAI project needs Agents API access, and
the `agent_api_sdk` Python package installs from the
[preview repository](https://github.com/OpenAI-Early-Access/agents-api-python-preview)
rather than PyPI.
Looking for the open-source agent framework instead? See
[OpenAI Agents SDK](/agents/openai-agents-sdk).
## Prerequisites
* an E2B API key
* an OpenAI project with Agents API access
* an OpenAI **application key** with **Responses → Write**, used to create sessions
* a restricted OpenAI **executor key** with only **List models → Read**, created in
the same organization, project, and user or service account as the application key
* [uv](https://docs.astral.sh/uv/); every script declares its dependencies inline
The executor key is the only credential that enters a worker sandbox, as
`CODEX_API_KEY`. Agent-generated code can read it, so keep the application key
out of workers entirely.
## Webhook-managed: set up the controller
Run everything from the webhook-managed example directory. `.env` is gitignored and
holds the five values below.
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
git clone https://github.com/e2b-dev/e2b-cookbook
cd e2b-cookbook/examples/openai-agents-api-python-sdk-webhook-managed
cp .env.example .env
```
| `.env` | Value |
| - | - |
| `OPENAI_API_KEY` | application key; creates sessions and reads their state |
| `OPENAI_EXECUTOR_API_KEY` | restricted executor key; the only key that enters a worker |
| `E2B_API_KEY` | creates the controller and worker sandboxes |
| `OPENAI_AGENT_ID` | printed by step 1 |
| `OPENAI_WEBHOOK_SECRET` | printed by OpenAI in step 4; stays valid across redeploys |
The controller only manages sessions of this one agent. Put the printed ID
in `.env` as `OPENAI_AGENT_ID`.
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
uv run --env-file .env client.py --create-agent openai-agents-api-python-sdk-webhook-managed
```
Workers boot from `openai-agents-api-python-sdk-webhook-managed` with
`codex exec-server` baked in, so a session's first turn does not pay for an
npm install. Rebuild only after bumping `CODEX_VERSION` in
`build_template.py`.
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
uv run --env-file .env build_template.py
```
Leave `OPENAI_WEBHOOK_SECRET` empty the first time.
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
uv run --env-file .env deploy.py
# Webhook: https://8000-.e2b.app/webhook
```
The controller ID is saved to the gitignored `.controller.json`. Rerunning
`deploy.py` reuses that sandbox, extends its one-hour timeout, and restarts
the handler with the current `.env`. If that sandbox is gone, it creates a
new one and prints the new webhook URL.
`uv run --env-file` does not override variables already exported in your
shell. If `E2B_API_KEY` is exported there, that key wins over `.env`.
In **Project settings → Webhooks** of the project that owns
`OPENAI_API_KEY`, add the URL and subscribe to
`agent.session.action_required` and `agent.session.failed`. Put the
generated signing secret in `.env` and redeploy to the same controller:
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
uv run --env-file .env deploy.py
```
A webhook in another project of the same organization is never delivered.
E2B's own [sandbox lifecycle webhooks](/sandbox/lifecycle-events-webhooks)
are unrelated to this step.
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
curl -sS https://8000-.e2b.app/health
# {"ok":true}
curl -sS -X POST https://8000-.e2b.app/webhook -d '{}'
# {"error":"Invalid signature"}
```
## Webhook-managed: run a session
The first line printed is `{"session_id": "sess_..."}`. Keep it.
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
set -a; . ./.env; set +a
uv run --env-file .env client.py --agent-id "$OPENAI_AGENT_ID" \
--input "Write hello to /workspace/hello.txt, then read it."
```
Behind the \~40 s: the client posts input, OpenAI sends
`agent.session.action_required` to the controller, the controller creates
the worker and launches `codex exec-server` inside it, the executor connects
out to OpenAI, and the turn runs in `/workspace`.
`SESSION_ID` is the first line printed by the previous step.
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
uv run --env-file .env client.py --session-id "$SESSION_ID" \
--input "Read /workspace/hello.txt again."
```
Deleting a session does not stop its sandbox.
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
uv run --env-file .env client.py --session-id "$SESSION_ID" --delete
e2b sandbox list # workers carry metadata agents-session-id
e2b sandbox kill # including paused workers
```
After the controller's one-hour timeout expires, rerun the deploy. It sees
the old sandbox is gone, starts a fresh one, and prints a new URL. Paste it
into the existing OpenAI webhook. The signing secret does not change.
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
uv run --env-file .env deploy.py
```
## Lifecycle
Workers stay running between turns and have a 30-minute timeout, refreshed on
every reconnect. Pause a worker with `e2b sandbox pause ` and the next input
resumes it with files intact. A killed worker is replaced by a fresh sandbox
without the previous files.
When a session fails, the controller pauses its worker instead of killing it,
so you can inspect the sandbox afterwards. Kill it when done.
OpenAI traffic cannot wake a paused or expired controller. Rerun `deploy.py`
before the hour is up to extend it, or start a new controller as above. Remove
the OpenAI webhook before killing the controller for good.
## Application-managed: Agents API Workbench
If you want the agent's events in your own process, for a live UI or for
function tools, your application creates the worker and starts the executor
itself. The [Agents API Workbench](https://github.com/e2b-dev/e2b-cookbook/tree/main/examples/openai-agents-api-python-sdk)
behind the `openai-agents-api-python-sdk` template does exactly that: a Flask
backend that runs one executor sandbox per chat, and a React frontend with a
streaming transcript and a live `/workspace` file viewer.
## Troubleshooting
The webhook points at a dead or wrong controller. Confirm the URL in OpenAI
matches the controller printed by the last `deploy.py`, that it is
registered in the project that owns the application key, and that
`OPENAI_AGENT_ID` matches the agent the session was created for.
`OPENAI_WEBHOOK_SECRET` is empty in the controller. Put the signing secret
in `.env` and run `deploy.py` again.
Input within about three minutes of killing or pausing a worker arrives
before OpenAI notices the executor is offline, so no reconnect webhook
fires and the turn runs without a shell. The session may keep declining to
use the shell afterwards. Wait, or start a new session.
The controller writes JSON lines for `enqueued`, `started`, and `paused` to
`/app/controller.log`. Each worker writes the executor output to
`/tmp/codex-executor.log`. Find them by metadata:
`agents-webhook-controller=e2b` for the controller and
`agents-session-id=` for workers.
# OpenAI Agents SDK
Source: https://docs.e2b.dev/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
# Use OpenAI computer-use helpers with E2B Desktop
Source: https://docs.e2b.dev/agents/openai-computer-use
Run OpenAI's computer-use helpers against a private E2B desktop sandbox you can watch live.
The OpenAI SDKs ship computer-use helpers: `ComputerToolset` runs the [computer tool](https://developers.openai.com/api/docs/guides/tools-computer-use) loop on the Responses API, and you supply a driver that executes each action the model asks for. E2B Desktop is that driver's target: a private Ubuntu desktop in the cloud, isolated from your machine, that you can watch live while the model clicks and types.
OpenAI documents two ways to connect a model to a screen, and both run on E2B Desktop:
* **Code execution**, which OpenAI recommends for `gpt-6-astra`: the model writes PyAutoGUI or Playwright code, and the desktop runs it. See [Use code execution](#use-code-execution).
* **The computer tool**: the model returns structured clicks and keystrokes, and your driver performs them. See [Run an agent on a desktop](#run-an-agent-on-a-desktop).
The E2B cookbook has runnable examples of both, including Excel to OrangeHRM, in [Python](https://github.com/e2b-dev/e2b-cookbook/tree/main/examples/openai-computer-use-python) and [TypeScript](https://github.com/e2b-dev/e2b-cookbook/tree/main/examples/openai-computer-use-js).
## How it works
1. **Create a desktop.** `Sandbox.create()` from E2B Desktop starts an XFCE desktop with LibreOffice, a file manager and other common apps.
2. **Write a driver.** One method per computer action (`click`, `type`, `scroll`, and so on) plus `screenshot`, each mapped to an E2B Desktop call.
3. **Run the toolset.** `ComputerToolset(driver).run(client, params)` calls the Responses API, dispatches every action to your driver, sends back a screenshot, and repeats until the model answers.
4. **Kill the desktop.** The helper never creates or closes the environment. You own it.
## Install
The helpers need `openai` 3.31.0 or newer in Python and 7.33.0 or newer in Node.
```bash JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
npm i openai @e2b/desktop
```
```bash Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
pip install openai e2b-desktop
```
Set `E2B_API_KEY` ([get one](https://e2b.dev/dashboard?tab=keys)) and `OPENAI_API_KEY` in your environment. Your OpenAI project needs access to a model that supports the `computer` tool.
## Run an agent on a desktop
This script seeds two order notes on a fresh desktop, opens their folder, and asks the model for the order number of the priority one. It prints a live view URL first, so you can watch the run.
```typescript JavaScript & TypeScript expandable theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from '@e2b/desktop'
import OpenAI, { ComputerToolset, type ComputerDriver } from 'openai'
const MODEL = 'gpt-6-luna'
// OpenAI key names that e2b-desktop does not map on its own
const KEYS: Record = {
ARROWUP: 'up', ARROWDOWN: 'down', ARROWLEFT: 'left', ARROWRIGHT: 'right',
PAGEUP: 'page_up', PAGEDOWN: 'page_down',
}
function noHeldKeys(keys?: string[] | null) {
if (keys?.length) throw new Error('Holding keys during mouse actions is not supported')
}
// Runs each action the model asks for on an E2B desktop.
function e2bDesktopDriver(desktop: Sandbox): ComputerDriver {
return {
async screenshot() {
const png = await desktop.screenshot()
return {
type: 'computer_screenshot',
image_url: `data:image/png;base64,${Buffer.from(png).toString('base64')}`,
}
},
async click({ button, x, y, keys }) {
noHeldKeys(keys)
if (button === 'left') await desktop.leftClick(x, y)
else if (button === 'right') await desktop.rightClick(x, y)
else if (button === 'wheel') await desktop.middleClick(x, y)
else throw new Error(`Unsupported button: ${button}`)
},
async double_click({ x, y, keys }) {
noHeldKeys(keys)
await desktop.doubleClick(x, y)
},
async drag({ path: [start, ...rest], keys }) {
noHeldKeys(keys)
await desktop.moveMouse(start.x, start.y)
await desktop.mousePress()
for (const point of rest) await desktop.moveMouse(point.x, point.y)
await desktop.mouseRelease()
},
async move({ x, y }) {
await desktop.moveMouse(x, y)
},
async scroll({ x, y, scroll_x, scroll_y, keys }) {
noHeldKeys(keys)
if (scroll_x) throw new Error('Horizontal scroll is not supported')
await desktop.moveMouse(x, y)
// The model sends pixels; one wheel step scrolls about 100
const steps = Math.max(1, Math.floor(Math.abs(scroll_y) / 100))
await desktop.scroll(scroll_y > 0 ? 'down' : 'up', steps)
},
async keypress({ keys }) {
await desktop.press(keys.map((key) => KEYS[key.toUpperCase()] ?? key))
},
async type({ text }) {
await desktop.write(text)
},
async wait() {
await new Promise((resolve) => setTimeout(resolve, 1000))
},
}
}
const desktop = await Sandbox.create({ resolution: [1280, 800], timeoutMs: 300_000 })
try {
await desktop.stream.start({ requireAuth: true })
const authKey = desktop.stream.getAuthKey()
console.log('Live view:', desktop.stream.getUrl({ authKey, viewOnly: true }))
await desktop.files.write('/home/user/orders/priority-order.txt', 'Order number: PO-7731\nPriority: high\n')
await desktop.files.write('/home/user/orders/standard-order.txt', 'Order number: PO-1001\nPriority: standard\n')
await desktop.open('/home/user/orders')
await desktop.wait(3000)
const response = await new ComputerToolset(e2bDesktopDriver(desktop)).run(
new OpenAI(),
{
model: MODEL,
store: false,
tools: [{ type: 'computer' }],
input: 'Open priority-order.txt and reply with only its order number.',
},
{
maxSteps: 15,
beforeAction: (action) => {
console.log('action:', action.type)
return true // approve every action; put your own policy here
},
},
)
console.log('Answer:', response.output_text)
} finally {
await desktop.kill()
}
```
```python Python expandable theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import base64
import time
from e2b_desktop import Sandbox
from openai import OpenAI
from openai.lib.computer import ComputerToolset, ComputerUseError
MODEL = "gpt-6-luna"
# OpenAI key names that e2b-desktop does not map on its own
KEYS = {"ARROWUP": "up", "ARROWDOWN": "down", "ARROWLEFT": "left", "ARROWRIGHT": "right",
"PAGEUP": "page_up", "PAGEDOWN": "page_down"}
class E2BDesktopDriver:
"""Runs each action the model asks for on an E2B desktop."""
def __init__(self, desktop: Sandbox):
self.desktop = desktop
def screenshot(self):
png = self.desktop.screenshot()
return {"type": "computer_screenshot",
"image_url": "data:image/png;base64," + base64.b64encode(png).decode()}
def click(self, action):
no_held_keys(action)
if action.button == "left":
self.desktop.left_click(action.x, action.y)
elif action.button == "right":
self.desktop.right_click(action.x, action.y)
elif action.button == "wheel":
self.desktop.middle_click(action.x, action.y)
else:
raise ComputerUseError(f"Unsupported button: {action.button}")
def double_click(self, action):
no_held_keys(action)
self.desktop.double_click(action.x, action.y)
def drag(self, action):
no_held_keys(action)
start, *rest = action.path
self.desktop.move_mouse(start.x, start.y)
self.desktop.mouse_press()
for point in rest:
self.desktop.move_mouse(point.x, point.y)
self.desktop.mouse_release()
def move(self, action):
self.desktop.move_mouse(action.x, action.y)
def scroll(self, action):
no_held_keys(action)
if action.scroll_x:
raise ComputerUseError("Horizontal scroll is not supported")
self.desktop.move_mouse(action.x, action.y)
# The model sends pixels; one wheel step scrolls about 100
steps = max(1, abs(action.scroll_y) // 100)
self.desktop.scroll("down" if action.scroll_y > 0 else "up", steps)
def keypress(self, action):
self.desktop.press([KEYS.get(key.upper(), key) for key in action.keys])
def type(self, action):
self.desktop.write(action.text)
def wait(self, action):
time.sleep(1)
def no_held_keys(action):
if getattr(action, "keys", None):
raise ComputerUseError("Holding keys during mouse actions is not supported")
def approve(action):
print("action:", action.type)
return True # approve every action; put your own policy here
desktop = Sandbox.create(resolution=(1280, 800), timeout=300)
try:
desktop.stream.start(require_auth=True)
auth_key = desktop.stream.get_auth_key()
print("Live view:", desktop.stream.get_url(auth_key=auth_key, view_only=True))
desktop.files.write("/home/user/orders/priority-order.txt", "Order number: PO-7731\nPriority: high\n")
desktop.files.write("/home/user/orders/standard-order.txt", "Order number: PO-1001\nPriority: standard\n")
desktop.open("/home/user/orders")
desktop.wait(3000)
with OpenAI() as client:
response = ComputerToolset(E2BDesktopDriver(desktop)).run(
client,
{
"model": MODEL,
"store": False,
"tools": [{"type": "computer"}],
"input": "Open priority-order.txt and reply with only its order number.",
},
max_steps=15,
before_action=approve,
)
print("Answer:", response.output_text)
finally:
desktop.kill()
```
Run it with `python agent.py`, or `npx tsx agent.ts` in an ES module project. A run takes a few model turns and ends with `Answer: PO-7731`.
For a full run, see the Excel to OrangeHRM example in the E2B cookbook, in [Python](https://github.com/e2b-dev/e2b-cookbook/tree/main/examples/openai-computer-use-python) and [TypeScript](https://github.com/e2b-dev/e2b-cookbook/tree/main/examples/openai-computer-use-js): `gpt-6-luna` reads new hires from a spreadsheet in LibreOffice Calc, enters each one into the OrangeHRM web app in Chrome, and writes the assigned Employee Ids back into the spreadsheet. It is the same task as the [Claude toolsets](/agents/claude-toolsets) example, driven by the computer tool alone.
The driver covers every action the computer tool can send. It refuses what E2B Desktop has no direct call for, such as clicks with held modifier keys and horizontal scrolling: a driver must raise instead of silently skipping an action, and the helper stops the run.
The examples use `gpt-6-luna`. In our runs of multi-step LibreOffice Calc and web app tasks on E2B Desktop, it finished about twice as fast as Claude Haiku 5.5 at about a tenth of the cost, mostly because OpenAI caches the repeated screenshot history automatically.
## Use code execution
With code execution, the model gets one function tool instead of the computer tool: `exec_py` for Python with [PyAutoGUI](https://pyautogui.readthedocs.io), or `exec_js` for JavaScript with [Playwright](https://playwright.dev). One call can batch several actions, read text from the screen or the page, and check its own result before the model looks again. Your application runs the code and returns its text and screenshots.
E2B Desktop is where that code runs. It executes inside the sandbox, never on your machine, and the desktop stays up between calls, so windows, variables and logins carry over. The E2B side has three parts:
1. **A template with the library.** Build it once per E2B team on top of the stock `desktop` template:
```typescript JavaScript & TypeScript (exec_js) theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Template, defaultBuildLogger } from 'e2b'
const template = Template()
.fromTemplate('desktop')
.setUser('root')
.runCmd(['curl -fsSL https://deb.nodesource.com/setup_22.x | bash -', 'apt-get install -y nodejs'])
.setUser('user')
.setWorkdir('/home/user')
// The runtime launches Playwright's own Chromium, as OpenAI's sample app does
.runCmd('mkdir -p /home/user/.exec_js && cd /home/user/.exec_js && npm install playwright@1.64.0 && npx playwright install chromium')
.setUser('root')
.runCmd('cd /home/user/.exec_js && npx playwright install-deps chromium')
.setUser('user')
await Template.build(template, 'openai-computer-use-exec-js', {
cpuCount: 8,
memoryMB: 8192,
onBuildLogs: defaultBuildLogger(),
})
```
```python Python (exec_py) theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Template, default_build_logger
# python-xlib replaces the python3-Xlib fork PyAutoGUI asks for: the desktop's
# X server listens only on an abstract socket, which python3-Xlib cannot reach.
PACKAGES = [
"pyautogui==0.9.54", "mouseinfo==0.1.3", "pygetwindow==0.0.9", "pymsgbox==2.0.1",
"pyperclip==1.11.0", "pyrect==0.2.0", "pyscreeze==1.0.1", "pytweening==1.2.0",
"python-xlib==0.33", "six==1.17.0", "pillow==11.3.0",
]
template = (
Template()
.from_template("desktop")
.set_user("root")
.apt_install(["python3-tk"]) # importing PyAutoGUI exits without tkinter
.run_cmd("pip install --no-deps " + " ".join(PACKAGES))
.set_user("user")
.set_workdir("/home/user")
)
Template.build(template, "openai-computer-use-exec-py", cpu_count=8, memory_mb=8192,
on_build_logs=default_build_logger())
```
2. **A persistent runtime inside the desktop.** A small process you start in the background keeps one Python namespace (or one Chromium that Playwright launches, visible on the desktop) for the whole run. It gives the code `log` or `console.log` for text and `display` for screenshots, and answers each call with the `input_text` and `input_image` items the Responses API takes as a `function_call_output`. Write each call to a file in the sandbox, and have the runtime run that file when `curl` posts to its Unix socket through `commands.run`, so nothing listens outside the sandbox.
3. **OpenAI's loop.** The `exec_py` or `exec_js` tool definition and the Responses loop from [OpenAI's guide](https://developers.openai.com/api/docs/guides/tools-computer-use#use-code-execution), with each `function_call` sent to the runtime and its output returned unchanged.
The E2B cookbook has both as a hello world, next to the computer-tool examples and with the same order-number task as above. In [Python](https://github.com/e2b-dev/e2b-cookbook/tree/main/examples/openai-computer-use-python), `gpt-6-astra` opens the file in the file manager with PyAutoGUI. In [TypeScript](https://github.com/e2b-dev/e2b-cookbook/tree/main/examples/openai-computer-use-js), it opens the same folder in Chromium with Playwright. The runtimes are adapted from [OpenAI's CUA sample app](https://github.com/openai/openai-cua-sample-app), which runs the generated code on your own computer; on E2B it runs in a throwaway desktop whose internet access you can limit to the hosts you allow.
PyAutoGUI drives the whole desktop by screen coordinates, so it works with any app. Playwright drives only the browser, but it can read page text and click elements by role, so it needs fewer screenshots. The TypeScript runtime also gives the code a `desktop` object (screenshot, click, type, key and more, through xdotool) for apps outside the browser, which the TypeScript Excel to OrangeHRM example uses for LibreOffice Calc.
## Watch live
`stream.start()` serves the desktop over noVNC. With `require_auth=True` (`requireAuth: true`) the URL carries a generated password, and `view_only` keeps viewers from moving the mouse under the model. Anyone with the URL can watch until the desktop is killed, so do not share it.
## Approve actions
`before_action` (`beforeAction`) runs before every action, including the final screenshot, and must return exactly `True` to let it through. The example approves everything and logs the action type; this is where an application checks or asks a person before consequential clicks and keystrokes.
Calls that carry pending safety checks stop the run unless you also pass `approve_safety_checks` (`approveSafetyChecks`) and return `True`.
## Sandbox ownership
* **Cleanup:** the helper never kills the desktop. Call `kill()` in `finally` so a failed or cancelled run does not leave it running until its timeout.
* **Timeout:** `timeout=300` (`timeoutMs: 300_000`) is a backstop. Keep it longer than a run of `max_steps` model turns can take.
* **Cancellation:** pass `cancelled` (Python) or `signal` (Node) to stop between model calls and actions.
* **Egress:** the desktop has internet access by default. Restrict it with [`allow_internet_access` or `network.allow_out`](/network/internet-access) when the task does not need the open web.
## Related guides
`gpt-6-luna` copies new hires from a spreadsheet into an HR system on one desktop
How computer use agents drive E2B Desktop sandboxes
What the desktop sandbox ships with and how to customize it
OpenAI's references: the [computer use guide](https://developers.openai.com/api/docs/guides/tools-computer-use), the [Node helper guide](https://github.com/openai/openai-node/blob/main/docs/computer.md) and the [Python helper source](https://github.com/openai/openai-python/blob/main/src/openai/lib/computer.py).
# Deploy OpenClaw
Source: https://docs.e2b.dev/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://docs.e2b.dev/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://docs.e2b.dev/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.
This page runs the whole agent inside a sandbox. To keep OpenCode on your machine and use sandboxes as remote workspaces instead, see [OpenCode workspaces](/agents/opencode/workspace-plugin).
## CLI
Create a sandbox with the [E2B CLI](/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.commands.run(
'git clone --depth 1 https://x-access-token:$GITHUB_TOKEN@github.com/your-org/your-repo.git /home/user/repo',
{ envs: { GITHUB_TOKEN: process.env.GITHUB_TOKEN } }
)
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.commands.run(
"git clone --depth 1 https://x-access-token:$GITHUB_TOKEN@github.com/your-org/your-repo.git /home/user/repo",
envs={"GITHUB_TOKEN": os.environ["GITHUB_TOKEN"]},
)
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
Keep OpenCode local and warp sessions into E2B sandboxes
Auto-pause, resume, and manage sandbox lifecycle
Connect to the sandbox via SSH for interactive sessions
# OpenCode workspaces
Source: https://docs.e2b.dev/agents/opencode/workspace-plugin
Use E2B sandboxes as remote workspaces for a local OpenCode session, and move sessions into them with /warp.
The [OpenCode E2B workspace plugin](https://github.com/e2b-dev/opencode-e2b) registers an `e2b` workspace type in OpenCode. OpenCode keeps running on your machine. Each workspace is an E2B sandbox that runs a remote OpenCode server on a copy of your project. You can start a session inside the sandbox, or move an existing local session into it with `/warp`.
The plugin builds on OpenCode's experimental workspace API. See [Known limitations](#known-limitations) before relying on it.
## Prerequisites
* An [E2B API key](/api-key).
* OpenCode `1.16.0` or newer.
* A macOS or Linux host. See [Known limitations](#known-limitations) for Windows.
## Install
Add the plugin to your OpenCode config. The second element of the tuple is optional and holds the plugin [options](#options).
```json opencode.json theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
{
"$schema": "https://opencode.ai/config.json",
"plugin": [
[
"opencode-e2b-workspace-plugin",
{
"nodeVariant": "22-slim",
"sandboxTimeoutMs": 3600000
}
]
]
}
```
Start OpenCode with your E2B API key and the workspace flag.
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
E2B_API_KEY=e2b_*** OPENCODE_EXPERIMENTAL_WORKSPACES=true opencode
```
## Use
In OpenCode, run `/warp` and choose **E2B Sandbox**. The plugin builds a sandbox template for your project, uploads a snapshot of the project, and starts a remote OpenCode server. The first build for a project takes one to two minutes. Later workspaces reuse the template.
Send a prompt. The agent runs inside the sandbox, on the sandbox copy of your project. The workspace is named after your branch, for example `e2b-main`.
From an existing local session, run `/warp` and pick the workspace. OpenCode moves the session history into the sandbox. To move a session back to your machine, run `/warp` and choose **None**. OpenCode then offers to move the sandbox's file changes with it: the diff of what the agent changed is applied to your project as a patch. Your local history is untouched, and commits made inside the sandbox do not travel back. Your project must be a git repository root for the patch to apply.
`/new` starts a new local session. Run `/warp` again to put it in the sandbox.
## How it works
The plugin layers a small workspace template on top of the published E2B template `e2b/opencode:v`, where `` matches your local OpenCode. If that template is not available, the plugin builds its own base from the configured Node image and installs the same OpenCode version. Managed templates use the `opencode-e2b-` alias prefix, and a best-effort cleanup removes old ones.
Inside the sandbox, the project lives at the **same path as on your host**, and the remote OpenCode server runs as the sandbox user. OpenCode resolves a session directory on both the host and the sandbox, so a shared path is what lets a warped session work on both sides. The plugin returns the workspace only after the remote server answers on its health and sync endpoints.
## Options
| Option | Description | Default |
| - | - | - |
| `nodeVariant` | Node image variant for managed templates. | `22-slim` |
| `sandboxTimeoutMs` | Sandbox lifetime. Every request OpenCode sends to the workspace extends the lifetime by this amount, so it acts as an inactivity limit. When it runs out, E2B destroys the sandbox. | `3600000` (1 hour) |
| `projectPath` | Project directory inside the sandbox. Any absolute path. | Your project's path on the host. Falls back to `/home/user/project` when the host path cannot be mirrored. |
| `serverPort` | Port of the remote OpenCode server. | `4096` |
| `secure` | Use E2B secure sandbox traffic. | `true` |
| `metadata` | Extra key-value metadata attached to each sandbox. | `{}` |
| `network.allowPublicTraffic` | Allow unauthenticated access to sandbox URLs. When `false`, requests carry E2B's traffic access token. | `false` |
| `network.allowOut` / `network.denyOut` | Outbound network allow and deny lists, passed to E2B as-is. | `[]` |
| `network.maskRequestHost` | Host to present on outbound requests, passed to E2B as-is. | unset |
| `upload.ignore` | Paths or path segments to skip when the project snapshot is uploaded. Your list replaces the default list. | `.git`, `node_modules`, `dist`, `build`, `.next`, `.turbo`, `.cache`, `.DS_Store` |
| `upload.includeDotGit` | Upload the local `.git` directory. | `false` |
| `upload.followSymlinks` | Follow symlinks during upload. Symlinks that leave the project are rejected. | `false` |
| `upload.batchSize` | Files per upload request. | `100` |
| `bootstrap.prepareCommands` | Shell commands to run in the sandbox after upload and before the remote server starts. | `[]` |
| `bootstrap.waitForPath` | Path polled on the remote server until it answers. | `/global/health` |
| `bootstrap.waitForTimeoutMs` | How long to wait for the remote server and its sync endpoints. | `30000` |
| `templateGc.enabled` | Remove old managed templates at plugin load and after a workspace is removed. | `true` |
| `templateGc.ttlMs` | Age after which an unused managed template is removed. Values below 1 hour are raised to 1 hour. | `259200000` (3 days) |
| `templateGc.cooldownMs` | Minimum gap between two cleanup passes. | `600000` (10 minutes) |
`bootstrap.installCommand` and `bootstrap.startCommand` from older configs are accepted but ignored: the template installs OpenCode and starts the server.
## Known limitations
### Experimental foundation
The plugin uses the `experimental_workspace` API of OpenCode. You must set `OPENCODE_EXPERIMENTAL_WORKSPACES=true`. OpenCode can change this API without notice.
### macOS and Linux hosts only
The sandbox stores the project at the same path as the host, which is what makes a session directory valid on both machines. A Windows path cannot exist in the sandbox, so on Windows the project lives at `/home/user/project` instead and the host cannot open that path. Workspaces created on Windows are not supported.
### A sandbox does not survive inactivity
E2B destroys a sandbox once `sandboxTimeoutMs` passes without any request from OpenCode. The default is 1 hour. The workspace then shows as unavailable and you must create it again. For long breaks, set a higher `sandboxTimeoutMs`.
### Warp of a session with history can fail
A warp of a new session is reliable. A warp of a session with existing messages can fail with a sync error ("sequence mismatch"). If this happens, try the warp again, or start the session inside the workspace.
## Related guides
The other direction: run the OpenCode agent inside an E2B sandbox
Pause and resume sandboxes to keep state beyond the timeout
How E2B templates are built and cached
# OpenEnv multi-turn RL
Source: https://docs.e2b.dev/agents/openenv
Inspect, run and repair code across native OpenEnv Terminus steps in E2B, with a first reward without a GPU and optional TRL training wiring.
[OpenEnv](https://github.com/huggingface/OpenEnv) gives a model tools, observations and rewards for reinforcement learning and agent evaluation. Its [Terminus environment](https://github.com/huggingface/OpenEnv/tree/v0.8.0/envs/terminus_env) runs terminal commands in E2B, so a policy can inspect files, execute a program, edit it and check its work over several turns.
## Where OpenEnv and E2B fit
E2B keeps executable episode code isolated from your trainer host and preserves files between steps. Reset starts a fresh sandbox for the next completion. The OpenEnv server, model inference and training run on your own compute; this Terminus path works with a local server and requires no Docker.
```mermaid theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
flowchart TB
P["Policy / TRL on your host"] -->|tools| B["Cookbook task environment"]
B -->|reset / step| O["Local Terminus server"]
O -->|execute| S["E2B episode sandbox"]
S -->|export only report.py, then delete| B
B -->|upload source and held-out CSV| V["Fresh offline verifier sandbox"]
V -->|exit code and bounded JSON output| B
B -->|grade| R["Host-held expectations"]
B -->|confirm both deletions| C["E2B control API"]
```
Terminus supplies the native reset/step protocol and policy workspace. The cookbook's `ReportEpisode` manages task data, deadlines, submission and cleanup. It creates the verifier through the E2B SDK and compares the result with expectations on the host. TRL supplies the model's tool loop and optimizer when you enable training.
## Run your first repair episode
The example starts from a Python CSV report that overwrites repeated regions instead of adding their sales. One episode submits that bug and scores zero. A fresh episode inspects the files, repairs the program, reruns it and scores one.
Requires Python 3.12 or 3.13, [uv](https://docs.astral.sh/uv/getting-started/installation/), Git and an [E2B API key](https://console.e2b.dev). The demo creates four short-lived sandboxes sequentially: a policy workspace and a verifier for each episode. It consumes usage credits; no model provider key or GPU is needed.
### Start the local server
Get the [OpenEnv Terminus example](https://github.com/e2b-dev/e2b-cookbook/tree/main/examples/openenv-terminus-python) and install its pinned dependencies:
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
git clone https://github.com/e2b-dev/e2b-cookbook.git
cd e2b-cookbook/examples/openenv-terminus-python
uv sync --frozen
cp .env.example .env
```
Set `E2B_API_KEY` in the ignored host `.env`. Start the server from that directory:
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
uv run --frozen uvicorn server:app --host 127.0.0.1 --port 8010
```
### Run the demo and inspect the result
In a second terminal, from the same directory:
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
uv run --frozen main.py
```
Expected repaired output on the visible CSV:
```json theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
{"east": 15, "west": 7}
```
The buggy output is `{"east": 5, "west": 7}`. The receipt contains rewards `[0.0, 1.0]`, two policy IDs, two verifier IDs, operation timings and confirmed cleanup. This scripted result checks execution and grading. Stop the local server with Ctrl-C after the clients finish.
## Follow an episode from reset to reward
### Inspect and edit persistent files
`reset()` installs the buggy `report.py` and visible `sales.csv` in a new policy sandbox. `terminal(command)` sends each action through native `step(CallToolAction(...))` and returns output to the policy. The same files are available for inspection, execution and repair across those calls.
### Submit the source to a fresh verifier
`submit()` exports only `report.py`, confirms deletion of the policy sandbox, then creates a fresh sandbox from the same template with outbound access disabled. It uploads the source and a separate CSV. Background processes, companion files and modified binaries from the policy workspace are excluded from that transfer.
### Grade on the host and finish cleanup
The verifier runs the fixed Python command under a timeout. The host requires exit code zero and JSON containing exactly the expected integer totals. Submission confirms verifier deletion before committing the score; `get_reward()` returns that committed result. Without submission, reward collection checks the policy sandbox before accepting zero and deletes it.
| Outcome | Task score |
| - | - |
| Buggy report or visible-answer hardcode fails the separate CSV | `0.0` |
| Correct aggregation produces the separate input's totals | `1.0` |
| Healthy episode ends without submission | `0.0` |
| Submitted code exits non-zero, times out or produces invalid output | `0.0` |
| Policy window exhausted in a healthy sandbox | Zero after confirmed cleanup; continue training |
| Resource loss, transport failure, global deadline or unresolved cleanup | Abort without scoring |
OpenEnv 0.8.0 accepts a policy-writable `reward.txt` value. This cookbook ignores it and grades on the host. The submitted candidate remains untrusted code in the fresh verifier. Both fixed fixtures are public, so this task check does not establish generalization or a complete adversarial grading boundary.
## Use the Python bridge
With the server running, execute this from the example directory:
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import shlex
from dotenv import load_dotenv
from episode import BUGGY, PROGRAM, ReportEpisode
load_dotenv()
episode = ReportEpisode()
try:
print(episode.reset())
print(episode.terminal(f"cat {PROGRAM}; python3 -B {PROGRAM}"))
fixed = BUGGY.replace(
'= int(row["amount"])',
'= totals.get(row["region"], 0) + int(row["amount"])',
)
episode.terminal(f"printf %s {shlex.quote(fixed)} > {PROGRAM}")
print(episode.submit()) # Verify the source on a separate CSV, then delete both sandboxes.
print(episode.get_reward()) # 1.0; both sandboxes are already confirmed absent.
finally:
episode._close()
```
`terminal` returns observations, `submit` verifies and cleans up, and `get_reward` returns the score. The `finally` also protects operations that fail before reward collection. After submission, further tool calls cannot mutate the episode.
## Adapt the task
The cookbook's [task.json](https://github.com/e2b-dev/e2b-cookbook/blob/main/examples/openenv-terminus-python/task.json) contains `id`, visible CSV `input`, integer `expected` totals, a `prompt`, and a `verification` pair with a separate input and expected totals. Setup uploads the visible input and buggy program; verification data stays on the host until submission.
Change both input/expectation pairs and the prompt together. For a different defect, update the starter in `episode.py`, the scripted repair in `main.py` and the focused tests. Keep grading expectations in trusted orchestration.
For TRL, the dataset columns are `prompt` (chat messages) and `task` (trusted reset data serialized with `json.dumps`). Reset decodes the JSON; this preserves each task's expectation keys when Arrow stores rows with different regions. Direct Python callers can still pass a task dictionary. Only `terminal(command)` and `submit()` are model tools; reset, expected answers, sandbox identity and cleanup remain orchestration controls.
## Connect the environment to TRL
### Choose a tool-capable model
Keep the local server running. Set `MODEL_PATH` to a Transformers checkpoint or model ID whose chat template and response parser support tool calls such as `terminal(command="...")`. Install the training extra and run:
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
uv sync --frozen --extra training
uv run --frozen --extra training train.py --model "$MODEL_PATH"
```
The example leaves model choice to you. `--cpu` supports local wiring checks; practical training requirements depend on your checkpoint and compute. Pretrained task success, GPU requirements and learning improvement have not been measured by this example.
### Let the native tool loop collect the rollout
`train.py` passes a factory to TRL 1.14.2's experimental `GRPOTrainer(environment_factory=...)`. TRL discovers the typed `terminal` and `submit` methods, calls `reset(task=...)` for each completion, appends tool observations and calls `get_reward()` when collecting scores. It owns generation, token masks and the GRPO loss; Transformers >=5.2 supplies tool-call parsing.
TRL pools Python environment objects. Constructing `ReportEpisode` allocates nothing, and each reset replaces its sandbox. Batch startup and tool calls can run serially. Submission blocks further mutations, while TRL's completion/token limits control when generation ends. The training run's `finally` closes every environment again.
### Understand the configuration and artifacts
The defaults request two completions per prompt, a two-completion batch, up to 512 completion tokens, a learning rate of `1e-6` and one optimizer step. `--max-steps` changes the step count; `--output-dir` defaults to `training-output`. CPU mode disables BF16; the normal path requests BF16.
On successful training, the script saves the model under `training-output/checkpoint` and prints JSON with operation traces and cleanup entries. This is a model save; the script does not save optimizer state for resuming training. External experiment reporting is disabled. Full generated-conversation logging is not enabled by default, and a one-step run may finish before TRL's usual metric logging interval.
### Check tool calls and reward signal
Use the operation trace to confirm the policy executed tools and submitted. When reading TRL metrics, distinguish reward quality from tool parsing and optimizer activity:
| Signal | What it tells you |
| - | - |
| `reward` | Mean task score for the scored completions |
| `tools/call_frequency`, `tools/failure_frequency` | Calls per completion and fraction of calls that raised; a completed shell error can still be a normal observation |
| `frac_reward_zero_std` | Fraction of groups with identical rewards; those groups supply no reward difference for GRPO |
| `train_loss` | Optimization summary; zero can occur when every completion receives the same reward |
An initial run can finish with all rewards zero and `train_loss=0`. Check tool parsing and submission first, then inspect candidate failures and adjust tasks or prompting until some completions succeed and others fail before measuring learning. TRL can catch a tool exception and show it to the model; the bridge remembers infrastructure failures and raises again at reward collection to keep them out of training data.
For a model comparison, evaluate the starting and saved models with the same tasks, verification inputs, prompts, sampling settings, token/tool budgets and sandbox resources. Report task scores, infrastructure failures, latency and cost separately. The scripted demo and one optimizer step establish wiring; a learning claim needs a separate evaluation on varied tasks and held-out data.
## Lifecycle and failures
### State and reset
Files persist within an episode. Each terminal command starts a fresh shell, so use absolute paths rather than relying on a previous `cd` or export. Reset replaces the whole sandbox, including background processes. The sandbox connection is attached before the policy runs, and submission never reconnects to or resumes that sandbox.
### Timeout budgets and cancellation
The policy window starts after reset setup finishes. When it expires, the next tool or reward boundary checks sandbox health, confirms deletion and commits zero; subsequent commands are blocked. A timely submission stops policy work, and export/verification use the global and operation budgets. Infrastructure or cleanup failures remain fatal even when the policy window has expired. These checks do not interrupt model generation on the host; the trainer must enforce its own generation timeout.
| Budget | Value | Purpose |
| - | - | - |
| Native policy lifetime, then host extension | 300 s, then 600 s | Remote backstop, extended after the policy ID arrives |
| Policy window after ready reset | 120 s | Batch waiting, generation and terminal work until submission |
| Global episode deadline | 600 s | Reset through verification and cleanup; exhaustion remains fatal |
| Sandbox-side command limit | 10 s | Bound an ordinary shell or report execution |
| Native client message limit | 25 s | Wait for an OpenEnv response |
| Verifier lifetime / SDK command response | 120 s / 20 s | Bound the extra resource and command response |
| Artifact/output transfer deadline | 10 s | Bound each file transfer, capped at 64 KiB |
A returned shell error or timeout lets the policy inspect the result and correct its program. A lost response or local cancellation leaves remote execution uncertain: the recipe aborts scoring, removes its owned resources and avoids replaying the step. Detached processes can outlive a shell command, so cleanup removes the whole sandbox. If reset loses an allocation response before an ID arrives, the native lifetime bounds that unknown resource.
### Cleanup and resource reconciliation
The OpenEnv 0.8.0 server can suppress teardown failures. The recipe therefore deletes and checks every known owned ID, retries deletion for those IDs and reports unresolved cleanup. A committed score is readable after deletion. For an unresolved ID, check that owned resource in the [E2B console](https://console.e2b.dev) before rerunning. An abrupt host kill can require manual reconciliation.
Keep the key on the host. The server binds to loopback; the policy sandbox retains native template networking, and the verifier has outbound access disabled. The cookbook includes failure diagnostics and a troubleshooting table.
## Scale and cost
With serial startup, the first sandbox waits roughly `(batch_size - 1) * reset_time` before generation. Budget its lifetime and both local windows for that wait, generation, tool calls, verification and cleanup, with margin for rate-limit delays. Measure those phases separately on your workload; the dated smoke sample is in the cookbook.
[Billing and limits](/billing) lists project-wide creation and concurrency limits. The server's two-session cap and allocation pacing apply to one process. Each submitted rollout uses two allocations; an episode without submission uses one. Coordinate additional workers against the same project budget. The E2B SDK honors `Retry-After` for API 429 responses; retrying an uncertain episode can duplicate work.
Use actual CPU/RAM sizes and full running lifetime with [current pricing](https://e2b.dev/pricing): sum `running_seconds * (vCPUs * CPU_rate + GiB * memory_rate)` across policy and verifier resources. Resources are billed while waiting for the model. Cleanup confirmation gives a conservative lifetime estimate, and completed resources close before optimizer time. Add model/GPU and subscription costs separately.
## Other rollout and training paths
Use Terminus for terminal steps inside one code-repair episode. [NeMo Gym](/agents/nemo-gym) offers a provider configuration for its own task harnesses. [OpenEnv Harbor](https://github.com/huggingface/OpenEnv/tree/v0.8.0/src/openenv/harbor) drives a whole coding-agent rollout with separate task/template and token-capture prerequisites. Native Jupyter shares mutable policy-kernel state, so keep task verification outside that kernel.
For managed compute, see the [Fireworks Training API RL](https://docs.fireworks.ai/fine-tuning/training-api/cookbook/rl) and [agentic RL](https://docs.fireworks.ai/fine-tuning/training-api/cookbook/agentic-rl) recipes. That training path requires an adapter associating generated tokens with their probabilities and tool-output masks; it is a separate integration.
# Pi
Source: https://docs.e2b.dev/agents/pi
Run Pi in a secure E2B sandbox with full filesystem, terminal, and git access.
[Pi](https://pi.dev) is a minimal terminal coding agent that can be extended with TypeScript extensions, skills, prompt templates, and themes. E2B provides a pre-built `pi` template with Pi already installed.
## CLI
Create a sandbox with the [E2B CLI](/cli).
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
e2b sbx create pi
```
Once inside the sandbox, start Pi.
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
pi
```
Use `/login` to authenticate with a supported subscription provider, or provide an API key through an environment variable.
## Run headless
Use `-p` for non-interactive print mode. Pi does not show permission prompts for its built-in tools, so no auto-approval flag is needed. The sandbox isolates Pi from your host machine, but sandboxes can reach the open internet by default — restrict outbound traffic with [network rules](/network/internet-access).
Pi supports multiple model providers. The examples below use Anthropic through the `ANTHROPIC_API_KEY` environment variable.
Pi can read and modify files and run shell commands directly. E2B contains those operations inside the sandbox, so Pi cannot access your host files or credentials. It can still make outbound network requests — use [outbound network rules](/network/internet-access) to limit access by CIDR or hostname. Hostname rules apply to HTTP(S) traffic only; use CIDR rules for other protocols.
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const sandbox = await Sandbox.create('pi', {
envs: { ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY },
})
const result = await sandbox.commands.run(
`pi -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("pi", envs={
"ANTHROPIC_API_KEY": os.environ["ANTHROPIC_API_KEY"],
})
result = sandbox.commands.run(
'pi -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('pi', {
envs: { ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY },
timeoutMs: 600_000,
})
await sandbox.commands.run(
'git clone --depth 1 https://x-access-token:$GITHUB_TOKEN@github.com/your-org/your-repo.git /home/user/repo',
{ envs: { GITHUB_TOKEN: process.env.GITHUB_TOKEN } }
)
const result = await sandbox.commands.run(
`cd /home/user/repo && pi -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("pi", envs={
"ANTHROPIC_API_KEY": os.environ["ANTHROPIC_API_KEY"],
}, timeout=600)
sandbox.commands.run(
"git clone --depth 1 https://x-access-token:$GITHUB_TOKEN@github.com/your-org/your-repo.git /home/user/repo",
envs={"GITHUB_TOKEN": os.environ["GITHUB_TOKEN"]},
)
result = sandbox.commands.run(
'cd /home/user/repo && pi -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()
```
## 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 `pi` template.
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
// template.ts
import { Template } from 'e2b'
export const template = Template()
.fromTemplate('pi')
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
# template.py
from e2b import Template
template = (
Template()
.from_template("pi")
)
```
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
// build.ts
import { Template, defaultBuildLogger } from 'e2b'
import { template as piTemplate } from './template'
await Template.build(piTemplate, 'my-pi', {
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 pi_template
Template.build(pi_template, "my-pi",
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
Connect to the sandbox via SSH for interactive sessions
# Prime Agent
Source: https://docs.e2b.dev/agents/prime
Run Prime Agent in a secure E2B sandbox with full filesystem, terminal, and git access.
[Prime Agent](https://github.com/PrimeIntellect-ai/prime-agent) is Prime Intellect's open-source coding and research agent for general and long-running work, built around a persistent IPython kernel and recursive subagents. E2B provides a pre-built `prime` template with Prime Agent already installed.
## CLI
Create a sandbox with the [E2B CLI](/cli).
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
e2b sbx create prime
```
Once inside the sandbox, start Prime Agent.
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
prime-agent
```
On first launch, run `/login` to pick a subscription (Claude Pro/Max, ChatGPT Plus/Pro, GitHub Copilot) or an API-key provider.
## Run headless
Use `-p` to print the response and exit. Prime Agent has no approval prompts or auto-approve flag — it runs model-generated Python and project commands with the permissions of the user it runs as, which is why upstream recommends running it inside a sandbox. The sandbox isolates the agent from your host machine, but sandboxes can reach the open internet by default — restrict outbound traffic with [network rules](/network/internet-access). Prime Agent authenticates with standard provider environment variables (`ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, `GEMINI_API_KEY`, `XAI_API_KEY`, `OPENROUTER_API_KEY`), or `PRIME_API_KEY` for Prime Inference. Pick a model with `--provider ` and `--model `.
Running tool calls without approval is contained by the sandbox: the agent cannot touch your host machine, local files, or credentials. It can still make outbound network requests — internet access is enabled by default. To limit where an unattended agent can connect, configure [outbound network rules](/network/internet-access) (`allowInternetAccess`, plus allow/deny lists by CIDR or hostname). Hostname rules apply to HTTP(S) traffic only; use CIDR rules for other protocols.
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const sandbox = await Sandbox.create('prime', {
envs: { ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY },
})
const result = await sandbox.commands.run(
`prime-agent -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("prime", envs={
"ANTHROPIC_API_KEY": os.environ["ANTHROPIC_API_KEY"],
})
result = sandbox.commands.run(
'prime-agent -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('prime', {
envs: { ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY },
timeoutMs: 600_000,
})
await sandbox.commands.run(
'git clone --depth 1 https://x-access-token:$GITHUB_TOKEN@github.com/your-org/your-repo.git /home/user/repo',
{ envs: { GITHUB_TOKEN: process.env.GITHUB_TOKEN } }
)
const result = await sandbox.commands.run(
`cd /home/user/repo && prime-agent -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("prime", envs={
"ANTHROPIC_API_KEY": os.environ["ANTHROPIC_API_KEY"],
}, timeout=600)
sandbox.commands.run(
"git clone --depth 1 https://x-access-token:$GITHUB_TOKEN@github.com/your-org/your-repo.git /home/user/repo",
envs={"GITHUB_TOKEN": os.environ["GITHUB_TOKEN"]},
)
result = sandbox.commands.run(
'cd /home/user/repo && prime-agent -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()
```
## Streaming JSON
Use `--mode json` to get a real-time JSONL event stream. The first line is a session header, followed by events as they occur — `agent_start`, `turn_start`, `message_start`/`message_update`/`message_end`, `tool_execution_start`/`tool_execution_end`, `turn_end`, and `agent_end`. For bidirectional integrations, `--mode rpc` exposes the same session over stdin/stdout.
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const sandbox = await Sandbox.create('prime', {
envs: { ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY },
})
const result = await sandbox.commands.run(
`cd /home/user/repo && prime-agent --mode json "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 === 'tool_execution_start') {
console.log(`[tool] ${event.toolName}`)
} else if (event.type === 'message_end') {
console.log(`[message] ${event.message.role}`)
} else if (event.type === 'agent_end') {
console.log(`[done] ${event.messages.length} messages`)
}
}
},
}
)
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("prime", 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"] == "tool_execution_start":
print(f"[tool] {event['toolName']}")
elif event["type"] == "message_end":
print(f"[message] {event['message']['role']}")
elif event["type"] == "agent_end":
print(f"[done] {len(event['messages'])} messages")
result = sandbox.commands.run(
'cd /home/user/repo && prime-agent --mode json "Find and fix all TODO comments"',
on_stdout=handle_event,
)
sandbox.kill()
```
## Session management
Prime Agent saves sessions to disk and runs them behind a background daemon, so work survives across commands in a long-lived sandbox. Use `-c` to continue the most recent session, `-r ` to resume a specific one, and `prime-agent agents` / `prime-agent attach ` to list and reattach to agents still running in the sandbox.
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const sandbox = await Sandbox.create('prime', {
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 && prime-agent -p "Analyze the codebase and create a refactoring plan"`,
{ onStdout: (data) => process.stdout.write(data) }
)
// Continue the most recent session with a follow-up task
const followUp = await sandbox.commands.run(
`cd /home/user/repo && prime-agent -c -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
from e2b import Sandbox
sandbox = Sandbox.create("prime", envs={
"ANTHROPIC_API_KEY": os.environ["ANTHROPIC_API_KEY"],
}, timeout=600)
# Start a new session
initial = sandbox.commands.run(
'cd /home/user/repo && prime-agent -p "Analyze the codebase and create a refactoring plan"',
on_stdout=lambda data: print(data, end=""),
)
# Continue the most recent session with a follow-up task
follow_up = sandbox.commands.run(
'cd /home/user/repo && prime-agent -c -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()
```
## 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 `prime` template.
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
// template.ts
import { Template } from 'e2b'
export const template = Template()
.fromTemplate('prime')
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
# template.py
from e2b import Template
template = (
Template()
.from_template("prime")
)
```
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
// build.ts
import { Template, defaultBuildLogger } from 'e2b'
import { template as primeTemplate } from './template'
await Template.build(primeTemplate, 'my-prime', {
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 prime_template
Template.build(prime_template, "my-prime",
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
Connect to the sandbox via SSH for interactive sessions
# Pydantic AI
Source: https://docs.e2b.dev/agents/pydantic-ai
Run Pydantic AI coding tools in E2B and keep their workspace across runs and worker restarts.
[Pydantic AI](https://pydantic.dev/docs/ai/) has a native E2B workspace provider in its official harness package. `E2BSandbox` supplies the execution environment; capabilities such as `Shell`, `FileSystem`, and `Coder` run their commands and file operations inside that environment.
Pydantic AI manages the agent loop and workspace selection. E2B supplies the isolated computer and its persistent filesystem. Your application decides when to pause or delete the sandbox.
## Install and authenticate
Use Python 3.11 or newer. Install the native integration and the model provider used below:
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
pip install "pydantic-ai-harness[e2b]>=0.54.0" "pydantic-ai-slim[openai]>=2.54.0"
```
Set keys in the process running the agent:
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
export E2B_API_KEY="your_e2b_api_key"
export OPENAI_API_KEY="your_openai_api_key"
```
The example uses `gpt-4.1-mini` to keep a small coding task inexpensive. Other providers need their corresponding Pydantic AI extra and model configuration. API keys remain on your machine; the sandbox does not inherit your environment.
## Fix a Python log-analysis CLI
This CLI incorrectly counts only HTTP 500 responses as server errors. Seed a small project, let the agent fix it, and verify the actual output independently:
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import asyncio
from pydantic_ai import Agent
from pydantic_ai.usage import UsageLimits
from pydantic_ai.workspaces import Workspace
from pydantic_ai_harness.e2b_sandbox import E2BSandbox, E2BSandboxBackend
from pydantic_ai_harness.filesystem import FileSystem
from pydantic_ai_harness.shell import Shell
async def main():
backend = E2BSandboxBackend(
working_dir="/home/user/log-cli",
env={"PYTHONDONTWRITEBYTECODE": "1"},
sandbox_timeout=300,
allow_internet_access=False,
)
try:
workspace = Workspace(backend)
await workspace.write_text("log_stats.py", """import json
from pathlib import Path
records = [json.loads(line) for line in Path('requests.jsonl').read_text().splitlines()]
print(sum(record['status'] == 500 for record in records))
""")
await workspace.write_text("requests.jsonl", '{"status": 200}\n{"status": 500}\n{"status": 503}\n')
agent = Agent(
"openai:gpt-4.1-mini",
capabilities=[
E2BSandbox(),
Shell(tools=["run_command"], default_timeout=20),
FileSystem(tools=["read_file", "write_file", "edit_file"], content_hashes=False),
],
model_settings={"max_tokens": 1024, "timeout": 30, "parallel_tool_calls": False},
)
async with asyncio.timeout(120):
result = await agent.run(
"Fix log_stats.py to count all HTTP 500–599 responses as server errors. "
"Read both files and run the CLI. Do not change requests.jsonl.",
workspace=backend,
usage_limits=UsageLimits(request_limit=6, input_tokens_limit=30_000),
)
check = await workspace.run(["python", "log_stats.py"], timeout=20)
assert check.exit_code == 0 and check.stdout.strip() == "2", check
assert await workspace.read_text("requests.jsonl") == '{"status": 200}\n{"status": 500}\n{"status": 503}\n'
print(f"Sandbox: {result.workspace.ref.id}; verified server errors: 2")
finally:
if backend.ref is not None:
await E2BSandbox().destroy(backend.ref)
if __name__ == "__main__":
asyncio.run(main())
```
The result prints your sandbox ID and `verified server errors: 2`. The application checks the executed CLI rather than trusting the agent's explanation. Internet access is disabled because this project needs no downloads; model calls happen on the host.
The supplied backend owns the working directory, environment, and lifetime settings. The agent's `E2BSandbox()` capability does not override those settings when `workspace=backend` is supplied. Initialization is inside `try` so a failed file upload or agent run still reaches cleanup.
## Continue after a pause
To keep the workspace, save `result.workspace.ref` and `result.all_messages()` instead of deleting it after the first run. Get the underlying E2B SDK handle with `await backend.get_sandbox()` and pause it with `await sandbox.pause()`.
For the second run, pass the saved ref as `workspace=` and the saved messages as `message_history=`. Configure `E2BSandbox` with the same working directory and environment for reattachment. The first workspace operation reconnects and resumes the paused sandbox; its files are still there. The ref identifies the workspace, while message history also preserves the conversation. A ref contains no credentials, so another process needs its own `E2B_API_KEY`.
A killed or unavailable sandbox raises `WorkspaceUnavailableError`; the integration does not quietly replace it with a new empty sandbox. Use `workspace="new"` only when you intentionally want a separate workspace.
## Recover a Temporal worker
Compose the agent with `TemporalDurability` from `pydantic_ai.durable_exec.temporal`, register it on a `PydanticAIWorkflow`, and connect the Temporal client with `PydanticAIPlugin`. Install the harness's `temporal` extra too. The plugin registers model/tool/workspace activities for the workflow's agents.
Persist the workspace ref before testing a worker crash. A replacement worker uses the same task queue, workflow, agent registration, and ref to continue in that sandbox. Temporal replay preserves completed operations; activities interrupted before their completion is recorded may execute again. Make their side effects repeatable. Killing a worker is not proof that a remote shell command stopped, and recovery is not an exactly-once guarantee for sandbox creation.
## Ownership and lifetime
* Pydantic AI does not delete the sandbox at the end of an agent run. It can keep running and billing until you pause it, delete it, or its lifetime expires.
* Native E2B sandboxes default to a 3,600-second lifetime and pause on timeout. `sandbox_timeout` also applies on reattachment. A command's `timeout` separately bounds that command.
* Call `await E2BSandbox().destroy(ref)` when the application is finished. It deletes a paused sandbox by ID without resuming it. Preserve the ref and report cleanup errors so deletion can be retried.
* File tools and shell commands share the sandbox. File-tool path restrictions do not constrain arbitrary shell commands, and the model should not receive host-file transfer tools or API keys it does not need.
See [E2B persistence](/sandbox/persistence), [the native Pydantic AI guide](https://pydantic.dev/docs/ai/harness/e2b-sandbox/), and [Pydantic AI's Temporal documentation](https://pydantic.dev/docs/ai/capabilities/durable_execution/temporal/).
## Complete cookbook examples
* **Pause/resume:** `pydantic-ai-python` diagnoses a JSONL log CLI, pauses the sandbox, then repairs it in a second run. It checks the same ID, preserved file checksums, original regression tests, and cleanup. [View cookbook](https://github.com/e2b-dev/e2b-cookbook/tree/7976c3d02982e56755f6883c92f6fbc6f5e2ab1f/examples/pydantic-ai-python).
* **Temporal crash recovery:** `pydantic-ai-temporal-python` kills and restarts a worker after a durable diagnosis checkpoint, then verifies the same sandbox ID and no additional running/paused sandboxes. [View cookbook](https://github.com/e2b-dev/e2b-cookbook/tree/7976c3d02982e56755f6883c92f6fbc6f5e2ab1f/examples/pydantic-ai-temporal-python).
# Code rewards for TRL GRPO
Source: https://docs.e2b.dev/agents/trl
Use E2B sandboxes to compute Python code rewards for Hugging Face TRL GRPO training. Get a first reward without a GPU, then connect it to your training run.
[TRL](https://huggingface.co/docs/trl/v1.14.2/grpo_trainer) is Hugging Face's library for training language models. With GRPO, a model generates multiple solutions to a task and learns from their relative rewards. E2B can execute the generated code in isolated sandboxes, while your evaluator checks its output and returns a reward to TRL.
The [E2B cookbook example](https://github.com/e2b-dev/e2b-cookbook/tree/main/examples/trl-grpo-python) trains a model to process JSON API-usage records: sum tokens, count requests and filter successful requests per customer. Start by evaluating fixed solutions without a GPU or model download, then use the same reward callback for training.
Use this workflow when you can check correctness with test inputs and trusted expected outputs, such as extracting or transforming structured data. Generated programs run remotely instead of on the machine holding your training data and model credentials. You do not need to install a code-execution service on the GPU host; that host calls E2B through the Python SDK. Remote execution adds sandbox creation and network overhead, so measure the reward stage before scaling a training job.
## Where E2B fits
```mermaid theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
flowchart TB
subgraph host["Your training machine"]
generate["TRL generates Python"]
grade["Evaluator compares JSON with expected outputs"]
update["TRL updates model weights"]
end
subgraph remote["E2B: pool of reusable workers"]
execute["Run each test in a fresh isolated process"]
end
generate -->|Code and inputs| execute
execute -->|Program output| grade
grade -->|Reward| update
update --> generate
```
Model generation and weight updates stay on your training machine. E2B receives candidate code and test inputs; expected outputs and reward calculation stay outside the candidate sandbox. Each candidate runs on a *worker*: an E2B sandbox that the pool keeps between callbacks. Every test still starts in a fresh process.
## Get your first code reward
The commands use Python 3.12; the package supports Python 3.11–3.13. You also need [uv](https://docs.astral.sh/uv/getting-started/installation/) and an [E2B API key](/api-key). This reward smoke test installs only the evaluator dependencies and uses E2B sandbox resources.
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
git clone https://github.com/e2b-dev/e2b-cookbook.git
cd e2b-cookbook/examples/trl-grpo-python
uv sync --locked --python 3.12 --no-dev
cp .env.example .env
```
Set `E2B_API_KEY` in `.env`, then run:
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
uv run --locked --no-dev --env-file .env python reward_smoke.py
```
The evaluator runs fixed programs against three test cases and returns:
```json theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
{
"correct": 1.0,
"wrong": 0.0,
"partial": 0.3333333333333333,
"syntax_error": 0.0,
"timeout": 0.0
}
```
A correct program passes every case. The partial program prints `{}`, so it passes only the empty-input case. Expected results are compared as complete JSON values; a matching prefix or candidate-supplied score is not accepted. Invalid syntax needs no sandbox. The command also verifies that no running or paused sandbox remains for its run.
## Follow one solution from code to reward
The first task asks the model to sum `tokens` for each `customer`. For example, the evaluator writes this input to the candidate's stdin:
```json theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
[
{"customer": "acme", "tokens": 12},
{"customer": "beta", "tokens": 3},
{"customer": "acme", "tokens": 8}
]
```
The expected output is `{"acme": 20, "beta": 3}`. A complete solution looks like this; the model must return it inside a fenced Python code block:
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import json
import sys
totals = {}
for record in json.load(sys.stdin):
customer = record["customer"]
totals[customer] = totals.get(customer, 0) + record["tokens"]
print(json.dumps(totals))
```
The reward callback extracts the code block and checks Python syntax on the host. For valid code, it borrows a pool worker, writes the program and runs each input in a fresh isolated process. The host parses each output as JSON and compares it with the trusted expectation. The candidate receives inputs, but neither expected outputs nor a grading script.
| Test case | Expected output | Correct program | `print('{}')` |
| - | - | - | - |
| Empty input | `{}` | Pass | Pass |
| Repeated customers | `{"acme": 20, "beta": 3}` | Pass | Fail |
| Zero and large token totals | `{"zero": 0, "large": 100000}` | Pass | Fail |
| Reward: passed cases / all cases | — | `3 / 3 = 1.0` | `1 / 3` |
This score measures program behavior rather than similarity to a reference implementation. GRPO compares rewards among solutions generated for the same prompt. If all solutions receive the same reward, there is no relative reward difference to learn from; inspect task difficulty, output formatting and completion length before increasing the training duration.
## Connect the reward to TRL
Connecting the reward to a training run takes three steps: check the baseline reward for your model, train with the same model, then compare held-out results before and after training.
### Check baseline rewards
Choose an instruction-tuned coding model with a suitable chat template and enough GPU memory for training. Set `TRAIN_MODEL` in your shell to its Hugging Face model ID or local directory. The example has no default model: an untuned sample of `Qwen/Qwen2.5-0.5B-Instruct` gave zero reward for all 24 solutions. Check the reward signal for your chosen model before extending a run.
On your training machine, install the training extra and check the baseline first:
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
uv sync --locked --python 3.12 --extra training --no-dev
uv run --locked --extra training --no-dev --env-file .env python train.py \
--model "$TRAIN_MODEL" --baseline-only
```
`--baseline-only` samples solutions for the training and held-out tasks, prints example completions and writes `training_results/baseline.json`. It does not update model weights or save a checkpoint. Inspect `baseline.train_tasks.eval_reward`, `baseline.held_out.eval_reward` and their `eval_frac_reward_zero_std` metrics before starting training. The mean reward is defined in [Interpret held-out evaluation](#interpret-held-out-evaluation). Unlike the fixed-program smoke, it uses your model compute and E2B resources.
Look for differing rewards among solutions to the same prompt. All-zero or all-one groups provide no relative learning signal, and identical partial scores also give none. Inspect the printed code and review model capability, prompt/schema and completion length before extending a job.
### Run training
When the baseline is useful, train with the same model:
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
uv run --locked --extra training --no-dev --env-file .env python train.py \
--model "$TRAIN_MODEL"
```
### Task schema and reward interface
The script passes its async `pool.code_reward` to `GRPOTrainer` as `reward_funcs`. TRL supplies the dataset's `test_cases` column with each batch of completions and expects one score per completion, in order. This uses TRL's native custom reward interface; no E2B trainer package or sync/async bridge is needed.
The trainer connection in `train.py` is shown below. This is an excerpt from the complete script: `args` comes from its command-line options, and `config` is the `GRPOConfig` built there.
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import asyncio
from datasets import Dataset
from trl import GRPOTrainer
from rewards import RewardPool
from tasks import EVAL_TASKS, TRAIN_TASKS
pool = RewardPool(args.sandbox_workers, args.create_interval)
try:
trainer = GRPOTrainer(
model=args.model,
args=config,
reward_funcs=pool.code_reward,
train_dataset=Dataset.from_list(TRAIN_TASKS),
eval_dataset=Dataset.from_list(EVAL_TASKS),
)
trainer.train()
finally:
asyncio.run(pool.close())
```
Each dataset row contains a conversational `prompt` and a `test_cases` list. The callback has the signature `async def code_reward(completions, test_cases, **kwargs)`: TRL aligns each completion with its cases, and `**kwargs` accepts the other trainer fields. The callback preserves that order even when sandbox evaluations finish in a different order. See [TRL's custom reward interface](https://huggingface.co/docs/trl/v1.14.2/grpo_trainer#using-a-custom-reward-function) for the framework contract.
| Dataset column | Shape | Consumer |
| - | - | - |
| `task_id` | String identifying the task. | Task organization. |
| `prompt` | List of messages, each with string `role` and `content`. | Model generation. |
| `test_cases` | List of objects with JSON strings `input` and `output`. | Host reward callback; `input` is stdin and `output` is the trusted expectation. |
The callback returns a list of numeric rewards in `[0, 1]`, one per completion. Keeping each case's JSON as a string lets `Dataset.from_list` load records with different customer names without changing its column schema.
### Training configuration
The configuration uses your chosen model, two generations per prompt and eight training steps on a CUDA GPU. Model weights download to your machine. E2B supplies code execution, not the training GPU; no model-provider API key or vLLM service is required. The lockfile pins the example's dependencies, including TRL 1.14.2.
| Setting | Default | What it controls |
| - | - | - |
| `--model` | Required | Model on your host, with a chat template and useful baseline rewards for these tasks. |
| `--baseline-only` | Off | Evaluate both task splits without updating weights. |
| `num_generations` in `train.py` | `2` | Solutions sampled per prompt for relative rewards. Keep generation batch sizes compatible with TRL's grouping requirements. |
| `--max-steps` | `8` | Training updates. This is a short workflow example, not a guaranteed convergence target. |
| `--max-completion-length` | `256` | Maximum generated tokens. Too short a budget can truncate the program or its closing code fence. |
| `--sandbox-workers` | `2` | Persistent workers, each evaluating one candidate at a time. This does not set GPU batch size. |
| `--create-interval` | `1.0 s` | Creation pacing. Use at least 1 second on Hobby or 0.2 seconds on Pro, leaving capacity for other clients. |
| Process deadline / worker TTL | `2 s` / `600 s` | Per-test execution limit; worker TTL is renewed every 200 seconds while the pool lives. |
Change CLI settings with `train.py --model ... --max-steps ... --max-completion-length ...`; change generation counts and batch settings together in `train.py`. The default uses BF16 on supported CUDA devices and otherwise FP32, with gradient checkpointing and no vLLM requirement.
The reward smoke test verifies E2B execution and grading. The short training configuration is a starting point for your experiment; GPU memory and learning results depend on the model, generation settings and tasks. Inspect the reward signal before extending a run. The cookbook includes a slower CPU diagnostic for the training and checkpoint path.
### Interpret held-out evaluation
The run saves model weights and tokenizer under `training_results/checkpoint/` and metrics in `training_results/metrics.json`. It evaluates separate tasks before and after training. Compare their mean rewards to check whether fitting the training tasks transfers to these held-out tasks.
In `metrics.json`, `before.eval_reward` and `after.eval_reward` are mean test-pass fractions over sampled completions. A mean reward of `0.5` does not mean half the programs are entirely correct: partial solutions contribute too. Compare the same held-out tasks and generation settings before and after training, and repeat measurements before drawing conclusions from a small improvement. `history` contains step-level training logs; a rising training reward can reflect fitting the training tasks rather than improvement on unseen tasks.
The held-out prompts in this example ask for input-token totals and error counts, rather than repeating the training instructions. For your own experiment, keep evaluation prompts and cases outside the training dataset.
Also inspect `frac_reward_zero_std` in the training entries of `history`. It is the fraction of prompt groups whose sampled solutions all receive the same reward. A value near `1.0` means those groups provide no relative reward signal. If rewards are all zero, inspect a generated program for missing fields, incomplete code or a task beyond the model's capability. If rewards are all one, try harder cases. More training steps alone do not fix either problem.
## Adapt the task
Edit `tasks.py` to define your instruction, JSON inputs and expected outputs. For example, this defines an output-token aggregation task using the existing helpers:
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from tasks import case, task
new_task = task(
"output-tokens",
'Sum the integer "output_tokens" field per customer. '
'Do not add the integer "input_tokens" field.',
[
case([], {}),
case(
[
{"customer": "acme", "input_tokens": 50, "output_tokens": 4},
{"customer": "acme", "input_tokens": 10, "output_tokens": 6},
{"customer": "beta", "input_tokens": 20, "output_tokens": 0},
],
{"acme": 10, "beta": 0},
),
],
)
```
Add the `task(...)` call to `TRAIN_TASKS` in `tasks.py`. The import line is only needed when the snippet runs as a separate file. Keep different evaluation prompts and cases in `EVAL_TASKS`. `case(...)` serializes inputs and outputs as JSON strings, keeping the dataset schema stable as customer names change.
The task helper already defines `customer` as a string in the prompt. This instruction adds `output_tokens` and `input_tokens` as integers. Name any additional input fields and their types in the instruction: the model sees only the prompt, not the test cases, so it must not have to guess field names.
Validate your cases with a known correct solution before training. To check the changed first task with `reward_smoke.py`, update its fixed candidate programs and expected scores together. Include empty input, repeated customers and values that expose shortcuts; the example above makes summing `input_tokens` visibly incorrect. If you change the stdin/stdout format, update `rewards.py` as well as the prompt and cases.
## Execution and failure behavior
### Sandbox pool and concurrency
With six valid completions and two workers, the pool evaluates candidates in waves and keeps the same workers for the next callback. This avoids creating six separate sandboxes and repeating creation on subsequent steps. Each candidate borrows one worker exclusively; extra candidates wait for a free worker. Pool size is separate from GPU batch size, and one pool serves one single-process trainer; distributed training is outside this recipe.
### Isolation and timeouts
The pool reuses workers across callbacks. Each test starts a fresh unprivileged Python runtime in private Linux mount/PID/network/IPC namespaces. Worker files are read-only; writable temporary directories and descendant processes disappear after the case. Test cases and candidates do not share writable state.
A two-second remote limit applies to each test. A trusted supervisor runs all cases in one SDK command with a deadline of `2 × case count + 10` seconds. Output exceeding 64 KiB fails grading.
The key and expected outputs stay on the host. Workers use secured access, disabled outbound internet and a fresh network namespace per test. The task needs only Python's standard library. See [internet access](/network/internet-access).
Every new worker first runs a preflight check of its isolation setup on the default template. The cookbook relies on Linux `unshare`, mount namespaces, `setpriv`, Python 3 and GNU `timeout`; unsupported templates fail rather than silently weaken isolation. This example is not a formally audited adversarial grading platform. Validate any changes to its template or execution boundaries.
### Failures and retries
Wrong output and candidate runtime failures score zero. Recoverable transport, unavailable-service, rate-limit or lost-worker errors get one evaluation retry on a replacement worker; worker creation gets up to two attempts. Candidate errors are not retried. Authentication/configuration, failed isolation setup, exhausted retries and unresolved cleanup abort the run instead of becoming false learning signals.
### Cleanup and cancellation
The trainer owns the pool and closes it in `finally`. Shutdown kills the workers and reconciles run metadata. Each worker has a 600-second kill-on-timeout TTL, renewed every 200 seconds while the pool is alive, including during model generation. Your plan's maximum continuous runtime still applies.
A stopped local coroutine alone does not prove remote execution stopped. Cancellation makes the pool unusable for further callbacks; the caller must close it to terminate its workers. If the host process exits without running cleanup, the last renewed TTL is the backstop.
### Account limits and cost
| Limit | Hobby | Pro baseline |
| - | - | - |
| Concurrent sandboxes | `20` | `100`, with add-ons for more. |
| Sandbox creations per second | `1` | `5` |
| Maximum continuous sandbox runtime | `1 hour` | `24 hours` |
Creation rate and concurrency are different limits, and both are shared across your project. On Hobby, two workers is a sensible start. For Pro workloads, you can raise `--sandbox-workers` and lower `--create-interval` within your shared capacity. The [cookbook README](https://github.com/e2b-dev/e2b-cookbook/tree/main/examples/trl-grpo-python#account-limits-and-cost) explains the pacing arithmetic. This is scaling guidance, not a verified 100-worker result.
### Measure execution and training overhead
The smoke prints pool metrics, and training saves them under `pool` in `metrics.json`. The [cookbook README](https://github.com/e2b-dev/e2b-cookbook/tree/main/examples/trl-grpo-python#measure-your-reward-stage) defines each counter. `worker_seconds_estimate` includes idle time while the GPU generates code or updates weights.
TRL's step history also records `e2b/reward_seconds` and `e2b/worker_seconds_since_previous_reward`. The latter includes idle time since the prior callback. TRL averages callback metrics within a logging step, so these are interval estimates rather than exclusive per-step billing allocations. The final pool total includes the remaining time through cleanup. Compare callback time with whole training step time before increasing the pool size.
Measure your own tasks and generation settings. If candidate evaluations spend time waiting for a worker, a larger pool may reduce callback time. If model generation or weight updates dominate the step, extra workers can mostly increase idle cost. To compare pool sizes, keep the model, completions and test cases the same and measure both callback time and total step time.
Estimate sandbox compute cost as **total worker-seconds × the per-worker resource rate**. Calculate that rate from your template's vCPU count and memory allocation using [current resource prices](https://e2b.dev/pricing). For a training interval, use `e2b/worker_seconds_since_previous_reward`; for the whole run, use the final `worker_seconds_estimate`. Include idle worker time: two workers waiting ten seconds for model generation add about twenty worker-seconds even without executing code. Model/GPU compute and the plan subscription are separate costs; confirm [billing limits](/billing) before scaling.
For a measured smoke example with its workload and configuration, see [Example smoke measurements](https://github.com/e2b-dev/e2b-cookbook/tree/main/examples/trl-grpo-python#example-smoke-measurements) in the cookbook.
## Troubleshoot a run
| Symptom | What to check |
| - | - |
| Missing key or authentication failure | Set `E2B_API_KEY` on the host and run with `--env-file .env`. A key in another shell or CLI login does not configure this command. |
| Default training refuses to start | The default requires CUDA. The cookbook has a slower `--cpu` diagnostic; it does not establish training quality or GPU memory requirements. |
| Smoke passes but model rewards are zero | Inspect actual model completions: does the prompt specify exact field names and types, and is the code complete and fenced? Check task difficulty, stdin/stdout format and completion length. Debug prints on stdout invalidate JSON. |
| `frac_reward_zero_std` stays near `1.0` | All sampled solutions for most prompts receive equal scores. Check whether tasks are too hard or too easy for the model, then review generation count and task diversity. |
| Out of GPU memory | Reduce model size or sequence length and review TRL batch settings. `--sandbox-workers` controls E2B work and does not reduce model memory. |
| Backend errors or account limits | Treat these as failed evaluations, not bad model solutions. Check credentials, service errors and account limits; reduce sandbox concurrency before retrying the run. |
| Cleanup fails or the process is interrupted | Inspect the logged run and sandbox IDs. Confirm remote state; a stopped local process alone does not confirm sandbox termination. The TTL is the fallback. |
| Training reward rises but held-out reward does not | Check for overfitting, repeated evaluation tasks and reward shortcuts. Increase the diversity of trusted cases before assuming a longer run will help. |
## Managed training with Fireworks AI
For GPU compute managed by a provider, see the [Fireworks AI Training API](https://docs.fireworks.ai/fine-tuning/training-api/introduction) and its [reinforcement learning cookbook](https://docs.fireworks.ai/fine-tuning/training-api/cookbook/rl). E2B supplies the code-execution environment; Fireworks supplies model computation through its training API. This cookbook uses TRL on your CUDA host. Combining its evaluator with a Fireworks training loop is a separate integration and requires adapting the API calls.
For full setup, runnable code and offline checks, see the [E2B cookbook example](https://github.com/e2b-dev/e2b-cookbook/tree/main/examples/trl-grpo-python).
# Vercel AI SDK
Source: https://docs.e2b.dev/agents/vercel-ai-sdk
Run AI SDK agents and tools on E2B sandboxes with the @e2b/ai-sdk-sandbox provider.
The [AI SDK](https://ai-sdk.dev) is Vercel's TypeScript toolkit for building agents. AI SDK 7 introduces a sandbox interface — a standard surface (`run`, `spawn`, file I/O) that tools and [harness agents](https://ai-sdk.dev/v7/docs/ai-sdk-harnesses/overview) execute code against. [`@e2b/ai-sdk-sandbox`](https://www.npmjs.com/package/@e2b/ai-sdk-sandbox) implements that interface on E2B: your agent's code runs in an isolated Firecracker microVM instead of your server, with a real Debian toolchain, pause/resume persistence, and outbound network controls.
The package is the E2B counterpart to `@ai-sdk/sandbox-vercel`. It is **experimental** and targets AI SDK 7.
## Install
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
npm install @e2b/ai-sdk-sandbox
```
Set `E2B_API_KEY` (get one from the console), or pass `apiKey` in the settings.
## Quickstart
`createE2BSandbox()` configures the provider without reaching E2B; the sandbox is created when you call `createSession()`.
```typescript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { createE2BSandbox } from '@e2b/ai-sdk-sandbox'
const session = await createE2BSandbox({ template: 'base' }).createSession()
await session.writeTextFile({ path: 'hello.txt', content: 'hi from e2b' })
const { stdout } = await session.run({ command: 'cat hello.txt' })
console.log(stdout) // "hi from e2b"
await session.destroy()
```
The session also supports `readTextFile`, and `spawn` for long-running processes with streamed stdout/stderr and `kill()`.
## Hand tools a sandbox
`session.restricted()` returns the same sandbox narrowed to the tool-safe surface — file I/O, `run`, `spawn` — with the lifecycle and network controls (`stop`, `destroy`, `ports`, `getPortUrl`, `setNetworkPolicy`) removed. That is the security boundary: pass the restricted view to an AI SDK tool's `execute()` via `experimental_sandbox`, and keep the full session with your application. Code the model drives can execute commands, but it cannot kill the box or loosen its network policy.
```typescript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { generateText, tool } from 'ai'
import { anthropic } from '@ai-sdk/anthropic'
import { z } from 'zod'
const session = await createE2BSandbox({ template: 'base' }).createSession()
const sandbox = session.restricted()
const result = await generateText({
model: anthropic('claude-sonnet-4-5'),
prompt: 'Check which Python version is installed.',
tools: {
bash: tool({
description: 'Run a shell command in an isolated E2B sandbox',
inputSchema: z.object({ command: z.string() }),
execute: async ({ command }) => {
const { stdout, stderr, exitCode } = await sandbox.run({ command })
return { stdout, stderr, exitCode }
},
}),
},
})
console.log(result.text)
await session.destroy()
```
See the [harness docs](https://ai-sdk.dev/v7/docs/ai-sdk-harnesses/overview) for the `restricted()` contract and the `experimental_sandbox` wiring in harness-managed tools.
## Settings
Everything goes in the object passed to `createE2BSandbox(...)`. Any of E2B's `SandboxOpts` (`template`, `envs`, `metadata`, `network`, …) are forwarded straight through, and the provider adds two options of its own:
```typescript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
const provider = createE2BSandbox({
template: 'base', // any E2B SandboxOpts
envs: { NODE_ENV: 'production' },
timeoutMs: 10 * 60 * 1000, // defaults to 30 min
ports: [3000], // provider option
setupCommands: ['sudo npm install -g pnpm@9'], // provider option
})
```
| Option | Default | What it does |
| - | - | - |
| `ports` | `[]` | Ports advertised on `session.ports`. The harness bridge binds to the first one; E2B can expose any listening port through `getHost`, so this is the list the session advertises. |
| `setupCommands` | `[]` | Commands run once on a fresh sandbox, before the harness bootstraps. For setup you control ahead of time, a prebuilt [template](/template/quickstart) is lighter. |
Already have a sandbox? Pass it as `sandbox` to reuse it. The provider then never touches its lifecycle — `stop()` and `destroy()` become no-ops and cleanup stays yours.
```typescript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from 'e2b'
const provider = createE2BSandbox({
sandbox: await Sandbox.create({ timeoutMs: 10 * 60_000 }),
})
```
## Run a coding agent in the sandbox
The provider plugs into AI SDK harness agents: the harness adapter (Claude Code, Codex) runs *inside* the E2B sandbox, and the host talks to it over a WebSocket bridge on the first advertised port.
```typescript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { HarnessAgent } from '@ai-sdk/harness/agent'
import { createClaudeCode } from '@ai-sdk/harness-claude-code'
import { createE2BSandbox } from '@e2b/ai-sdk-sandbox'
const agent = new HarnessAgent({
harness: createClaudeCode({
auth: { anthropic: { apiKey: process.env.ANTHROPIC_API_KEY! } },
startupTimeoutMs: 300_000, // first boot installs the CLI in the sandbox
}),
sandbox: createE2BSandbox({
template: 'pnpm-base', // custom template with pnpm preinstalled
ports: [4000], // bridge port — the adapter binds to ports[0]
timeoutMs: 30 * 60 * 1000,
}),
})
const session = await agent.createSession()
const result = await agent.generate({
session,
prompt: 'Write fizzbuzz.js and run it with node. Show me the output.',
})
console.log(result.text)
await session.destroy()
```
The claude-code adapter installs its own pinned CLI and bridge inside the sandbox with pnpm, so pass a [template](/template/quickstart) with pnpm preinstalled.
## Pause and resume
`stop()` **pauses** the sandbox — filesystem and memory preserved — rather than killing it; `destroy()` removes it for good. Resume works off a session-id tag in the sandbox metadata, so pass a `sessionId` to `createSession` if you plan to resume, then reconnect with `resumeSession` — even from a different process.
```typescript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
const provider = createE2BSandbox({ template: 'base' })
const first = await provider.createSession({ sessionId: 'demo-1' })
await first.writeTextFile({ path: 'state.txt', content: 'survived the pause' })
await first.stop() // pause (resumable)
// later — possibly a different process
const resumed = await provider.resumeSession({ sessionId: 'demo-1' })
console.log(await resumed.readTextFile({ path: 'state.txt' })) // "survived the pause"
await resumed.destroy()
```
## Network policy
Outbound access can be tightened or loosened on a running sandbox — from the full session only, never from the restricted view:
```typescript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
await session.setNetworkPolicy({
mode: 'custom',
allowedHosts: ['api.example.com'],
deniedCIDRs: ['169.254.169.254/32'],
})
```
Hostname rules apply to HTTP(S) traffic only; use CIDR rules for other protocols. See [internet access](/network/internet-access).
## Good to know
* `run` and `spawn` switch off E2B's 60-second per-command timeout, so long builds and background servers don't get cut off. The overall sandbox `timeoutMs` still applies.
* Relative paths in file operations and commands resolve against the session's working directory (`/home/user`).
* The default sandbox timeout is 30 minutes; pass `timeoutMs` to change it.
## Related guides
Build custom sandbox templates with pre-installed dependencies
Pause, resume, and manage sandbox lifecycle
Restrict outbound traffic with network rules
# Code contexts
Source: https://docs.e2b.dev/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)
```
# Interactive charts
Source: https://docs.e2b.dev/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://docs.e2b.dev/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.
# Customize the template
Source: https://docs.e2b.dev/code-interpreting/customize-template
Extend the Code Interpreter sandbox or build the production template from source
The Code Interpreter SDK runs on the public `code-interpreter-v1` sandbox template. You can customize it in two ways:
1. [Create a custom template](#create-a-custom-template) that extends `code-interpreter-v1` with extra packages or a different runtime — this is the recommended approach for most use cases.
2. [Build the production template](#build-the-production-template) directly from the [`code-interpreter` repository](https://github.com/e2b-dev/code-interpreter) — useful when contributing to the SDK or building the official `code-interpreter-v1` template yourself.
***
## Create a custom template
Use this when you need extra preinstalled packages or a different runtime. Your template builds on top of `code-interpreter-v1`, so everything the Code Interpreter SDK relies on stays in place.
### 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
```
### 2. Create a template file
```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");
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
# template.py
from e2b import Template
template = Template().from_template("code-interpreter-v1")
```
### 3. Create a build script
```typescript JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
// build.ts
import "dotenv/config";
import { Template, defaultBuildLogger } from 'e2b';
import { template } from './template';
async function main() {
await Template.build(template, 'code-interpreter-custom', {
cpuCount: 2,
memoryMB: 2048,
onBuildLogs: defaultBuildLogger(),
});
}
main().catch(console.error);
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
# build.py
from dotenv import load_dotenv
from e2b import Template, default_build_logger
from template import template
load_dotenv()
Template.build(
template,
alias="code-interpreter-custom",
cpu_count=2,
memory_mb=2048,
on_build_logs=default_build_logger(),
)
```
### 4. Set your environment variables
Create a `.env` file with your API key (loaded by `dotenv` / `load_dotenv()`):
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
E2B_API_KEY=e2b_***
```
### 5. Build 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
```
### 6. Use the custom template
```js JavaScript & TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Sandbox } from '@e2b/code-interpreter'
const sbx = await Sandbox.create("code-interpreter-custom")
const execution = await sbx.runCode("print('Hello, World!')")
console.log(execution.logs.stdout)
```
```python Python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b_code_interpreter import Sandbox
sbx = Sandbox.create("code-interpreter-custom")
execution = sbx.run_code("print('Hello, World!')")
print(execution.logs.stdout)
```
See [Install custom packages](/quickstart/install-custom-packages) for preinstalling specific Python and Node.js packages with `pip` / `npm`.
***
## Build the production template
To build the official `code-interpreter-v1` template from the [`code-interpreter` repository](https://github.com/e2b-dev/code-interpreter), use `build_prod.py`. This is the script CI and releases run, and it's the path to take when you're contributing to the SDK or want to rebuild the production template yourself.
### 1. Clone the repository
The template lives in the `template/` directory — run the remaining steps from there.
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
git clone https://github.com/e2b-dev/code-interpreter.git
cd code-interpreter/template
```
### 2. Install the build dependencies
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
pip install -r requirements-dev.txt
```
### 3. Provide your credentials
Create a `.env` file with your API key:
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
E2B_API_KEY=e2b_***
```
### 4. Build the template
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
python build_prod.py
```
To force a clean rebuild that ignores the layer cache, set `SKIP_CACHE=true`:
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
SKIP_CACHE=true python build_prod.py
```
# Streaming
Source: https://docs.e2b.dev/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://docs.e2b.dev/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](/code-interpreting/supported-languages/python)
* [JavaScript and TypeScript](/code-interpreting/supported-languages/javascript)
* [R](/code-interpreting/supported-languages/r)
* [Java](/code-interpreting/supported-languages/java)
* [Bash](/code-interpreting/supported-languages/bash)
You can use any custom language runtime by creating a [custom sandbox template](/template/quickstart).
# Run bash code
Source: https://docs.e2b.dev/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://docs.e2b.dev/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://docs.e2b.dev/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://docs.e2b.dev/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://docs.e2b.dev/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)
```
# Overview
Source: https://docs.e2b.dev/mcp-gateway
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](/mcp-gateway/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://docs.e2b.dev/mcp-gateway/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](/mcp-gateway/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://docs.e2b.dev/mcp-gateway/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 console 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://docs.e2b.dev/mcp-gateway/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](/mcp-gateway/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](/template/quickstart) - Get started with custom templates
* [Defining Templates](/template/defining-template) - Learn all template configuration options
* [Template Build](/template/build) - Understand the build process
# Examples
Source: https://docs.e2b.dev/mcp-gateway/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://docs.e2b.dev/mcp-gateway/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
# Client
Source: https://docs.e2b.dev/sdk-reference/js-sdk/v2.56.0/client
### E2B
E2B client with an explicitly bound connection configuration.
The resources exposed by the client (E2B.Sandbox,
E2B.Volume, E2B.Template, E2B.Secret) behave exactly
like the top-level `Sandbox` / `Volume` / `Template` / `Secret` exports,
except the options passed to the client are used as the defaults instead of
the environment variables.
Per-call options still take precedence over the client's options.
Multiple clients are fully isolated from each other and from the top-level
env-configured exports.
#### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { E2B } from 'e2b'
const client = new E2B({ apiKey: 'e2b_...', domain: 'e2b.dev' })
const sandbox = await client.Sandbox.create()
const volumes = await client.Volume.list()
await client.Template.build(client.Template().fromPythonImage('3'), 'my-env')
```
#### Constructors
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
new E2B(opts?: E2BClientOpts): E2B
```
Create a new client with the connection options bound to it.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `opts`? | `E2BClientOpts` | connection options used as the defaults for every call made through this client's resource classes. |
###### Returns
`E2B`
#### Properties
| Property | Modifier | Type | Description |
| - | - | - | - |
| `Sandbox` | `readonly` | *typeof* `Sandbox` | `Sandbox` class bound to this client's connection configuration. |
| `Secret` | `readonly` | *typeof* `Secret` | `Secret` class bound to this client's connection configuration. |
| `Template` | `readonly` | `CallableTemplate`\<*typeof* `TemplateBase`> | `Template` bound to this client's connection configuration. Both the builder (`client.Template()`) and the statics (`client.Template.build(...)`, `client.Template.exists(...)`, …) work like the top-level `Template`. |
| `Volume` | `readonly` | *typeof* `Volume` | `Volume` class bound to this client's connection configuration. |
## Type Aliases
### E2BClientOpts
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
type E2BClientOpts = Omit;
```
Connection options bound to an E2B client.
Same as ConnectionOpts without `signal`, which cancels a single
request and therefore can only be passed per call.
# Errors
Source: https://docs.e2b.dev/sdk-reference/js-sdk/v2.56.0/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`
***
### FileNotFoundError
Thrown when a file or directory is not found inside a sandbox.
#### Constructors
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
new FileNotFoundError(message: string): FileNotFoundError
```
###### Parameters
| Parameter | Type |
| - | - |
| `message` | `string` |
###### Returns
`FileNotFoundError`
#### Properties
| Property | Type | Description | Inherited from |
| - | - | - | - |
| `statusCode?` | `number` | HTTP status of the API response that produced this error, when there was one. | `NotFoundError`.`statusCode` |
***
### 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.
#### Deprecated
Run git with `sandbox.commands.run()` instead. The git module will be removed in the next major version.
#### 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.
#### Deprecated
Run git with `sandbox.commands.run()` instead. The git module will be removed in the next major version.
#### Constructors
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
new GitUpstreamError(message: string): GitUpstreamError
```
###### Parameters
| Parameter | Type |
| - | - |
| `message` | `string` |
###### Returns
`GitUpstreamError`
#### Properties
| Property | Type | Description | Inherited from |
| - | - | - | - |
| ~~`statusCode?`~~ | `number` | HTTP status of the API response that produced this error, when there was one. | `SandboxError`.`statusCode` |
***
### 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`
#### Properties
| Property | Type | Description | Inherited from |
| - | - | - | - |
| `statusCode?` | `number` | HTTP status of the API response that produced this error, when there was one. | `SandboxError`.`statusCode` |
***
### NotEnoughSpaceError
Thrown when there is not enough disk space.
#### Constructors
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
new NotEnoughSpaceError(message: string): NotEnoughSpaceError
```
###### Parameters
| Parameter | Type |
| - | - |
| `message` | `string` |
###### Returns
`NotEnoughSpaceError`
#### Properties
| Property | Type | Description | Inherited from |
| - | - | - | - |
| `statusCode?` | `number` | HTTP status of the API response that produced this error, when there was one. | `SandboxError`.`statusCode` |
***
### ~~NotFoundError~~
Thrown when a resource is not found.
#### Deprecated
Use FileNotFoundError or SandboxNotFoundError instead. This class will be removed in the next major version.
#### Extended by
* `FileNotFoundError`
* `SandboxNotFoundError`
#### Constructors
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
new NotFoundError(message: string): NotFoundError
```
###### Parameters
| Parameter | Type |
| - | - |
| `message` | `string` |
###### Returns
`NotFoundError`
#### Properties
| Property | Type | Description | Inherited from |
| - | - | - | - |
| ~~`statusCode?`~~ | `number` | HTTP status of the API response that produced this error, when there was one. | `SandboxError`.`statusCode` |
***
### 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`
#### Properties
| Property | Type | Description | Inherited from |
| - | - | - | - |
| `statusCode?` | `number` | HTTP status of the API response that produced this error, when there was one. | `SandboxError`.`statusCode` |
***
### 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): SandboxError
```
###### Parameters
| Parameter | Type |
| - | - |
| `message`? | `string` |
###### Returns
`SandboxError`
#### Properties
| Property | Type | Description |
| - | - | - |
| `statusCode?` | `number` | HTTP status of the API response that produced this error, when there was one. |
***
### SandboxNotFoundError
Thrown when a sandbox is not found (e.g. it doesn't exist or is no longer running).
#### Constructors
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
new SandboxNotFoundError(message: string): SandboxNotFoundError
```
###### Parameters
| Parameter | Type |
| - | - |
| `message` | `string` |
###### Returns
`SandboxNotFoundError`
#### Properties
| Property | Type | Description | Inherited from |
| - | - | - | - |
| `statusCode?` | `number` | HTTP status of the API response that produced this error, when there was one. | `NotFoundError`.`statusCode` |
***
### SandboxNotRunningError
Thrown when the sandbox is not running anymore: the proxy in front of it
answered a request (or the health probe run after a request failed at the
connection level) with the sandbox not found — it was killed, paused or
reached its timeout. Retrying the request will not help; create or resume a
sandbox.
The split from `SandboxUnreachableError` is only as good as the proxy's answer
and the health probe: a sandbox that is gone but could not be asked about is a
`SandboxUnreachableError`. `SandboxNotFoundError` is only raised by the control
plane API (e.g. `Sandbox.connect()` to a sandbox that does not exist).
Subclass of `TimeoutError`, which this case surfaced as before.
#### Constructors
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
new SandboxNotRunningError(message: string, options?: ErrorOptions): SandboxNotRunningError
```
###### Parameters
| Parameter | Type |
| - | - |
| `message` | `string` |
| `options`? | `ErrorOptions` |
###### Returns
`SandboxNotRunningError`
#### Properties
| Property | Type | Description | Inherited from |
| - | - | - | - |
| `statusCode?` | `number` | HTTP status of the API response that produced this error, when there was one. | `TimeoutError`.`statusCode` |
***
### SandboxUnreachableError
Thrown when the sandbox could not be reached while it is not confirmed to be
stopped: the proxy in front of the sandbox reports it running but envd's port
not open (e.g. the sandbox's network is down), the proxy or envd answered
with an unavailability the health probe could not resolve (e.g. the connection
to the sandbox was lost mid-stream while the probe found it running), or a
request failed at the connection level (the connection could not be
established or was dropped mid-request) and the follow-up health probe got
no answer from the sandbox either. A sandbox confirmed stopped (or paused) is
a `SandboxNotRunningError` — but only as far as the probe can tell: a sandbox
that is gone and could not be asked about surfaces here.
This usually means the sandbox's network is down, envd inside it is not up
(yet), or a network issue between the client and the sandbox. Check the
sandbox state with `Sandbox.getInfo()`.
Subclass of `TimeoutError`, which these cases surfaced as before.
#### Constructors
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
new SandboxUnreachableError(message: string, options?: ErrorOptions): SandboxUnreachableError
```
###### Parameters
| Parameter | Type |
| - | - |
| `message` | `string` |
| `options`? | `ErrorOptions` |
###### Returns
`SandboxUnreachableError`
#### Properties
| Property | Type | Description | Inherited from |
| - | - | - | - |
| `statusCode?` | `number` | HTTP status of the API response that produced this error, when there was one. | `TimeoutError`.`statusCode` |
***
### SecretError
Base class for all secret errors.
Thrown when general secret errors occur.
#### Extended by
* `SecretNotFoundError`
#### Constructors
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
new SecretError(message: string): SecretError
```
###### Parameters
| Parameter | Type |
| - | - |
| `message` | `string` |
###### Returns
`SecretError`
***
### SecretNotFoundError
Thrown when a secret is not found.
#### Constructors
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
new SecretNotFoundError(message: string): SecretNotFoundError
```
###### Parameters
| Parameter | Type |
| - | - |
| `message` | `string` |
###### Returns
`SecretNotFoundError`
***
### ServiceBusyError
Thrown when the API refused the operation because the service is
temporarily busy (HTTP 503): no capacity to place a sandbox right now, or
the node running the sandbox declined a request it cannot serve yet.
Nothing was changed by the refused call: for example a refused pause
leaves the sandbox running with its state intact, so the same call can be
retried after a short wait.
Like AuthenticationError and unlike the other API errors, this is
not a SandboxError: it is raised for every 503 whatever the
operation, so catch it explicitly.
#### Constructors
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
new ServiceBusyError(message: string): ServiceBusyError
```
###### Parameters
| Parameter | Type |
| - | - |
| `message` | `string` |
###### Returns
`ServiceBusyError`
#### Properties
| Property | Modifier | Type | Default value | Description |
| - | - | - | - | - |
| `statusCode` | `readonly` | `503` | `503` | HTTP status of the API response: always 503. |
***
### 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`
#### Properties
| Property | Type | Description | Inherited from |
| - | - | - | - |
| `statusCode?` | `number` | HTTP status of the API response that produced this error, when there was one. | `SandboxError`.`statusCode` |
***
### 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.
#### Extended by
* `SandboxNotRunningError`
* `SandboxUnreachableError`
#### Constructors
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
new TimeoutError(message: string): TimeoutError
```
###### Parameters
| Parameter | Type |
| - | - |
| `message` | `string` |
###### Returns
`TimeoutError`
#### Properties
| Property | Type | Description | Inherited from |
| - | - | - | - |
| `statusCode?` | `number` | HTTP status of the API response that produced this error, when there was one. | `SandboxError`.`statusCode` |
***
### VolumeError
Base class for all volume errors.
Thrown when general volume errors occur.
#### Extended by
* `VolumeNotFoundError`
* `VolumePathNotFoundError`
#### Constructors
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
new VolumeError(message: string): VolumeError
```
###### Parameters
| Parameter | Type |
| - | - |
| `message` | `string` |
###### Returns
`VolumeError`
***
### VolumeNotFoundError
Thrown when a volume is not found.
#### Constructors
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
new VolumeNotFoundError(message: string): VolumeNotFoundError
```
###### Parameters
| Parameter | Type |
| - | - |
| `message` | `string` |
###### Returns
`VolumeNotFoundError`
***
### VolumePathNotFoundError
Thrown when a file or directory is not found inside a volume.
#### Constructors
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
new VolumePathNotFoundError(message: string): VolumePathNotFoundError
```
###### Parameters
| Parameter | Type |
| - | - |
| `message` | `string` |
###### Returns
`VolumePathNotFoundError`
## Functions
### formatSandboxUnavailableError()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
function formatSandboxUnavailableError(message: string): Error
```
Maps a 502/Unavailable answered by the proxy in front of envd: the sandbox was not
found (killed or reached its timeout) is a `SandboxNotRunningError`; the sandbox
running but envd's port not open (e.g. its network is down) a
`SandboxUnreachableError`; any other message (e.g. envd ending a stream because
the connection to the sandbox was lost) a `SandboxUnreachableError` too, with the
sandbox state reported as unknown.
#### Parameters
| Parameter | Type |
| - | - |
| `message` | `string` |
#### Returns
`Error`
***
### isSandboxNotFoundMessage()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
function isSandboxNotFoundMessage(message: string): boolean
```
#### Parameters
| Parameter | Type |
| - | - |
| `message` | `string` |
#### Returns
`boolean`
***
### isSandboxPortNotOpenMessage()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
function isSandboxPortNotOpenMessage(message: string): boolean
```
#### Parameters
| Parameter | Type |
| - | - |
| `message` | `string` |
#### Returns
`boolean`
# Sandbox
Source: https://docs.e2b.dev/sdk-reference/js-sdk/v2.56.0/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 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 **Deprecated** Run git with `sandbox.commands.run()` instead. The git module will be removed in the next major version. |
| `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?: SandboxPauseOpts): Promise
```
###### Parameters
| Parameter | Type |
| - | - |
| `opts`? | `SandboxPauseOpts` |
###### 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?: CreateSnapshotOpts): 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`? | `CreateSnapshotOpts` | snapshot creation options including optional name, `mode` (`'filesystem'` takes a filesystem-only snapshot; sandboxes created from it cold-boot instead of restoring memory), and connection options. |
###### Returns
`Promise`\<`SnapshotInfo`>
snapshot information including the snapshot ID.
###### Throws
SandboxError with `statusCode` 400 when `mode: 'filesystem'`
is requested but the feature is not enabled for the team
(`snapshot_filesystem_only_disabled`), and with `statusCode` 409 when the
sandbox's node runs an orchestrator that predates the option
(`snapshot_filesystem_only_unsupported_node`). A full memory snapshot
still works in both cases; for 409, pause and resume the sandbox and retry.
###### 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({ name: 'my-snapshot' })
// Create a new sandbox from the snapshot
const newSandbox = await Sandbox.create(snapshot.snapshotId)
// Filesystem-only snapshot: sandboxes created from it start fresh from disk
const fsSnapshot = await sandbox.createSnapshot({ mode: 'filesystem' })
```
### 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.
### fork()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
fork(opts?: SandboxForkOpts): Promise<(Error | Sandbox)[]>
```
Fork the sandbox.
The sandbox is checkpointed in place (briefly paused, snapshotted with its
full memory state, and resumed — its ID and expiration stay untouched) and
`count` new sandboxes are created from that snapshot. All forks boot from
the same snapshot, so the snapshot is captured once regardless of count.
Each fork succeeds or fails independently — the returned array contains
one entry per requested fork, either a running Sandbox instance or
an `Error` describing why that fork failed to start
(`Promise.allSettled`-style). Per-fork error codes map to the same error
classes as other API errors (e.g. 429 to `RateLimitError`).
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `opts`? | `SandboxForkOpts` | fork options — `count`, `timeoutMs` and connection options. |
###### Returns
`Promise`\<(`Error` | `Sandbox`)\[]>
array with one entry per requested fork — a sandbox instance or an error.
###### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
const sandbox = await Sandbox.create()
const [fork1, fork2] = await sandbox.fork({ count: 2 })
if (fork1 instanceof Sandbox) {
await fork1.commands.run('echo "hello from fork"')
}
```
### 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.run('python3 -m http.server 3000', { background: true })
// 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"` \| `"signal"`> | 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"` \| `"signal"`> |
###### 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"` \| `"signal"`> | connection options. |
###### Returns
`Promise`\<`boolean`>
`true` if the sandbox was killed, `false` if the sandbox was not found.
### 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?: SandboxPauseOpts): Promise
```
Pause a sandbox by its ID.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `opts`? | `SandboxPauseOpts` | connection options, plus `mode` to control the snapshot kind. With `mode: 'filesystem'`, the in-memory state is dropped and only the filesystem is persisted; resuming such a sandbox cold-boots (reboots) it from disk, losing running processes and open connections. Defaults to `'full'` (memory and filesystem). |
###### Returns
`Promise`\<`boolean`>
`true` if the sandbox got paused, `false` if the sandbox was already paused.
###### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
const sandbox = await Sandbox.create()
await sandbox.pause()
// filesystem-only snapshot (resume reboots the sandbox)
await sandbox.pause({ mode: 'filesystem' })
```
### 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"` \| `"signal"`> | connection options. |
###### Returns
`Promise`\<`void`>
### updateNetwork()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
updateNetwork(network: SandboxNetworkUpdate, opts?: Pick): Promise
```
Update the network configuration of the sandbox.
Replaces the current egress configuration atomically — fields that are
omitted are cleared on the server.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `network` | `SandboxNetworkUpdate` | new network configuration. |
| `opts`? | `Pick`\<`SandboxOpts`, `"requestTimeoutMs"` \| `"signal"`> | 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` | upload url options. |
###### Returns
`Promise`\<`string`>
URL for uploading file.
### ~~betaPause()~~
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
static betaPause(sandboxId: string, opts?: SandboxPauseOpts): Promise
```
###### Parameters
| Parameter | Type |
| - | - |
| `sandboxId` | `string` |
| `opts`? | `SandboxPauseOpts` |
###### 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()
###### Call Signature
```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
###### Call Signature
```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?: CreateSnapshotOpts): 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`? | `CreateSnapshotOpts` | snapshot creation options including optional name, `mode` (`'filesystem'` takes a filesystem-only snapshot), and connection options. |
###### Returns
`Promise`\<`SnapshotInfo`>
snapshot information including the snapshot name that can be used with Sandbox.create().
###### Throws
SandboxError with `statusCode` 400 when `mode: 'filesystem'`
is requested but the feature is not enabled for the team
(`snapshot_filesystem_only_disabled`), and with `statusCode` 409 when the
sandbox's node runs an orchestrator that predates the option
(`snapshot_filesystem_only_unsupported_node`). A full memory snapshot
still works in both cases; for 409, pause and resume the sandbox and retry.
### 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.
### fork()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
static fork(
this: S,
sandboxId: string,
opts?: SandboxForkOpts): Promise<(Error | InstanceType)[]>
```
Fork a running sandbox specified by sandbox ID.
The sandbox is checkpointed in place (briefly paused, snapshotted with its
full memory state, and resumed — its ID and expiration stay untouched) and
`count` new sandboxes are created from that snapshot. All forks boot from
the same snapshot, so the snapshot is captured once regardless of count.
Each fork succeeds or fails independently — the returned array contains
one entry per requested fork, either a running Sandbox instance or
an `Error` describing why that fork failed to start
(`Promise.allSettled`-style). Per-fork error codes map to the same error
classes as other API errors (e.g. 429 to `RateLimitError`).
###### Type Parameters
| Type Parameter |
| - |
| `S` *extends* *typeof* `Sandbox` |
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `this` | `S` | - |
| `sandboxId` | `string` | sandbox ID. |
| `opts`? | `SandboxForkOpts` | fork options — `count`, `timeoutMs` and connection options. |
###### Returns
`Promise`\<(`Error` | `InstanceType`\<`S`>)\[]>
array with one entry per requested fork — a sandbox instance or an error.
###### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
const sandbox = await Sandbox.create()
const [fork1, fork2] = await Sandbox.fork(sandbox.sandboxId, { count: 2 })
if (fork1 instanceof Sandbox) {
await fork1.commands.run('echo "hello from fork"')
}
```
### ~~getFullInfo()~~
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
static getFullInfo(sandboxId: string, opts?: SandboxApiOpts): Promise
```
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `sandboxId` | `string` | sandbox ID. |
| `opts`? | `SandboxApiOpts` | connection options. |
###### Returns
`Promise`\<`SandboxInfo`>
sandbox information.
###### Deprecated
Use Sandbox.getInfo instead.
### getInfo()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
static getInfo(sandboxId: string, opts?: SandboxApiOpts): Promise
```
Get sandbox information like sandbox ID, template, metadata, started at/end at date.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `sandboxId` | `string` | sandbox ID. |
| `opts`? | `SandboxApiOpts` | connection options. |
###### Returns
`Promise`\<`SandboxInfo`>
sandbox information.
### getMetrics()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
static getMetrics(sandboxId: string, opts?: SandboxMetricsOpts): Promise
```
Get the metrics of the sandbox.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `sandboxId` | `string` | sandbox ID. |
| `opts`? | `SandboxMetricsOpts` | sandbox metrics options. |
###### Returns
`Promise`\<`SandboxMetrics`\[]>
List of sandbox metrics containing CPU, memory and disk usage information.
### kill()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
static kill(sandboxId: string, opts?: SandboxApiOpts): Promise
```
Kill the sandbox specified by sandbox ID.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `sandboxId` | `string` | sandbox ID. |
| `opts`? | `SandboxApiOpts` | connection options. |
###### Returns
`Promise`\<`boolean`>
`true` if the sandbox was found and killed, `false` otherwise.
### list()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
static list(opts?: SandboxListOpts): SandboxPaginator
```
List sandboxes.
By default (no `query.state` set in `opts`), returns sandboxes in both
`running` and `paused` states. To filter by state, pass
`opts.query.state = [...]`.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `opts`? | `SandboxListOpts` | connection options, plus optional `query` to filter by metadata / state / start time / template, `order` to sort by start time across the whole result set (not within a page), and `limit` / `nextToken` for pagination. |
###### Returns
`SandboxPaginator`
a SandboxPaginator that yields pages of sandboxes
(running and paused by default). Iterate pages via
`await paginator.nextItems()` while `paginator.hasNext` is `true`.
### listSnapshots()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
static listSnapshots(opts?: SnapshotListOpts): SnapshotPaginator
```
List all snapshots.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `opts`? | `SnapshotListOpts` | list options including filters and pagination. |
###### Returns
`SnapshotPaginator`
paginator for listing snapshots.
### pause()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
static pause(sandboxId: string, opts?: SandboxPauseOpts): Promise
```
Pause the sandbox specified by sandbox ID.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `sandboxId` | `string` | sandbox ID. |
| `opts`? | `SandboxPauseOpts` | pause options, including `mode` and connection options. |
###### Returns
`Promise`\<`boolean`>
`true` if the sandbox got paused, `false` if the sandbox was already paused.
### setTimeout()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
static setTimeout(
sandboxId: string,
timeoutMs: number,
opts?: SandboxApiOpts): Promise
```
Set the timeout of the specified sandbox.
After the timeout expires the sandbox will be automatically killed.
This method can extend or reduce the sandbox timeout set when creating the sandbox or from the last call to Sandbox.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 |
| - | - | - |
| `sandboxId` | `string` | sandbox ID. |
| `timeoutMs` | `number` | timeout in **milliseconds**. |
| `opts`? | `SandboxApiOpts` | connection options. |
###### Returns
`Promise`\<`void`>
### updateNetwork()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
static updateNetwork(
sandboxId: string,
network: SandboxNetworkUpdate,
opts?: SandboxApiOpts): Promise
```
Update the network configuration of a running sandbox.
Replaces the current egress configuration atomically — fields that are
omitted are cleared on the server.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `sandboxId` | `string` | sandbox ID. |
| `network` | `SandboxNetworkUpdate` | new network configuration. |
| `opts`? | `SandboxApiOpts` | connection options. |
###### Returns
`Promise`\<`void`>
## Interfaces
### SandboxUrlOpts
Options for sandbox upload/download URL generation.
#### Properties
### user?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional user: string;
```
User that will be used to access the file.
### useSignatureExpiration?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional useSignatureExpiration: number;
```
Use signature expiration for the URL.
Optional parameter to set the expiration time for the signature in seconds.
# Sandbox commands
Source: https://docs.e2b.dev/sdk-reference/js-sdk/v2.56.0/sandbox-commands
### Commands
Module for starting and interacting with commands in the sandbox.
#### Constructors
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
new Commands(
transport: Transport,
envdApi: EnvdApiClient,
connectionConfig: ConnectionConfig): Commands
```
###### Parameters
| Parameter | Type |
| - | - |
| `transport` | `Transport` |
| `envdApi` | `EnvdApiClient` |
| `connectionConfig` | `ConnectionConfig` |
###### Returns
`Commands`
#### Methods
### closeStdin()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
closeStdin(pid: number, opts?: CommandRequestOpts): Promise
```
Close command stdin.
This signals EOF to the command. The command must have been started with `stdin: true`.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `pid` | `number` | process ID of the command. You can get the list of running commands using Commands.list. |
| `opts`? | `CommandRequestOpts` | connection options. |
###### Returns
`Promise`\<`void`>
### connect()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
connect(pid: number, opts?: CommandConnectOpts): Promise
```
Connect to a running command.
You can use CommandHandle.wait to wait for the command to finish and get execution results.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `pid` | `number` | process ID of the command to connect to. You can get the list of running commands using Commands.list. |
| `opts`? | `CommandConnectOpts` | connection options. |
###### Returns
`Promise`\<`CommandHandle`>
`CommandHandle` handle to interact with the running command.
### kill()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
kill(pid: number, opts?: CommandRequestOpts): Promise
```
Kill a running command specified by its process ID.
It uses `SIGKILL` signal to kill the command.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `pid` | `number` | process ID of the command. You can get the list of running commands using Commands.list. |
| `opts`? | `CommandRequestOpts` | connection options. |
###### Returns
`Promise`\<`boolean`>
`true` if the command was killed, `false` if the command was not found.
### list()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
list(opts?: CommandRequestOpts): Promise
```
List all running commands and PTY sessions.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `opts`? | `CommandRequestOpts` | connection options. |
###### Returns
`Promise`\<`ProcessInfo`\[]>
list of running commands and PTY sessions.
### run()
###### Call Signature
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
run(cmd: string, opts?: CommandStartOpts & object): Promise
```
Start a new command and wait until it finishes executing.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `cmd` | `string` | command to execute. |
| `opts`? | `CommandStartOpts` & `object` | options for starting the command. |
###### Returns
`Promise`\<`CommandResult`>
`CommandResult` result of the command execution.
###### Call Signature
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
run(cmd: string, opts: CommandStartOpts & object): Promise
```
Start a new command in the background.
You can use CommandHandle.wait to wait for the command to finish and get its result.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `cmd` | `string` | command to execute. |
| `opts` | `CommandStartOpts` & `object` | options for starting the command |
###### Returns
`Promise`\<`CommandHandle`>
`CommandHandle` handle to interact with the running command.
###### Call Signature
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
run(cmd: string, opts?: CommandStartOpts & object): Promise
```
Start a new command.
###### Parameters
| Parameter | Type | Description | |
| - | - | - | - |
| `cmd` | `string` | command to execute. | |
| `opts`? | `CommandStartOpts` & `object` | options for starting the command. - `opts.background: true` - runs in background, returns `CommandHandle` - \`opts.background: false | undefined`- waits for completion, returns`CommandResult\` |
###### Returns
`Promise`\<`CommandResult` | `CommandHandle`>
Either a `CommandHandle` or a `CommandResult` (depending on `opts.background`).
### sendStdin()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
sendStdin(
pid: number,
data: string | Uint8Array,
opts?: CommandRequestOpts): Promise
```
Send data to command stdin.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `pid` | `number` | process ID of the command. You can get the list of running commands using Commands.list. |
| `data` | `string` \| `Uint8Array`\<`ArrayBufferLike`> | data to send to the command. |
| `opts`? | `CommandRequestOpts` | connection options. |
###### Returns
`Promise`\<`void`>
***
### Pty
Module for interacting with PTYs (pseudo-terminals) in the sandbox.
#### Constructors
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
new Pty(
transport: Transport,
envdApi: EnvdApiClient,
connectionConfig: ConnectionConfig): Pty
```
###### Parameters
| Parameter | Type |
| - | - |
| `transport` | `Transport` |
| `envdApi` | `EnvdApiClient` |
| `connectionConfig` | `ConnectionConfig` |
###### Returns
`Pty`
#### Methods
### connect()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
connect(pid: number, opts?: PtyConnectOpts): Promise
```
Connect to a running PTY.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `pid` | `number` | process ID of the PTY to connect to. You can get the list of running PTYs using Commands.list. |
| `opts`? | `PtyConnectOpts` | connection options. |
###### Returns
`Promise`\<`CommandHandle`>
handle to interact with the PTY.
### create()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
create(opts: PtyCreateOpts): Promise
```
Create a new PTY (pseudo-terminal).
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `opts` | `PtyCreateOpts` | options for creating the PTY. |
###### Returns
`Promise`\<`CommandHandle`>
handle to interact with the PTY.
### kill()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
kill(pid: number, opts?: Pick): Promise
```
Kill a running PTY specified by process ID.
It uses `SIGKILL` signal to kill the PTY.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `pid` | `number` | process ID of the PTY. |
| `opts`? | `Pick`\<`ConnectionOpts`, `"requestTimeoutMs"` \| `"signal"`> | connection options. |
###### Returns
`Promise`\<`boolean`>
`true` if the PTY was killed, `false` if the PTY was not found.
### resize()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
resize(
pid: number,
size: object,
opts?: Pick): Promise
```
Resize PTY.
Call this when the terminal window is resized and the number of columns and rows has changed.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `pid` | `number` | process ID of the PTY. |
| `size` | \{ `cols`: `number`; `rows`: `number`; } | new size of the PTY. |
| `size.cols` | `number` | - |
| `size.rows`? | `number` | - |
| `opts`? | `Pick`\<`ConnectionOpts`, `"requestTimeoutMs"` \| `"signal"`> | connection options. |
###### Returns
`Promise`\<`void`>
### sendInput()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
sendInput(
pid: number,
data: Uint8Array,
opts?: Pick): Promise
```
Send input to a PTY.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `pid` | `number` | process ID of the PTY. |
| `data` | `Uint8Array` | input data to send to the PTY. |
| `opts`? | `Pick`\<`ConnectionOpts`, `"requestTimeoutMs"` \| `"signal"`> | connection options. |
###### Returns
`Promise`\<`void`>
## Interfaces
### CommandRequestOpts
Options for sending a command request.
#### Extended by
* `CommandStartOpts`
#### Properties
### requestTimeoutMs?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional requestTimeoutMs: number;
```
Timeout for requests to the API in **milliseconds**.
###### Default
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
60_000 // 60 seconds
```
### signal?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional signal: AbortSignal;
```
An optional `AbortSignal` that can be used to cancel the in-flight request.
When the signal is aborted, the underlying `fetch` is aborted and the
returned promise rejects with an `AbortError`.
***
### CommandStartOpts
Options for starting a new command.
#### Properties
### background?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional background: boolean;
```
If true, starts command in the background and the method returns immediately.
You can use CommandHandle.wait to wait for the command to finish.
### cwd?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional cwd: string;
```
Working directory for the command.
###### Default
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
// home directory of the user used to start the command
```
### envs?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional envs: Record;
```
Environment variables used for the command.
This overrides the default environment variables from `Sandbox` constructor.
###### Default
`{}`
### onStderr()?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional onStderr: (data: string) => void | Promise;
```
Callback for command stderr output.
###### Parameters
| Parameter | Type |
| - | - |
| `data` | `string` |
###### Returns
`void` | `Promise`\<`void`>
### onStdout()?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional onStdout: (data: string) => void | Promise;
```
Callback for command stdout output.
###### Parameters
| Parameter | Type |
| - | - |
| `data` | `string` |
###### Returns
`void` | `Promise`\<`void`>
### requestTimeoutMs?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional requestTimeoutMs: number;
```
Timeout for requests to the API in **milliseconds**.
###### Default
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
60_000 // 60 seconds
```
### signal?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional signal: AbortSignal;
```
An optional `AbortSignal` that can be used to cancel the in-flight request.
When the signal is aborted, the underlying `fetch` is aborted and the
returned promise rejects with an `AbortError`.
### stdin?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional stdin: boolean;
```
If true, command stdin is kept open and you can send data to it using Commands.sendStdin or CommandHandle.sendStdin.
###### Default
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
false
```
### timeoutMs?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional timeoutMs: number;
```
Timeout for the command in **milliseconds**.
###### Default
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
60_000 // 60 seconds
```
### user?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional user: string;
```
User to run the command as.
###### Default
`default Sandbox user (as specified in the template)`
***
### ProcessInfo
Information about a command, PTY session or start command running in the sandbox as process.
#### Properties
### args
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
args: string[];
```
Command arguments.
### cmd
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
cmd: string;
```
Command that was executed.
### cwd?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional cwd: string;
```
Executed command working directory.
### envs
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
envs: Record;
```
Environment variables used for the command.
### pid
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
pid: number;
```
Process ID.
### tag?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional tag: string;
```
Custom tag used for identifying special commands like start command in the custom template.
## Type Aliases
### CommandConnectOpts
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
type CommandConnectOpts = Pick & CommandRequestOpts;
```
Options for connecting to a command.
# Sandbox filesystem
Source: https://docs.e2b.dev/sdk-reference/js-sdk/v2.56.0/sandbox-filesystem
### FileType
Sandbox filesystem object type.
#### Enumeration Members
| Enumeration Member | Value | Description |
| - | - | - |
| `DIR` | `"dir"` | Filesystem object is a directory. |
| `FILE` | `"file"` | Filesystem object is a file. |
| `SYMLINK` | `"symlink"` | Filesystem object is a symlink. |
## Classes
### Filesystem
Module for interacting with the sandbox filesystem.
#### Constructors
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
new Filesystem(
transport: Transport,
envdApi: EnvdApiClient,
connectionConfig: ConnectionConfig): Filesystem
```
###### Parameters
| Parameter | Type |
| - | - |
| `transport` | `Transport` |
| `envdApi` | `EnvdApiClient` |
| `connectionConfig` | `ConnectionConfig` |
###### Returns
`Filesystem`
#### Methods
### exists()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
exists(path: string, opts?: FilesystemRequestOpts): Promise
```
Check if a file or a directory exists.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `path` | `string` | path to a file or a directory |
| `opts`? | `FilesystemRequestOpts` | connection options. |
###### Returns
`Promise`\<`boolean`>
`true` if the file or directory exists, `false` otherwise
### getInfo()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
getInfo(path: string, opts?: FilesystemRequestOpts): Promise
```
Get information about a file or directory.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `path` | `string` | path to a file or directory. |
| `opts`? | `FilesystemRequestOpts` | connection options. |
###### Returns
`Promise`\<`EntryInfo`>
information about the file or directory like name, type, and path.
### list()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
list(path: string, opts?: FilesystemListOpts): Promise
```
List entries in a directory.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `path` | `string` | path to the directory. |
| `opts`? | `FilesystemListOpts` | connection options. |
###### Returns
`Promise`\<`EntryInfo`\[]>
list of entries in the sandbox filesystem directory.
### makeDir()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
makeDir(path: string, opts?: FilesystemRequestOpts): Promise
```
Create a new directory and all directories along the way if needed on the specified path.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `path` | `string` | path to a new directory. For example '/dirA/dirB' when creating 'dirB'. |
| `opts`? | `FilesystemRequestOpts` | connection options. |
###### Returns
`Promise`\<`boolean`>
`true` if the directory was created, `false` if it already exists.
### read()
###### Call Signature
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
read(path: string, opts?: FilesystemReadOpts & object): Promise
```
Read file content as a `string`.
You can pass `text`, `bytes`, `blob`, or `stream` to `opts.format` to change the return type.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `path` | `string` | path to the file. |
| `opts`? | `FilesystemReadOpts` & `object` | connection options. |
###### Returns
`Promise`\<`string`>
file content as string
###### Call Signature
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
read(path: string, opts?: FilesystemReadOpts & object): Promise>
```
Read file content as a `Uint8Array`.
You can pass `text`, `bytes`, `blob`, or `stream` to `opts.format` to change the return type.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `path` | `string` | path to the file. |
| `opts`? | `FilesystemReadOpts` & `object` | connection options. |
###### Returns
`Promise`\<`Uint8Array`\<`ArrayBufferLike`>>
file content as `Uint8Array`
###### Call Signature
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
read(path: string, opts?: FilesystemReadOpts & object): Promise
```
Read file content as a `Blob`.
You can pass `text`, `bytes`, `blob`, or `stream` to `opts.format` to change the return type.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `path` | `string` | path to the file. |
| `opts`? | `FilesystemReadOpts` & `object` | connection options. |
###### Returns
`Promise`\<`Blob`>
file content as `Blob`
###### Call Signature
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
read(path: string, opts?: FilesystemReadOpts & object): Promise>>
```
Read file content as a `ReadableStream`.
You can pass `text`, `bytes`, `blob`, or `stream` to `opts.format` to change the return type.
The request timeout bounds only the initial handshake. The returned stream
holds a pooled connection until it is fully read, cancelled, errors, or the
idle timeout (`opts.streamIdleTimeoutMs`) fires—so consume it to the end or
cancel it (`opts.signal`).
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `path` | `string` | path to the file. |
| `opts`? | `FilesystemReadOpts` & `object` | connection options. |
###### Returns
`Promise`\<`ReadableStream`\<`Uint8Array`\<`ArrayBufferLike`>>>
file content as `ReadableStream`
### remove()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
remove(path: string, opts?: FilesystemRequestOpts): Promise
```
Remove a file or directory.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `path` | `string` | path to a file or directory. |
| `opts`? | `FilesystemRequestOpts` | connection options. |
###### Returns
`Promise`\<`void`>
### rename()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
rename(
oldPath: string,
newPath: string,
opts?: FilesystemRequestOpts): Promise
```
Rename a file or directory.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `oldPath` | `string` | path to the file or directory to rename. |
| `newPath` | `string` | new path for the file or directory. |
| `opts`? | `FilesystemRequestOpts` | connection options. |
###### Returns
`Promise`\<`EntryInfo`>
information about renamed file or directory.
### watchDir()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
watchDir(
path: string,
onEvent: (event: FilesystemEvent) => void | Promise,
opts?: WatchOpts & object): Promise
```
Start watching a directory for filesystem events.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `path` | `string` | path to directory to watch. |
| `onEvent` | (`event`: `FilesystemEvent`) => `void` \| `Promise`\<`void`> | callback to call when an event in the directory occurs. |
| `opts`? | `WatchOpts` & `object` | connection options. |
###### Returns
`Promise`\<`WatchHandle`>
`WatchHandle` object for stopping watching directory.
### write()
###### Call Signature
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
write(
path: string,
data: string | Blob | ReadableStream | ArrayBuffer,
opts?: FilesystemWriteOpts): Promise
```
Write content to a file.
Writing to a file that doesn't exist creates the file.
Writing to a file that already exists overwrites the file.
Writing to a file at path that doesn't exist creates the necessary directories.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `path` | `string` | path to file. |
| `data` | `string` \| `Blob` \| `ReadableStream`\<`any`> \| `ArrayBuffer` | data to write to the file. Data can be a string, `ArrayBuffer`, `Blob`, or `ReadableStream`. |
| `opts`? | `FilesystemWriteOpts` | connection options. |
###### Returns
`Promise`\<`WriteInfo`>
information about the written file
###### Call Signature
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
write(files: WriteEntry[], opts?: FilesystemWriteOpts): Promise
```
Write content to a file.
Writing to a file that doesn't exist creates the file.
Writing to a file that already exists overwrites the file.
Writing to a file at path that doesn't exist creates the necessary directories.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `files` | `WriteEntry`\[] | - |
| `opts`? | `FilesystemWriteOpts` | connection options. |
###### Returns
`Promise`\<`WriteInfo`\[]>
information about the written file
### writeFiles()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
writeFiles(files: WriteEntry[], opts?: FilesystemWriteOpts): Promise
```
Write multiple files.
Writing to a file that doesn't exist creates the file.
Writing to a file that already exists overwrites the file.
Writing to a file at path that doesn't exist creates the necessary directories.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `files` | `WriteEntry`\[] | list of files to write as `WriteEntry` objects, each containing `path` and `data`. |
| `opts`? | `FilesystemWriteOpts` | connection options. |
###### Returns
`Promise`\<`WriteInfo`\[]>
information about the written files
## Interfaces
### EntryInfo
Sandbox filesystem object information.
#### Properties
### group
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
group: string;
```
Group owner of the filesystem object.
### metadata?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional metadata: Record;
```
User-defined metadata stored on the file as `user.e2b.*` extended
attributes. On writes this reflects the metadata supplied on upload; on
reads (`getInfo`, `list`, `rename`) it reflects any `user.e2b.*` xattr on
the file, including ones set out-of-band. `undefined` when none is set.
### mode
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
mode: number;
```
File mode and permission bits.
### modifiedTime?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional modifiedTime: Date;
```
Last modification time of the filesystem object.
### name
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
name: string;
```
Name of the filesystem object.
### owner
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
owner: string;
```
Owner of the filesystem object.
### path
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
path: string;
```
Path to the filesystem object.
### permissions
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
permissions: string;
```
String representation of file permissions (e.g. 'rwxr-xr-x').
### size
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
size: number;
```
Size of the filesystem object in bytes.
### symlinkTarget?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional symlinkTarget: string;
```
If the filesystem object is a symlink, this is the target of the symlink.
### type?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional type: FileType;
```
Type of the filesystem object.
***
### FilesystemListOpts
Options for the sandbox filesystem operations.
#### Properties
### depth?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional depth: number;
```
Depth of the directory to list.
### requestTimeoutMs?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional requestTimeoutMs: number;
```
Timeout for requests to the API in **milliseconds**.
###### Default
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
60_000 // 60 seconds
```
### signal?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional signal: AbortSignal;
```
An optional `AbortSignal` that can be used to cancel the in-flight request.
When the signal is aborted, the underlying `fetch` is aborted and the
returned promise rejects with an `AbortError`.
### user?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional user: string;
```
User to use for the operation in the sandbox.
This affects the resolution of relative paths and ownership of the created filesystem objects.
***
### FilesystemReadOpts
Options for reading files from the sandbox filesystem.
#### Properties
### gzip?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional gzip: boolean;
```
When true, the download will request gzip-encoded responses.
### requestTimeoutMs?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional requestTimeoutMs: number;
```
Timeout for requests to the API in **milliseconds**.
###### Default
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
60_000 // 60 seconds
```
### signal?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional signal: AbortSignal;
```
An optional `AbortSignal` that can be used to cancel the in-flight request.
When the signal is aborted, the underlying `fetch` is aborted and the
returned promise rejects with an `AbortError`.
### streamIdleTimeoutMs?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional streamIdleTimeoutMs: number;
```
Idle timeout for a streamed read (`format: 'stream'`) in **milliseconds**:
abort if no chunk arrives from the server within this window *while
reading*. It bounds only the wire — a slow or paused consumer never trips
it (a consumer that holds the stream but stops reading is reclaimed
server-side). Defaults to the request timeout (60s); pass `0` to disable.
### user?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional user: string;
```
User to use for the operation in the sandbox.
This affects the resolution of relative paths and ownership of the created filesystem objects.
***
### FilesystemRequestOpts
Options for the sandbox filesystem operations.
#### Extended by
* `FilesystemWriteOpts`
* `FilesystemReadOpts`
* `FilesystemListOpts`
* `WatchOpts`
#### Properties
### requestTimeoutMs?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional requestTimeoutMs: number;
```
Timeout for requests to the API in **milliseconds**.
###### Default
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
60_000 // 60 seconds
```
### signal?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional signal: AbortSignal;
```
An optional `AbortSignal` that can be used to cancel the in-flight request.
When the signal is aborted, the underlying `fetch` is aborted and the
returned promise rejects with an `AbortError`.
### user?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional user: string;
```
User to use for the operation in the sandbox.
This affects the resolution of relative paths and ownership of the created filesystem objects.
***
### FilesystemWriteOpts
Options for writing files to the sandbox filesystem.
#### Properties
### gzip?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional gzip: boolean;
```
When true, the upload will be gzip-compressed. Implies the
`application/octet-stream` upload.
Requires envd 0.5.7 or later — when not supported by the sandbox's envd
version, the upload falls back to uncompressed `multipart/form-data`.
### metadata?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional metadata: Record;
```
User-defined metadata to persist on the uploaded file(s) as extended
attributes. Keys are lowercased by the sandbox, so they may differ in case
when read back. Invalid keys or values throw an `InvalidArgumentError`.
The same metadata is applied to every file in a multi-file upload.
Requires envd 0.6.2 or later.
### requestTimeoutMs?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional requestTimeoutMs: number;
```
Timeout for requests to the API in **milliseconds**.
###### Default
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
60_000 // 60 seconds
```
### signal?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional signal: AbortSignal;
```
An optional `AbortSignal` that can be used to cancel the in-flight request.
When the signal is aborted, the underlying `fetch` is aborted and the
returned promise rejects with an `AbortError`.
### useOctetStream?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional useOctetStream: boolean;
```
When true, the upload uses `application/octet-stream` instead of `multipart/form-data`.
Outside the browser, `ReadableStream` data is then streamed to the sandbox
instead of being buffered in memory.
Defaults to `undefined`, which uses octet-stream when any entry is a
`ReadableStream` (so streamed uploads aren't buffered) and
`multipart/form-data` otherwise; browsers always use `multipart/form-data`
since they can't stream request bodies. Requires envd 0.5.7 or later — when
not supported by the sandbox's envd version, the upload falls back to
`multipart/form-data`.
### user?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional user: string;
```
User to use for the operation in the sandbox.
This affects the resolution of relative paths and ownership of the created filesystem objects.
***
### WatchOpts
Options for watching a directory.
#### Properties
### allowNetworkMounts?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional allowNetworkMounts: boolean;
```
Allow watching paths on network filesystem mounts (NFS, CIFS, SMB, FUSE),
which are rejected by default. Events on network mounts may be unreliable
or not delivered at all.
Requires envd 0.6.4 or later. Watching with this option against an older sandbox
throws a `TemplateError`.
### includeEntry?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional includeEntry: boolean;
```
Include the EntryInfo of the affected entry in each FilesystemEvent.
The entry is populated best-effort and may be `undefined` for events where the
entry no longer exists at the path (e.g. remove or rename-away events).
Requires envd 0.6.3 or later. Watching with this option against an older sandbox
throws a `TemplateError`.
### onExit()?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional onExit: (err?: Error) => void | Promise;
```
Callback to call when the watch operation stops.
`err` is `undefined` after WatchHandle.stop or a clean stream end,
and is set only when the watch fails.
###### Parameters
| Parameter | Type |
| - | - |
| `err`? | `Error` |
###### Returns
`void` | `Promise`\<`void`>
### recursive?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional recursive: boolean;
```
Watch the directory recursively
### requestTimeoutMs?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional requestTimeoutMs: number;
```
Timeout for requests to the API in **milliseconds**.
###### Default
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
60_000 // 60 seconds
```
### signal?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional signal: AbortSignal;
```
An optional `AbortSignal` that can be used to cancel the in-flight request.
When the signal is aborted, the underlying `fetch` is aborted and the
returned promise rejects with an `AbortError`.
### timeoutMs?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional timeoutMs: number;
```
Timeout for the watch operation in **milliseconds**.
You can pass `0` to disable the timeout.
###### Default
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
60_000 // 60 seconds
```
### user?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional user: string;
```
User to use for the operation in the sandbox.
This affects the resolution of relative paths and ownership of the created filesystem objects.
***
### WriteInfo
Sandbox filesystem object information.
#### Extended by
* `EntryInfo`
#### Properties
### metadata?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional metadata: Record;
```
User-defined metadata stored on the file as `user.e2b.*` extended
attributes. On writes this reflects the metadata supplied on upload; on
reads (`getInfo`, `list`, `rename`) it reflects any `user.e2b.*` xattr on
the file, including ones set out-of-band. `undefined` when none is set.
### name
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
name: string;
```
Name of the filesystem object.
### path
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
path: string;
```
Path to the filesystem object.
### type?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional type: FileType;
```
Type of the filesystem object.
## Type Aliases
### WriteEntry
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
type WriteEntry = object;
```
#### Type declaration
| Name | Type |
| - | - |
| `data` | `string` \| `ArrayBuffer` \| `Blob` \| `ReadableStream` |
| `path` | `string` |
## Functions
### mapEntryInfo()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
function mapEntryInfo(entry: EntryInfo): EntryInfo
```
Map a protobuf `EntryInfo` to the SDK `EntryInfo`.
#### Parameters
| Parameter | Type |
| - | - |
| `entry` | `EntryInfo` |
#### Returns
`EntryInfo`
# Secret
Source: https://docs.e2b.dev/sdk-reference/js-sdk/v2.56.0/secret
### Secret
Module for managing E2B secrets and workload identity helpers.
Secret values are write-only: they are accepted by Secret.create
and Secret.update but never returned by any read surface.
#### Constructors
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
new Secret(): Secret
```
###### Returns
`Secret`
#### Methods
### create()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
static create(
name: string,
value: string,
opts?: SecretCreateOpts): Promise
```
Create a new secret and its first value.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `name` | `string` | name of the secret, unique within the project. |
| `value` | `string` | secret value. Write-only — never returned by the API. |
| `opts`? | `SecretCreateOpts` | connection options. |
###### Returns
`Promise`\<`SecretInfo`>
the secret's ID, name, current version (`1` for a new secret),
metadata, and creation and update times.
### destroy()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
static destroy(secret: string, opts?: ConnectionOpts): Promise
```
Destroy a secret, making all its versions inaccessible.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `secret` | `string` | secret ID or name. |
| `opts`? | `ConnectionOpts` | connection options. |
###### Returns
`Promise`\<`boolean`>
`true` if the secret was destroyed, `false` if it was not found.
### exists()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
static exists(secret: string, opts?: ConnectionOpts): Promise
```
Check whether a secret exists.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `secret` | `string` | secret ID or name. |
| `opts`? | `ConnectionOpts` | connection options. |
###### Returns
`Promise`\<`boolean`>
`true` if the secret exists, `false` otherwise.
### fill()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
static fill(secret: string): string
```
Format a placeholder that the runtime resolves to the secret's current
value.
This is a local formatting helper and makes no network call — it does
not check whether the named secret exists. An unknown reference fails
server-side when the placeholder is resolved.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `secret` | `string` | secret name. |
###### Returns
`string`
placeholder string resolving to the secret's value.
###### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
Secret.fill('openai-api-key')
// '${e2b.secrets.openai-api-key}'
```
### getInfo()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
static getInfo(secret: string, opts?: ConnectionOpts): Promise
```
Get a secret's metadata.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `secret` | `string` | secret ID or name. |
| `opts`? | `ConnectionOpts` | connection options. |
###### Returns
`Promise`\<`SecretInfo`>
the secret's ID, name, current version, metadata, and creation
and update times.
### iamToken()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
static iamToken(token: SandboxIamToken): SandboxIamToken
```
Define a workload identity token to pass to `iam.tokens` when creating
a sandbox.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `token` | `SandboxIamToken` | workload token definition. |
###### Returns
`SandboxIamToken`
a token definition passable to `iam.tokens`.
###### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
const sandbox = await Sandbox.create({
iam: {
tokens: {
aws: Secret.iamToken({
audience: 'sts.amazonaws.com',
tokenType: 'JWT-SVID',
}),
},
},
})
```
### list()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
static list(opts?: SecretListOpts): SecretPaginator
```
List the project's secrets.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `opts`? | `SecretListOpts` | connection options. |
###### Returns
`SecretPaginator`
paginator over the project's secrets. Drain it page by page:
`while (paginator.hasNext) { const secrets = await paginator.nextItems() }`.
###### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
const paginator = Secret.list({ limit: 50 })
while (paginator.hasNext) {
const secrets = await paginator.nextItems()
}
```
### update()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
static update(
secret: string,
value: string,
opts?: SecretUpdateOpts): Promise
```
Update a secret's value by storing it as the secret's new version.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `secret` | `string` | secret ID or name. |
| `value` | `string` | new secret value. Write-only — never returned by the API. |
| `opts`? | `SecretUpdateOpts` | connection options. |
###### Returns
`Promise`\<`SecretInfo`>
the secret's ID, name, new current version, metadata, and
creation and update times.
***
### SecretPaginator
Paginator for listing secrets.
#### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
const paginator = Secret.list()
while (paginator.hasNext) {
const secrets = await paginator.nextItems()
console.log(secrets)
}
```
#### Constructors
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
new SecretPaginator(opts?: SecretListOpts): SecretPaginator
```
###### Parameters
| Parameter | Type |
| - | - |
| `opts`? | `SecretListOpts` |
###### Returns
`SecretPaginator`
#### Accessors
### hasNext
###### Get Signature
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
get hasNext(): boolean
```
Returns true if there are more items to fetch.
###### Returns
`boolean`
### nextToken
###### Get Signature
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
get nextToken(): undefined | string
```
Returns the next token to use for pagination.
###### Returns
`undefined` | `string`
#### Methods
### nextItems()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
nextItems(opts?: ConnectionOpts): Promise
```
Get the next page of items.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `opts`? | `ConnectionOpts` | per-call connection options. When provided, this call uses these options (e.g. `apiKey`, `domain`, `headers`, `requestTimeoutMs`, `signal`) instead of the ones the paginator was constructed with. Aborting a page via `signal` does not affect subsequent Paginator.nextItems calls — pass a fresh signal each call you want to be cancellable. |
###### Returns
`Promise`\<`SecretInfo`\[]>
List of items
###### Throws
Error if there are no more items to fetch. Call this method only if `hasNext` is `true`.
## Interfaces
### SecretCreateOpts
#### Properties
### apiHeaders?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional apiHeaders: Record;
```
Additional headers to send with E2B API requests.
### apiKey?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional apiKey: string;
```
E2B API key to use for authentication.
###### Default
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
E2B_API_KEY // environment variable
```
### domain?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional domain: string;
```
Domain to use for the API.
###### Default
E2B\_DOMAIN // environment variable or `e2b.app`
### ~~headers?~~
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional headers: Record;
```
Additional headers to send with the request.
###### Deprecated
Use `apiHeaders` instead.
### httpVersion?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional httpVersion: HttpVersion;
```
HTTP version for requests to the E2B API and to sandboxes (commands,
filesystem, PTY). `'2'` multiplexes streams over shared connections;
`'1.1'` pins them to HTTP/1.1 with one connection per concurrent
request — for example when an intermediary on the path retires or
mishandles long-lived HTTP/2 connections. Only applies in Node.
###### Default
E2B\_HTTP\_VERSION // environment variable or `'2'`
### logger?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional logger: Logger;
```
Logger to use for logging messages. It can accept any object that implements `Logger` interface—for example, console.
### metadata?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional metadata: Record;
```
Customer metadata to store with the secret.
### proxy?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional proxy: string;
```
Proxy URL to use for requests. In case of a sandbox it applies to all
requests made to the returned sandbox.
###### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
'http://user:pass@127.0.0.1:8080'
```
### requestTimeoutMs?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional requestTimeoutMs: number;
```
Timeout for requests to the API in **milliseconds**.
###### Default
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
60_000 // 60 seconds
```
### retries?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional retries: number;
```
Number of control-plane API retries after a 429, 502 or 503 response or
a failure to establish the connection (refused, DNS, unreachable host).
Any other network error (dropped connection, opaque browser/Workers
`TypeError`) is retried too. A 502 and those network errors are not
retried for operations that create a resource (e.g. sandbox creation) or
append to one (secret update), as the server may already have processed
them.
A 429 is retried only with a valid, non-negative integer delta-seconds
`Retry-After` header (HTTP-date and malformed values are not retried).
502 and 503 honor such a `Retry-After` when present; otherwise they and
connection failures use exponential backoff with jitter starting at
100 ms (capped at 10 s).
Retry waits use a 60-second total limit when request timeouts are disabled.
###### Default
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
3
```
### signal?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional signal: AbortSignal;
```
An optional `AbortSignal` that can be used to cancel the in-flight request.
When the signal is aborted, the underlying `fetch` is aborted and the
returned promise rejects with an `AbortError`.
### ~~validateApiKey?~~
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional validateApiKey: boolean;
```
Whether to validate the format of the E2B API key on the client side.
###### Deprecated
The API key format is no longer validated on the client side;
this option has no effect.
***
### SecretInfo
Metadata of a secret. Secret values are write-only and never returned.
#### Properties
### createdAt
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
createdAt: Date;
```
Time when the secret was created.
### metadata
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
metadata: Record;
```
Customer metadata of the secret.
### name
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
name: string;
```
Secret name, unique within the project.
### secretId
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
secretId: string;
```
Secret ID.
### updatedAt
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
updatedAt: Date;
```
Time when the secret was last updated.
### version
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
version: number;
```
Version served to readers that do not name one.
***
### SecretListOpts
#### Properties
### apiHeaders?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional apiHeaders: Record;
```
Additional headers to send with E2B API requests.
### apiKey?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional apiKey: string;
```
E2B API key to use for authentication.
###### Default
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
E2B_API_KEY // environment variable
```
### domain?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional domain: string;
```
Domain to use for the API.
###### Default
E2B\_DOMAIN // environment variable or `e2b.app`
### ~~headers?~~
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional headers: Record;
```
Additional headers to send with the request.
###### Deprecated
Use `apiHeaders` instead.
### httpVersion?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional httpVersion: HttpVersion;
```
HTTP version for requests to the E2B API and to sandboxes (commands,
filesystem, PTY). `'2'` multiplexes streams over shared connections;
`'1.1'` pins them to HTTP/1.1 with one connection per concurrent
request — for example when an intermediary on the path retires or
mishandles long-lived HTTP/2 connections. Only applies in Node.
###### Default
E2B\_HTTP\_VERSION // environment variable or `'2'`
### limit?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional limit: number;
```
Number of secrets to return per page.
###### Default
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
100
```
### logger?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional logger: Logger;
```
Logger to use for logging messages. It can accept any object that implements `Logger` interface—for example, console.
### nextToken?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional nextToken: string;
```
Token to the next page.
### proxy?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional proxy: string;
```
Proxy URL to use for requests. In case of a sandbox it applies to all
requests made to the returned sandbox.
###### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
'http://user:pass@127.0.0.1:8080'
```
### requestTimeoutMs?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional requestTimeoutMs: number;
```
Timeout for requests to the API in **milliseconds**.
###### Default
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
60_000 // 60 seconds
```
### retries?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional retries: number;
```
Number of control-plane API retries after a 429, 502 or 503 response or
a failure to establish the connection (refused, DNS, unreachable host).
Any other network error (dropped connection, opaque browser/Workers
`TypeError`) is retried too. A 502 and those network errors are not
retried for operations that create a resource (e.g. sandbox creation) or
append to one (secret update), as the server may already have processed
them.
A 429 is retried only with a valid, non-negative integer delta-seconds
`Retry-After` header (HTTP-date and malformed values are not retried).
502 and 503 honor such a `Retry-After` when present; otherwise they and
connection failures use exponential backoff with jitter starting at
100 ms (capped at 10 s).
Retry waits use a 60-second total limit when request timeouts are disabled.
###### Default
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
3
```
### ~~validateApiKey?~~
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional validateApiKey: boolean;
```
Whether to validate the format of the E2B API key on the client side.
###### Deprecated
The API key format is no longer validated on the client side;
this option has no effect.
***
### SecretUpdateOpts
#### Properties
### apiHeaders?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional apiHeaders: Record;
```
Additional headers to send with E2B API requests.
### apiKey?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional apiKey: string;
```
E2B API key to use for authentication.
###### Default
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
E2B_API_KEY // environment variable
```
### domain?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional domain: string;
```
Domain to use for the API.
###### Default
E2B\_DOMAIN // environment variable or `e2b.app`
### ~~headers?~~
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional headers: Record;
```
Additional headers to send with the request.
###### Deprecated
Use `apiHeaders` instead.
### httpVersion?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional httpVersion: HttpVersion;
```
HTTP version for requests to the E2B API and to sandboxes (commands,
filesystem, PTY). `'2'` multiplexes streams over shared connections;
`'1.1'` pins them to HTTP/1.1 with one connection per concurrent
request — for example when an intermediary on the path retires or
mishandles long-lived HTTP/2 connections. Only applies in Node.
###### Default
E2B\_HTTP\_VERSION // environment variable or `'2'`
### logger?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional logger: Logger;
```
Logger to use for logging messages. It can accept any object that implements `Logger` interface—for example, console.
### metadata?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional metadata: Record;
```
Customer metadata to store with the secret. When provided, replaces the
stored metadata.
### proxy?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional proxy: string;
```
Proxy URL to use for requests. In case of a sandbox it applies to all
requests made to the returned sandbox.
###### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
'http://user:pass@127.0.0.1:8080'
```
### requestTimeoutMs?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional requestTimeoutMs: number;
```
Timeout for requests to the API in **milliseconds**.
###### Default
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
60_000 // 60 seconds
```
### retries?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional retries: number;
```
Number of control-plane API retries after a 429, 502 or 503 response or
a failure to establish the connection (refused, DNS, unreachable host).
Any other network error (dropped connection, opaque browser/Workers
`TypeError`) is retried too. A 502 and those network errors are not
retried for operations that create a resource (e.g. sandbox creation) or
append to one (secret update), as the server may already have processed
them.
A 429 is retried only with a valid, non-negative integer delta-seconds
`Retry-After` header (HTTP-date and malformed values are not retried).
502 and 503 honor such a `Retry-After` when present; otherwise they and
connection failures use exponential backoff with jitter starting at
100 ms (capped at 10 s).
Retry waits use a 60-second total limit when request timeouts are disabled.
###### Default
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
3
```
### signal?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional signal: AbortSignal;
```
An optional `AbortSignal` that can be used to cancel the in-flight request.
When the signal is aborted, the underlying `fetch` is aborted and the
returned promise rejects with an `AbortError`.
### ~~validateApiKey?~~
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional validateApiKey: boolean;
```
Whether to validate the format of the E2B API key on the client side.
###### Deprecated
The API key format is no longer validated on the client side;
this option has no effect.
## Type Aliases
### SecretDestroyOpts
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
type SecretDestroyOpts = ConnectionOpts;
```
***
### SecretExistsOpts
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
type SecretExistsOpts = ConnectionOpts;
```
***
### SecretGetInfoOpts
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
type SecretGetInfoOpts = ConnectionOpts;
```
# Template
Source: https://docs.e2b.dev/sdk-reference/js-sdk/v2.56.0/template
### TemplateBase
Builder for E2B sandbox templates, and the entrypoint for the template API.
Exposed as Template, which can be called as a factory.
#### Implements
* `TemplateFromImage`
* `TemplateBuilder`
* `TemplateFinal`
#### Constructors
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
new TemplateBase(options?: TemplateOptions): TemplateBase
```
###### Parameters
| Parameter | Type |
| - | - |
| `options`? | `TemplateOptions` |
###### Returns
`TemplateBase`
#### Methods
### addMcpServer()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
addMcpServer(servers: keyof McpServer | keyof McpServer[]): TemplateBuilder
```
Install MCP servers using mcp-gateway.
Note: Requires a base image with mcp-gateway pre-installed (e.g., mcp-gateway).
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `servers` | keyof McpServer \| keyof McpServer\[] | MCP server name(s) |
###### Returns
`TemplateBuilder`
###### Throws
If the base template is not mcp-gateway
###### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
template.addMcpServer('exa')
template.addMcpServer(['brave', 'firecrawl', 'duckduckgo'])
```
###### Implementation of
`TemplateBuilder`.`addMcpServer`
### aptInstall()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
aptInstall(packages: string | string[], options?: object): TemplateBuilder
```
Install Debian/Ubuntu packages using apt-get.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `packages` | `string` \| `string`\[] | Package name(s) |
| `options`? | \{ `fixMissing`: `boolean`; `noInstallRecommends`: `boolean`; } | - |
| `options.fixMissing`? | `boolean` | - |
| `options.noInstallRecommends`? | `boolean` | - |
###### Returns
`TemplateBuilder`
###### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
template.aptInstall('vim')
template.aptInstall(['git', 'curl', 'wget'])
template.aptInstall(['vim'], { noInstallRecommends: true })
template.aptInstall(['vim'], { fixMissing: true })
```
###### Implementation of
`TemplateBuilder`.`aptInstall`
### betaDevContainerPrebuild()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
betaDevContainerPrebuild(devcontainerDirectory: string): TemplateBuilder
```
Prebuild a devcontainer from the specified directory.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `devcontainerDirectory` | `string` | Path to the devcontainer directory |
###### Returns
`TemplateBuilder`
###### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
template
.gitClone('https://myrepo.com/project.git', '/my-devcontainer')
.betaDevContainerPrebuild('/my-devcontainer')
```
###### Implementation of
`TemplateBuilder`.`betaDevContainerPrebuild`
### betaSetDevContainerStart()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
betaSetDevContainerStart(devcontainerDirectory: string): TemplateFinal
```
Start a devcontainer from the specified directory.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `devcontainerDirectory` | `string` | Path to the devcontainer directory |
###### Returns
`TemplateFinal`
###### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
template
.gitClone('https://myrepo.com/project.git', '/my-devcontainer')
.startDevcontainer('/my-devcontainer')
// Prebuild and start
template
.gitClone('https://myrepo.com/project.git', '/my-devcontainer')
.betaDevContainerPrebuild('/my-devcontainer')
// Other instructions...
.betaSetDevContainerStart('/my-devcontainer')
```
###### Implementation of
`TemplateBuilder`.`betaSetDevContainerStart`
### bunInstall()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
bunInstall(packages?: string | string[], options?: object): TemplateBuilder
```
Install Bun packages using bun.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `packages`? | `string` \| `string`\[] | Package name(s) or undefined for package.json |
| `options`? | \{ `dev`: `boolean`; `g`: `boolean`; } | Install options |
| `options.dev`? | `boolean` | - |
| `options.g`? | `boolean` | - |
###### Returns
`TemplateBuilder`
###### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
template.bunInstall('express')
template.bunInstall(['lodash', 'axios'])
template.bunInstall('tsx', { g: true })
template.bunInstall('typescript', { dev: true })
template.bunInstall() // Installs from package.json
```
###### Implementation of
`TemplateBuilder`.`bunInstall`
### copy()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
copy(
src: PathLike | PathLike[],
dest: PathLike,
options?: object): TemplateBuilder
```
Copy files or directories into the template.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `src` | `PathLike` \| `PathLike`\[] | Source path(s) |
| `dest` | `PathLike` | Destination path |
| `options`? | \{ `forceUpload`: `true`; `gzip`: `boolean`; `mode`: `number`; `resolveSymlinks`: `boolean`; `user`: `string`; } | Copy options |
| `options.forceUpload`? | `true` | - |
| `options.gzip`? | `boolean` | - |
| `options.mode`? | `number` | - |
| `options.resolveSymlinks`? | `boolean` | - |
| `options.user`? | `string` | - |
###### Returns
`TemplateBuilder`
###### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
template.copy('requirements.txt', '/home/user/')
template.copy(['app.ts', 'config.ts'], '/app/', { mode: 0o755 })
```
###### Implementation of
`TemplateBuilder`.`copy`
### copyItems()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
copyItems(items: CopyItem[]): TemplateBuilder
```
Copy multiple items with individual options.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `items` | `CopyItem`\[] | Array of copy items |
###### Returns
`TemplateBuilder`
###### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
template.copyItems([
{ src: 'app.ts', dest: '/app/' },
{ src: 'config.ts', dest: '/app/', mode: 0o644 }
])
```
###### Implementation of
`TemplateBuilder`.`copyItems`
### fromAlpineImage()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
fromAlpineImage(variant: string): TemplateBuilder
```
Start from an Alpine-based Docker image.
###### Parameters
| Parameter | Type | Default value | Description |
| - | - | - | - |
| `variant` | `string` | `'3.24'` | Alpine variant (default: '3.24') |
###### Returns
`TemplateBuilder`
###### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
Template().fromAlpineImage('3.24')
```
###### Implementation of
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
TemplateFromImage.fromAlpineImage
```
### fromArchImage()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
fromArchImage(variant: string): TemplateBuilder
```
Start from an Arch Linux-based Docker image.
Defaults to `latest`: Arch is a rolling release and template provisioning
runs `pacman -Syu`, so pinning a tag would not change the built result.
###### Parameters
| Parameter | Type | Default value | Description |
| - | - | - | - |
| `variant` | `string` | `'latest'` | Arch Linux variant (default: 'latest') |
###### Returns
`TemplateBuilder`
###### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
Template().fromArchImage('base-devel')
```
###### Implementation of
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
TemplateFromImage.fromArchImage
```
### fromAWSRegistry()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
fromAWSRegistry(image: string, credentials: object): TemplateBuilder
```
Start from a Docker image in AWS ECR.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `image` | `string` | Full ECR image path |
| `credentials` | \{ `accessKeyId`: `string`; `region`: `string`; `secretAccessKey`: `string`; } | AWS credentials |
| `credentials.accessKeyId` | `string` | - |
| `credentials.region` | `string` | - |
| `credentials.secretAccessKey` | `string` | - |
###### Returns
`TemplateBuilder`
###### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
Template().fromAWSRegistry(
'123456789.dkr.ecr.us-west-2.amazonaws.com/myimage:latest',
{
accessKeyId: 'AKIA...',
secretAccessKey: '...',
region: 'us-west-2'
}
)
```
###### Implementation of
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
TemplateFromImage.fromAWSRegistry
```
### fromBaseImage()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
fromBaseImage(): TemplateBuilder
```
Start from E2B's default base image (e2bdev/base:latest).
###### Returns
`TemplateBuilder`
###### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
Template().fromBaseImage()
```
###### Implementation of
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
TemplateFromImage.fromBaseImage
```
### fromBunImage()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
fromBunImage(variant: string): TemplateBuilder
```
Start from a Bun-based Docker image.
###### Parameters
| Parameter | Type | Default value | Description |
| - | - | - | - |
| `variant` | `string` | `'latest'` | Bun variant (default: 'latest') |
###### Returns
`TemplateBuilder`
###### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
Template().fromBunImage('1.3')
```
###### Implementation of
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
TemplateFromImage.fromBunImage
```
### fromDebianImage()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
fromDebianImage(variant: string): TemplateBuilder
```
Start from a Debian-based Docker image.
###### Parameters
| Parameter | Type | Default value | Description |
| - | - | - | - |
| `variant` | `string` | `'stable'` | Debian variant (default: 'stable') |
###### Returns
`TemplateBuilder`
###### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
Template().fromDebianImage('bookworm')
```
###### Implementation of
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
TemplateFromImage.fromDebianImage
```
### fromDockerfile()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
fromDockerfile(dockerfileContentOrPath: string): TemplateBuilder
```
Parse a Dockerfile and convert it to Template SDK format.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `dockerfileContentOrPath` | `string` | Dockerfile content or path |
###### Returns
`TemplateBuilder`
###### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
Template().fromDockerfile('Dockerfile')
Template().fromDockerfile('FROM python:3\nRUN pip install numpy')
```
###### Implementation of
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
TemplateFromImage.fromDockerfile
```
### fromFedoraImage()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
fromFedoraImage(variant: string): TemplateBuilder
```
Start from a Fedora-based Docker image.
###### Parameters
| Parameter | Type | Default value | Description |
| - | - | - | - |
| `variant` | `string` | `'44'` | Fedora variant (default: '44') |
###### Returns
`TemplateBuilder`
###### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
Template().fromFedoraImage('44')
```
###### Implementation of
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
TemplateFromImage.fromFedoraImage
```
### fromGCPRegistry()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
fromGCPRegistry(image: string, credentials: object): TemplateBuilder
```
Start from a Docker image in Google Container Registry.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `image` | `string` | Full GCR/GAR image path |
| `credentials` | \{ `serviceAccountJSON`: `string` \| `object`; } | GCP service account credentials |
| `credentials.serviceAccountJSON` | `string` \| `object` | - |
###### Returns
`TemplateBuilder`
###### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
Template().fromGCPRegistry(
'gcr.io/myproject/myimage:latest',
{ serviceAccountJSON: 'path/to/service-account.json' }
)
```
###### Implementation of
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
TemplateFromImage.fromGCPRegistry
```
### fromImage()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
fromImage(baseImage: string, credentials?: object): TemplateBuilder
```
Start from a custom Docker image.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `baseImage` | `string` | Docker image name |
| `credentials`? | \{ `password`: `string`; `username`: `string`; } | Optional credentials for private registries |
| `credentials.password`? | `string` | - |
| `credentials.username`? | `string` | - |
###### Returns
`TemplateBuilder`
###### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
Template().fromImage('python:3')
// With credentials (optional)
Template().fromImage('myregistry.com/myimage:latest', {
username: 'user',
password: 'pass'
})
```
###### Implementation of
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
TemplateFromImage.fromImage
```
### fromNodeImage()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
fromNodeImage(variant: string): TemplateBuilder
```
Start from a Node.js-based Docker image.
###### Parameters
| Parameter | Type | Default value | Description |
| - | - | - | - |
| `variant` | `string` | `'lts'` | Node.js variant (default: 'lts') |
###### Returns
`TemplateBuilder`
###### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
Template().fromNodeImage('24')
```
###### Implementation of
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
TemplateFromImage.fromNodeImage
```
### fromPythonImage()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
fromPythonImage(version: string): TemplateBuilder
```
Start from a Python-based Docker image.
###### Parameters
| Parameter | Type | Default value | Description |
| - | - | - | - |
| `version` | `string` | `'3'` | Python version (default: '3') |
###### Returns
`TemplateBuilder`
###### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
Template().fromPythonImage('3')
```
###### Implementation of
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
TemplateFromImage.fromPythonImage
```
### fromTemplate()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
fromTemplate(template: string): TemplateBuilder
```
Start from an existing E2B template.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `template` | `string` | E2B template ID or alias |
###### Returns
`TemplateBuilder`
###### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
Template().fromTemplate('my-base-template')
```
###### Implementation of
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
TemplateFromImage.fromTemplate
```
### fromUbuntuImage()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
fromUbuntuImage(variant: string): TemplateBuilder
```
Start from an Ubuntu-based Docker image.
###### Parameters
| Parameter | Type | Default value | Description |
| - | - | - | - |
| `variant` | `string` | `'latest'` | Ubuntu variant (default: 'latest') |
###### Returns
`TemplateBuilder`
###### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
Template().fromUbuntuImage('24.04')
```
###### Implementation of
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
TemplateFromImage.fromUbuntuImage
```
### gitClone()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
gitClone(
url: string,
path?: PathLike,
options?: object): TemplateBuilder
```
Clone a Git repository.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `url` | `string` | Repository URL |
| `path`? | `PathLike` | Optional destination path |
| `options`? | \{ `branch`: `string`; `depth`: `number`; `user`: `string`; } | Clone options |
| `options.branch`? | `string` | - |
| `options.depth`? | `number` | - |
| `options.user`? | `string` | - |
###### Returns
`TemplateBuilder`
###### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
template.gitClone('https://github.com/user/repo.git', '/app/repo')
template.gitClone('https://github.com/user/repo.git', undefined, {
branch: 'main',
depth: 1
})
template.gitClone('https://github.com/user/repo.git', '/app/repo', {
user: 'root'
})
```
###### Implementation of
`TemplateBuilder`.`gitClone`
### makeDir()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
makeDir(path: PathLike | PathLike[], options?: object): TemplateBuilder
```
Create directories.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `path` | `PathLike` \| `PathLike`\[] | Directory path(s) |
| `options`? | \{ `mode`: `number`; `user`: `string`; } | Directory options |
| `options.mode`? | `number` | - |
| `options.user`? | `string` | - |
###### Returns
`TemplateBuilder`
###### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
template.makeDir('/app/data', { mode: 0o755 })
template.makeDir(['/app/logs', '/app/cache'])
template.makeDir('/app/data', { mode: 0o755, user: 'root' })
```
###### Implementation of
`TemplateBuilder`.`makeDir`
### makeSymlink()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
makeSymlink(
src: PathLike,
dest: PathLike,
options?: object): TemplateBuilder
```
Create a symbolic link.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `src` | `PathLike` | Source path (target) |
| `dest` | `PathLike` | Destination path (symlink location) |
| `options`? | \{ `force`: `boolean`; `user`: `string`; } | Symlink options |
| `options.force`? | `boolean` | - |
| `options.user`? | `string` | - |
###### Returns
`TemplateBuilder`
###### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
template.makeSymlink('/usr/bin/python3', '/usr/bin/python')
template.makeSymlink('/usr/bin/python3', '/usr/bin/python', { user: 'root' })
template.makeSymlink('/usr/bin/python3', '/usr/bin/python', { force: true })
```
###### Implementation of
`TemplateBuilder`.`makeSymlink`
### npmInstall()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
npmInstall(packages?: string | string[], options?: object): TemplateBuilder
```
Install Node.js packages using npm.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `packages`? | `string` \| `string`\[] | Package name(s) or undefined for package.json |
| `options`? | \{ `dev`: `boolean`; `g`: `boolean`; } | Install options |
| `options.dev`? | `boolean` | - |
| `options.g`? | `boolean` | - |
###### Returns
`TemplateBuilder`
###### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
template.npmInstall('express')
template.npmInstall(['lodash', 'axios'])
template.npmInstall('tsx', { g: true })
template.npmInstall('typescript', { dev: true })
template.npmInstall() // Installs from package.json
```
###### Implementation of
`TemplateBuilder`.`npmInstall`
### pipInstall()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
pipInstall(packages?: string | string[], options?: object): TemplateBuilder
```
Install Python packages using pip.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `packages`? | `string` \| `string`\[] | Package name(s) or undefined for current directory |
| `options`? | \{ `g`: `boolean`; } | Install options |
| `options.g`? | `boolean` | Install globally as root (default: true). Set to false for user-only installation with --user flag |
###### Returns
`TemplateBuilder`
###### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
template.pipInstall('numpy') // Installs globally (default)
template.pipInstall(['pandas', 'scikit-learn'])
template.pipInstall('numpy', { g: false }) // Install for user only
template.pipInstall() // Installs from current directory
```
###### Implementation of
`TemplateBuilder`.`pipInstall`
### remove()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
remove(path: PathLike | PathLike[], options?: object): TemplateBuilder
```
Remove files or directories.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `path` | `PathLike` \| `PathLike`\[] | Path(s) to remove |
| `options`? | \{ `force`: `boolean`; `recursive`: `boolean`; `user`: `string`; } | Remove options |
| `options.force`? | `boolean` | - |
| `options.recursive`? | `boolean` | - |
| `options.user`? | `string` | - |
###### Returns
`TemplateBuilder`
###### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
template.remove('/tmp/cache', { recursive: true, force: true })
template.remove('/tmp/cache', { recursive: true, force: true, user: 'root' })
```
###### Implementation of
`TemplateBuilder`.`remove`
### rename()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
rename(
src: PathLike,
dest: PathLike,
options?: object): TemplateBuilder
```
Rename or move a file or directory.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `src` | `PathLike` | Source path |
| `dest` | `PathLike` | Destination path |
| `options`? | \{ `force`: `boolean`; `user`: `string`; } | Rename options |
| `options.force`? | `boolean` | - |
| `options.user`? | `string` | - |
###### Returns
`TemplateBuilder`
###### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
template.rename('/tmp/old.txt', '/tmp/new.txt')
template.rename('/tmp/old.txt', '/tmp/new.txt', { user: 'root' })
```
###### Implementation of
`TemplateBuilder`.`rename`
### runCmd()
###### Call Signature
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
runCmd(command: string, options?: object): TemplateBuilder
```
Run a shell command.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `command` | `string` | Command string |
| `options`? | \{ `user`: `string`; } | Command options |
| `options.user`? | `string` | - |
###### Returns
`TemplateBuilder`
###### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
template.runCmd('apt-get update')
template.runCmd(['pip install numpy', 'pip install pandas'])
template.runCmd('apt-get install vim', { user: 'root' })
```
###### Implementation of
`TemplateBuilder`.`runCmd`
###### Call Signature
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
runCmd(commands: string[], options?: object): TemplateBuilder
```
Run multiple shell commands.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `commands` | `string`\[] | Array of command strings |
| `options`? | \{ `user`: `string`; } | Command options |
| `options.user`? | `string` | - |
###### Returns
`TemplateBuilder`
###### Implementation of
`TemplateBuilder`.`runCmd`
### setEnvs()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
setEnvs(envs: Record): TemplateBuilder
```
Set environment variables.
Note: Environment variables defined here are available only during template build.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `envs` | `Record`\<`string`, `string`> | Environment variables |
###### Returns
`TemplateBuilder`
###### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
template.setEnvs({ NODE_ENV: 'production', PORT: '8080' })
```
###### Implementation of
`TemplateBuilder`.`setEnvs`
### setReadyCmd()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
setReadyCmd(readyCommand: string | ReadyCmd): TemplateFinal
```
Set or update the ready check command.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `readyCommand` | `string` \| `ReadyCmd` | Command to check readiness |
###### Returns
`TemplateFinal`
###### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
// Using a string command
template.setReadyCmd('curl http://localhost:8000/health')
// Using ReadyCmd helpers
import { waitForPort, waitForFile, waitForProcess } from 'e2b'
template.setReadyCmd(waitForPort(3000))
template.setReadyCmd(waitForFile('/tmp/ready'))
template.setReadyCmd(waitForProcess('nginx'))
```
###### Implementation of
`TemplateBuilder`.`setReadyCmd`
### setStartCmd()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
setStartCmd(startCommand: string, readyCommand: string | ReadyCmd): TemplateFinal
```
Set the start command and ready check.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `startCommand` | `string` | Command to run on startup |
| `readyCommand` | `string` \| `ReadyCmd` | Command to check readiness |
###### Returns
`TemplateFinal`
###### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
// Using a string command
template.setStartCmd(
'node app.js',
'curl http://localhost:8000/health'
)
// Using ReadyCmd helpers
import { waitForPort, waitForURL } from 'e2b'
template.setStartCmd(
'python -m http.server 8000',
waitForPort(8000)
)
template.setStartCmd(
'npm start',
waitForURL('http://localhost:3000/health', 200)
)
```
###### Implementation of
`TemplateBuilder`.`setStartCmd`
### setUser()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
setUser(user: string): TemplateBuilder
```
Set the user for subsequent commands.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `user` | `string` | Username |
###### Returns
`TemplateBuilder`
###### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
template.setUser('root')
```
###### Implementation of
`TemplateBuilder`.`setUser`
### setWorkdir()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
setWorkdir(workdir: PathLike): TemplateBuilder
```
Set the working directory.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `workdir` | `PathLike` | Working directory path |
###### Returns
`TemplateBuilder`
###### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
template.setWorkdir('/app')
```
###### Implementation of
`TemplateBuilder`.`setWorkdir`
### skipCache()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
skipCache(): this
```
Skip cache for all subsequent build instructions from this point.
###### Returns
`this`
###### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
Template().skipCache().fromPythonImage('3')
```
###### Implementation of
`TemplateBuilder`.`skipCache`
### ~~aliasExists()~~
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
static aliasExists(alias: string, options?: ConnectionOpts): Promise
```
Check if a template with the given alias exists.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `alias` | `string` | Template alias to check |
| `options`? | `ConnectionOpts` | Authentication options |
###### Returns
`Promise`\<`boolean`>
True if the alias exists, false otherwise
###### Deprecated
Use `exists` instead.
###### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
const exists = await Template.aliasExists('my-python-env')
if (exists) {
console.log('Template exists!')
}
```
### assignTags()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
static assignTags(
targetName: string,
tags: string | string[],
options?: ConnectionOpts): Promise
```
Assign tag(s) to an existing template build.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `targetName` | `string` | Template name in 'name:tag' format (the source build to tag from) |
| `tags` | `string` \| `string`\[] | Tag or tags to assign |
| `options`? | `ConnectionOpts` | Authentication options |
###### Returns
`Promise`\<`TemplateTagInfo`>
Tag info with buildId and assigned tags
###### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
// Assign a single tag
await Template.assignTags('my-template:v1.0', 'production')
// Assign multiple tags
await Template.assignTags('my-template:v1.0', ['production', 'stable'])
```
### build()
###### Call Signature
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
static build(
template: TemplateClass,
name: string,
options?: Omit): Promise
```
Build and deploy a template to E2B infrastructure.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `template` | `TemplateClass` | The template to build |
| `name` | `string` | Template name in 'name' or 'name:tag' format |
| `options`? | `Omit`\<`BuildOptions`, `"alias"`> | Optional build configuration options |
###### Returns
`Promise`\<`BuildInfo`>
###### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
const template = Template().fromPythonImage('3')
// Build with single tag in name
await Template.build(template, 'my-python-env:v1.0')
// Build with multiple tags
await Template.build(template, 'my-python-env', { tags: ['v1.0', 'stable'] })
```
###### Call Signature
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
static build(template: TemplateClass, options: BuildOptions): Promise
```
Build and deploy a template to E2B infrastructure.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `template` | `TemplateClass` | The template to build |
| `options` | `BuildOptions` | Build configuration options with alias (deprecated) |
###### Returns
`Promise`\<`BuildInfo`>
###### Deprecated
Use the overload with `name` parameter instead.
###### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
// Deprecated:
await Template.build(template, { alias: 'my-python-env' })
// Use instead:
await Template.build(template, 'my-python-env:v1.0')
```
### buildInBackground()
###### Call Signature
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
static buildInBackground(
template: TemplateClass,
name: string,
options?: Omit): Promise
```
Build and deploy a template to E2B infrastructure without waiting for completion.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `template` | `TemplateClass` | The template to build |
| `name` | `string` | Template name in 'name' or 'name:tag' format |
| `options`? | `Omit`\<`BuildOptions`, `"alias"`> | Optional build configuration options |
###### Returns
`Promise`\<`BuildInfo`>
###### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
const template = Template().fromPythonImage('3')
// Build with single tag in name
const data = await Template.buildInBackground(template, 'my-python-env:v1.0')
// Build with multiple tags
const data = await Template.buildInBackground(template, 'my-python-env', { tags: ['v1.0', 'stable'] })
```
###### Call Signature
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
static buildInBackground(template: TemplateClass, options: BuildOptions): Promise
```
Build and deploy a template to E2B infrastructure without waiting for completion.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `template` | `TemplateClass` | The template to build |
| `options` | `BuildOptions` | Build configuration options with alias (deprecated) |
###### Returns
`Promise`\<`BuildInfo`>
###### Deprecated
Use the overload with `name` parameter instead.
###### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
// Deprecated:
await Template.buildInBackground(template, { alias: 'my-python-env' })
// Use instead:
await Template.buildInBackground(template, 'my-python-env:v1.0')
```
### exists()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
static exists(name: string, options?: ConnectionOpts): Promise
```
Check if a template with the given name exists.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `name` | `string` | Template name to check |
| `options`? | `ConnectionOpts` | Authentication options |
###### Returns
`Promise`\<`boolean`>
True if the name exists, false otherwise
###### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
const exists = await Template.exists('my-python-env')
if (exists) {
console.log('Template exists!')
}
```
### getBuildStatus()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
static getBuildStatus(data: Pick, options?: GetBuildStatusOptions): Promise
```
Get the status of a build.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `data` | `Pick`\<`BuildInfo`, `"templateId"` \| `"buildId"`> | Build identifiers |
| `options`? | `GetBuildStatusOptions` | Authentication options |
###### Returns
`Promise`\<`TemplateBuildStatusResponse`>
###### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
const status = await Template.getBuildStatus(data)
```
### getTags()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
static getTags(templateId: string, options?: ConnectionOpts): Promise
```
Get all tags for a template.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `templateId` | `string` | Template ID or name |
| `options`? | `ConnectionOpts` | Authentication options |
###### Returns
`Promise`\<`TemplateTag`\[]>
Array of tag details including tag name, buildId, and creation date
###### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
const tags = await Template.getTags('my-template')
for (const tag of tags) {
console.log(`Tag: ${tag.tag}, Build: ${tag.buildId}, Created: ${tag.createdAt}`)
}
```
### removeTags()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
static removeTags(
name: string,
tags: string | string[],
options?: ConnectionOpts): Promise
```
Remove tag(s) from a template.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `name` | `string` | Template name |
| `tags` | `string` \| `string`\[] | Tag or tags to remove |
| `options`? | `ConnectionOpts` | Authentication options |
###### Returns
`Promise`\<`void`>
###### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
// Remove a single tag
await Template.removeTags('my-template', 'production')
// Remove multiple tags from a template
await Template.removeTags('my-template', ['production', 'staging'])
```
### toDockerfile()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
static toDockerfile(template: TemplateClass): string
```
Convert a template to Dockerfile format.
Note: Templates based on other E2B templates cannot be converted to Dockerfile.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `template` | `TemplateClass` | The template to convert |
###### Returns
`string`
Dockerfile string representation
###### Throws
Error if the template is based on another E2B template
### toJSON()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
static toJSON(template: TemplateClass, computeHashes: boolean): Promise
```
Convert a template to JSON representation.
###### Parameters
| Parameter | Type | Default value | Description |
| - | - | - | - |
| `template` | `TemplateClass` | `undefined` | The template to convert |
| `computeHashes` | `boolean` | `true` | Whether to compute file hashes for cache invalidation |
###### Returns
`Promise`\<`string`>
JSON string representation of the template
## Interfaces
### TemplateBuilder
Main builder state for constructing templates.
Provides methods for customizing the template environment.
#### Methods
### addMcpServer()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
addMcpServer(servers: keyof McpServer | keyof McpServer[]): TemplateBuilder
```
Install MCP servers using mcp-gateway.
Note: Requires a base image with mcp-gateway pre-installed (e.g., mcp-gateway).
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `servers` | keyof McpServer \| keyof McpServer\[] | MCP server name(s) |
###### Returns
`TemplateBuilder`
###### Throws
If the base template is not mcp-gateway
###### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
template.addMcpServer('exa')
template.addMcpServer(['brave', 'firecrawl', 'duckduckgo'])
```
### aptInstall()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
aptInstall(packages: string | string[], options?: object): TemplateBuilder
```
Install Debian/Ubuntu packages using apt-get.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `packages` | `string` \| `string`\[] | Package name(s) |
| `options`? | \{ `fixMissing`: `boolean`; `noInstallRecommends`: `boolean`; } | - |
| `options.fixMissing`? | `boolean` | - |
| `options.noInstallRecommends`? | `boolean` | - |
###### Returns
`TemplateBuilder`
###### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
template.aptInstall('vim')
template.aptInstall(['git', 'curl', 'wget'])
template.aptInstall(['vim'], { noInstallRecommends: true })
template.aptInstall(['vim'], { fixMissing: true })
```
### betaDevContainerPrebuild()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
betaDevContainerPrebuild(devcontainerDirectory: string): TemplateBuilder
```
Prebuild a devcontainer from the specified directory.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `devcontainerDirectory` | `string` | Path to the devcontainer directory |
###### Returns
`TemplateBuilder`
###### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
template
.gitClone('https://myrepo.com/project.git', '/my-devcontainer')
.betaDevContainerPrebuild('/my-devcontainer')
```
### betaSetDevContainerStart()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
betaSetDevContainerStart(devcontainerDirectory: string): TemplateFinal
```
Start a devcontainer from the specified directory.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `devcontainerDirectory` | `string` | Path to the devcontainer directory |
###### Returns
`TemplateFinal`
###### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
template
.gitClone('https://myrepo.com/project.git', '/my-devcontainer')
.startDevcontainer('/my-devcontainer')
// Prebuild and start
template
.gitClone('https://myrepo.com/project.git', '/my-devcontainer')
.betaDevContainerPrebuild('/my-devcontainer')
// Other instructions...
.betaSetDevContainerStart('/my-devcontainer')
```
### bunInstall()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
bunInstall(packages?: string | string[], options?: object): TemplateBuilder
```
Install Bun packages using bun.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `packages`? | `string` \| `string`\[] | Package name(s) or undefined for package.json |
| `options`? | \{ `dev`: `boolean`; `g`: `boolean`; } | Install options |
| `options.dev`? | `boolean` | - |
| `options.g`? | `boolean` | - |
###### Returns
`TemplateBuilder`
###### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
template.bunInstall('express')
template.bunInstall(['lodash', 'axios'])
template.bunInstall('tsx', { g: true })
template.bunInstall('typescript', { dev: true })
template.bunInstall() // Installs from package.json
```
### copy()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
copy(
src: PathLike | PathLike[],
dest: PathLike,
options?: object): TemplateBuilder
```
Copy files or directories into the template.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `src` | `PathLike` \| `PathLike`\[] | Source path(s) |
| `dest` | `PathLike` | Destination path |
| `options`? | \{ `forceUpload`: `true`; `gzip`: `boolean`; `mode`: `number`; `resolveSymlinks`: `boolean`; `user`: `string`; } | Copy options |
| `options.forceUpload`? | `true` | - |
| `options.gzip`? | `boolean` | - |
| `options.mode`? | `number` | - |
| `options.resolveSymlinks`? | `boolean` | - |
| `options.user`? | `string` | - |
###### Returns
`TemplateBuilder`
###### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
template.copy('requirements.txt', '/home/user/')
template.copy(['app.ts', 'config.ts'], '/app/', { mode: 0o755 })
```
### copyItems()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
copyItems(items: CopyItem[]): TemplateBuilder
```
Copy multiple items with individual options.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `items` | `CopyItem`\[] | Array of copy items |
###### Returns
`TemplateBuilder`
###### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
template.copyItems([
{ src: 'app.ts', dest: '/app/' },
{ src: 'config.ts', dest: '/app/', mode: 0o644 }
])
```
### gitClone()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
gitClone(
url: string,
path?: PathLike,
options?: object): TemplateBuilder
```
Clone a Git repository.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `url` | `string` | Repository URL |
| `path`? | `PathLike` | Optional destination path |
| `options`? | \{ `branch`: `string`; `depth`: `number`; `user`: `string`; } | Clone options |
| `options.branch`? | `string` | - |
| `options.depth`? | `number` | - |
| `options.user`? | `string` | - |
###### Returns
`TemplateBuilder`
###### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
template.gitClone('https://github.com/user/repo.git', '/app/repo')
template.gitClone('https://github.com/user/repo.git', undefined, {
branch: 'main',
depth: 1
})
template.gitClone('https://github.com/user/repo.git', '/app/repo', {
user: 'root'
})
```
### makeDir()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
makeDir(path: PathLike | PathLike[], options?: object): TemplateBuilder
```
Create directories.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `path` | `PathLike` \| `PathLike`\[] | Directory path(s) |
| `options`? | \{ `mode`: `number`; `user`: `string`; } | Directory options |
| `options.mode`? | `number` | - |
| `options.user`? | `string` | - |
###### Returns
`TemplateBuilder`
###### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
template.makeDir('/app/data', { mode: 0o755 })
template.makeDir(['/app/logs', '/app/cache'])
template.makeDir('/app/data', { mode: 0o755, user: 'root' })
```
### makeSymlink()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
makeSymlink(
src: PathLike,
dest: PathLike,
options?: object): TemplateBuilder
```
Create a symbolic link.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `src` | `PathLike` | Source path (target) |
| `dest` | `PathLike` | Destination path (symlink location) |
| `options`? | \{ `force`: `boolean`; `user`: `string`; } | Symlink options |
| `options.force`? | `boolean` | - |
| `options.user`? | `string` | - |
###### Returns
`TemplateBuilder`
###### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
template.makeSymlink('/usr/bin/python3', '/usr/bin/python')
template.makeSymlink('/usr/bin/python3', '/usr/bin/python', { user: 'root' })
template.makeSymlink('/usr/bin/python3', '/usr/bin/python', { force: true })
```
### npmInstall()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
npmInstall(packages?: string | string[], options?: object): TemplateBuilder
```
Install Node.js packages using npm.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `packages`? | `string` \| `string`\[] | Package name(s) or undefined for package.json |
| `options`? | \{ `dev`: `boolean`; `g`: `boolean`; } | Install options |
| `options.dev`? | `boolean` | - |
| `options.g`? | `boolean` | - |
###### Returns
`TemplateBuilder`
###### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
template.npmInstall('express')
template.npmInstall(['lodash', 'axios'])
template.npmInstall('tsx', { g: true })
template.npmInstall('typescript', { dev: true })
template.npmInstall() // Installs from package.json
```
### pipInstall()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
pipInstall(packages?: string | string[], options?: object): TemplateBuilder
```
Install Python packages using pip.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `packages`? | `string` \| `string`\[] | Package name(s) or undefined for current directory |
| `options`? | \{ `g`: `boolean`; } | Install options |
| `options.g`? | `boolean` | Install globally as root (default: true). Set to false for user-only installation with --user flag |
###### Returns
`TemplateBuilder`
###### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
template.pipInstall('numpy') // Installs globally (default)
template.pipInstall(['pandas', 'scikit-learn'])
template.pipInstall('numpy', { g: false }) // Install for user only
template.pipInstall() // Installs from current directory
```
### remove()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
remove(path: PathLike | PathLike[], options?: object): TemplateBuilder
```
Remove files or directories.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `path` | `PathLike` \| `PathLike`\[] | Path(s) to remove |
| `options`? | \{ `force`: `boolean`; `recursive`: `boolean`; `user`: `string`; } | Remove options |
| `options.force`? | `boolean` | - |
| `options.recursive`? | `boolean` | - |
| `options.user`? | `string` | - |
###### Returns
`TemplateBuilder`
###### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
template.remove('/tmp/cache', { recursive: true, force: true })
template.remove('/tmp/cache', { recursive: true, force: true, user: 'root' })
```
### rename()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
rename(
src: PathLike,
dest: PathLike,
options?: object): TemplateBuilder
```
Rename or move a file or directory.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `src` | `PathLike` | Source path |
| `dest` | `PathLike` | Destination path |
| `options`? | \{ `force`: `boolean`; `user`: `string`; } | Rename options |
| `options.force`? | `boolean` | - |
| `options.user`? | `string` | - |
###### Returns
`TemplateBuilder`
###### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
template.rename('/tmp/old.txt', '/tmp/new.txt')
template.rename('/tmp/old.txt', '/tmp/new.txt', { user: 'root' })
```
### runCmd()
###### Call Signature
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
runCmd(command: string, options?: object): TemplateBuilder
```
Run a shell command.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `command` | `string` | Command string |
| `options`? | \{ `user`: `string`; } | Command options |
| `options.user`? | `string` | - |
###### Returns
`TemplateBuilder`
###### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
template.runCmd('apt-get update')
template.runCmd(['pip install numpy', 'pip install pandas'])
template.runCmd('apt-get install vim', { user: 'root' })
```
###### Call Signature
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
runCmd(commands: string[], options?: object): TemplateBuilder
```
Run multiple shell commands.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `commands` | `string`\[] | Array of command strings |
| `options`? | \{ `user`: `string`; } | Command options |
| `options.user`? | `string` | - |
###### Returns
`TemplateBuilder`
###### Call Signature
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
runCmd(commandOrCommands: string | string[], options?: object): TemplateBuilder
```
Run command(s).
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `commandOrCommands` | `string` \| `string`\[] | Command or commands |
| `options`? | \{ `user`: `string`; } | Command options |
| `options.user`? | `string` | - |
###### Returns
`TemplateBuilder`
### setEnvs()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
setEnvs(envs: Record): TemplateBuilder
```
Set environment variables.
Note: Environment variables defined here are available only during template build.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `envs` | `Record`\<`string`, `string`> | Environment variables |
###### Returns
`TemplateBuilder`
###### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
template.setEnvs({ NODE_ENV: 'production', PORT: '8080' })
```
### setReadyCmd()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
setReadyCmd(readyCommand: string | ReadyCmd): TemplateFinal
```
Set or update the ready check command.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `readyCommand` | `string` \| `ReadyCmd` | Command to check readiness |
###### Returns
`TemplateFinal`
###### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
// Using a string command
template.setReadyCmd('curl http://localhost:8000/health')
// Using ReadyCmd helpers
import { waitForPort, waitForFile, waitForProcess } from 'e2b'
template.setReadyCmd(waitForPort(3000))
template.setReadyCmd(waitForFile('/tmp/ready'))
template.setReadyCmd(waitForProcess('nginx'))
```
### setStartCmd()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
setStartCmd(startCommand: string, readyCommand: string | ReadyCmd): TemplateFinal
```
Set the start command and ready check.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `startCommand` | `string` | Command to run on startup |
| `readyCommand` | `string` \| `ReadyCmd` | Command to check readiness |
###### Returns
`TemplateFinal`
###### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
// Using a string command
template.setStartCmd(
'node app.js',
'curl http://localhost:8000/health'
)
// Using ReadyCmd helpers
import { waitForPort, waitForURL } from 'e2b'
template.setStartCmd(
'python -m http.server 8000',
waitForPort(8000)
)
template.setStartCmd(
'npm start',
waitForURL('http://localhost:3000/health', 200)
)
```
### setUser()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
setUser(user: string): TemplateBuilder
```
Set the user for subsequent commands.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `user` | `string` | Username |
###### Returns
`TemplateBuilder`
###### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
template.setUser('root')
```
### setWorkdir()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
setWorkdir(workdir: PathLike): TemplateBuilder
```
Set the working directory.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `workdir` | `PathLike` | Working directory path |
###### Returns
`TemplateBuilder`
###### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
template.setWorkdir('/app')
```
### skipCache()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
skipCache(): this
```
Skip cache for all subsequent build instructions from this point.
###### Returns
`this`
###### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
template.skipCache().runCmd('apt-get update')
```
## Type Aliases
### BuildInfo
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
type BuildInfo = object;
```
Information about a built template.
#### Type declaration
| Name | Type | Description |
| - | - | - |
| `alias` | `string` | First alias from the build (for backward compatibility). **Deprecated** Use `name` instead. |
| `buildId` | `string` | Build identifier. |
| `name` | `string` | Name of the template. |
| `tags` | `string`\[] | Tags assigned to this build. |
| `templateId` | `string` | Template identifier. |
***
### BuildOptions
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
type BuildOptions = ConnectionOpts & BasicBuildOptions;
```
Options for building a template with authentication.
***
### BuildStatusReason
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
type BuildStatusReason = object;
```
Reason for the current build status (typically for errors).
#### Type declaration
| Name | Type | Description |
| - | - | - |
| `logEntries` | `LogEntry`\[] | Log entries related to the status reason. |
| `message` | `string` | Message with the status reason. |
| `step`? | `string` | Step that failed. |
***
### CopyItem
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
type CopyItem = object;
```
Configuration for a single file/directory copy operation.
#### Type declaration
| Name | Type |
| - | - |
| `dest` | `PathLike` |
| `forceUpload`? | `true` |
| `gzip`? | `boolean` |
| `mode`? | `number` |
| `resolveSymlinks`? | `boolean` |
| `src` | `PathLike` \| `PathLike`\[] |
| `user`? | `string` |
***
### GetBuildStatusOptions
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
type GetBuildStatusOptions = ConnectionOpts & object;
```
Options for getting build status.
#### Type declaration
| Name | Type |
| - | - |
| `logsOffset`? | `number` |
***
### McpServerName
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
type McpServerName = keyof McpServer;
```
MCP server names that can be installed.
***
### TemplateBuildStatus
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
type TemplateBuildStatus = "building" | "waiting" | "ready" | "error";
```
Status of a template build.
***
### TemplateBuildStatusResponse
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
type TemplateBuildStatusResponse = object;
```
Response from getting build status.
#### Type declaration
| Name | Type | Description |
| - | - | - |
| `buildID` | `string` | Build identifier. |
| `logEntries` | `LogEntry`\[] | Build log entries. |
| `logs` | `string`\[] | Build logs (raw strings). Always empty since the V1 build path was removed. **Deprecated** Use `logEntries` instead. Will be removed in the next major version. |
| `reason`? | `BuildStatusReason` | Reason for the current status (typically for errors). |
| `status` | `TemplateBuildStatus` | Current status of the build. |
| `templateID` | `string` | Template identifier. |
***
### TemplateClass
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
type TemplateClass = TemplateBuilder | TemplateFinal;
```
Type representing a template in any state (builder or final).
***
### TemplateTag
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
type TemplateTag = object;
```
Detailed information about a single template tag.
#### Type declaration
| Name | Type | Description |
| - | - | - |
| `buildId` | `string` | Build identifier associated with this tag. |
| `createdAt` | `Date` | When this tag was assigned. |
| `tag` | `string` | Name of the tag. |
***
### TemplateTagInfo
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
type TemplateTagInfo = object;
```
Information about assigned template tags.
#### Type declaration
| Name | Type | Description |
| - | - | - |
| `buildId` | `string` | Build identifier associated with this tag. |
| `tags` | `string`\[] | Assigned tags of the template. |
## Variables
### Template
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
const Template: CallableTemplate;
```
Builder and API entrypoint for E2B sandbox templates.
`Template` is the TemplateBase class, wrapped so it can also be
called as a factory returning a builder. The statics (`Template.build`,
`Template.exists`, …) resolve their connection options off the class they are
called on — so a subclass can bind its own defaults.
#### Param
Optional builder options, e.g. the file context path used to
resolve relative paths passed to `copy`
#### Returns
A template builder
#### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Template } from 'e2b'
const template = Template()
.fromPythonImage('3')
.copy('requirements.txt', '/app/')
.pipInstall()
await Template.build(template, 'my-python-app:v1.0')
```
# Template logger
Source: https://docs.e2b.dev/sdk-reference/js-sdk/v2.56.0/template-logger
### LogEntry
Represents a single log entry from the template build process.
#### Extended by
* `LogEntryStart`
* `LogEntryEnd`
#### Constructors
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
new LogEntry(
timestamp: Date,
level: LogEntryLevel,
message: string): LogEntry
```
###### Parameters
| Parameter | Type |
| - | - |
| `timestamp` | `Date` |
| `level` | `LogEntryLevel` |
| `message` | `string` |
###### Returns
`LogEntry`
#### Properties
| Property | Modifier | Type |
| - | - | - |
| `level` | `readonly` | `LogEntryLevel` |
| `message` | `readonly` | `string` |
| `timestamp` | `readonly` | `Date` |
#### Methods
### toString()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
toString(): string
```
###### Returns
`string`
***
### LogEntryEnd
Special log entry indicating the end of a build process.
#### Constructors
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
new LogEntryEnd(timestamp: Date, message: string): LogEntryEnd
```
###### Parameters
| Parameter | Type |
| - | - |
| `timestamp` | `Date` |
| `message` | `string` |
###### Returns
`LogEntryEnd`
#### Properties
| Property | Modifier | Type | Inherited from |
| - | - | - | - |
| `level` | `readonly` | `LogEntryLevel` | `LogEntry`.`level` |
| `message` | `readonly` | `string` | `LogEntry`.`message` |
| `timestamp` | `readonly` | `Date` | `LogEntry`.`timestamp` |
#### Methods
### toString()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
toString(): string
```
###### Returns
`string`
***
### LogEntryStart
Special log entry indicating the start of a build process.
#### Constructors
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
new LogEntryStart(timestamp: Date, message: string): LogEntryStart
```
###### Parameters
| Parameter | Type |
| - | - |
| `timestamp` | `Date` |
| `message` | `string` |
###### Returns
`LogEntryStart`
#### Properties
| Property | Modifier | Type | Inherited from |
| - | - | - | - |
| `level` | `readonly` | `LogEntryLevel` | `LogEntry`.`level` |
| `message` | `readonly` | `string` | `LogEntry`.`message` |
| `timestamp` | `readonly` | `Date` | `LogEntry`.`timestamp` |
#### Methods
### toString()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
toString(): string
```
###### Returns
`string`
## Type Aliases
### LogEntryLevel
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
type LogEntryLevel = "debug" | "info" | "warn" | "error";
```
Log entry severity levels.
## Functions
### defaultBuildLogger()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
function defaultBuildLogger(options?: object): (logEntry: LogEntry) => void
```
Create a default build logger with animated timer display.
#### Parameters
| Parameter | Type | Description |
| - | - | - |
| `options`? | \{ `minLevel`: `LogEntryLevel`; } | Logger configuration options |
| `options.minLevel`? | `LogEntryLevel` | Minimum log level to display (default: 'info') |
#### Returns
`Function`
Logger function that accepts LogEntry instances
### Parameters
| Parameter | Type |
| - | - |
| `logEntry` | `LogEntry` |
### Returns
`void`
#### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Template, defaultBuildLogger } from 'e2b'
const template = Template().fromPythonImage()
await Template.build(template, {
alias: 'my-template',
onBuildLogs: defaultBuildLogger({ minLevel: 'debug' })
})
```
# Template readycmd
Source: https://docs.e2b.dev/sdk-reference/js-sdk/v2.56.0/template-readycmd
### ReadyCmd
Class for ready check commands.
#### Constructors
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
new ReadyCmd(cmd: string): ReadyCmd
```
###### Parameters
| Parameter | Type |
| - | - |
| `cmd` | `string` |
###### Returns
`ReadyCmd`
#### Methods
### getCmd()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
getCmd(): string
```
###### Returns
`string`
## Functions
### waitForFile()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
function waitForFile(filename: string): ReadyCmd
```
Wait for a file to exist.
Uses shell test command to check file existence.
#### Parameters
| Parameter | Type | Description |
| - | - | - |
| `filename` | `string` | Path to the file to wait for |
#### Returns
`ReadyCmd`
ReadyCmd that checks for the file
#### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Template, waitForFile } from 'e2b'
const template = Template()
.fromBaseImage()
.setStartCmd('./init.sh', waitForFile('/tmp/ready'))
```
***
### waitForPort()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
function waitForPort(port: number): ReadyCmd
```
Wait for a port to be listening.
Uses `ss` command to check if a port is open and listening.
#### Parameters
| Parameter | Type | Description |
| - | - | - |
| `port` | `number` | Port number to wait for |
#### Returns
`ReadyCmd`
ReadyCmd that checks for the port
#### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Template, waitForPort } from 'e2b'
const template = Template()
.fromPythonImage()
.setStartCmd('python -m http.server 8000', waitForPort(8000))
```
***
### waitForProcess()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
function waitForProcess(processName: string): ReadyCmd
```
Wait for a process with a specific name to be running.
Uses `pgrep` to check if a process exists.
#### Parameters
| Parameter | Type | Description |
| - | - | - |
| `processName` | `string` | Name of the process to wait for |
#### Returns
`ReadyCmd`
ReadyCmd that checks for the process
#### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Template, waitForProcess } from 'e2b'
const template = Template()
.fromBaseImage()
.setStartCmd('./my-daemon', waitForProcess('my-daemon'))
```
***
### waitForTimeout()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
function waitForTimeout(timeout: number): ReadyCmd
```
Wait for a specified timeout before considering the sandbox ready.
Uses `sleep` command to wait for a fixed duration.
#### Parameters
| Parameter | Type | Description |
| - | - | - |
| `timeout` | `number` | Time to wait in milliseconds (minimum: 1000ms / 1 second) |
#### Returns
`ReadyCmd`
ReadyCmd that waits for the specified duration
#### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Template, waitForTimeout } from 'e2b'
const template = Template()
.fromNodeImage()
.setStartCmd('npm start', waitForTimeout(5000)) // Wait 5 seconds
```
***
### waitForURL()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
function waitForURL(url: string, statusCode: number): ReadyCmd
```
Wait for a URL to return a specific HTTP status code.
Uses `curl` to make HTTP requests and check the response status.
#### Parameters
| Parameter | Type | Default value | Description |
| - | - | - | - |
| `url` | `string` | `undefined` | URL to check (e.g., '[http://localhost:3000/health](http://localhost:3000/health)') |
| `statusCode` | `number` | `200` | Expected HTTP status code (default: 200) |
#### Returns
`ReadyCmd`
ReadyCmd that checks the URL
#### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { Template, waitForURL } from 'e2b'
const template = Template()
.fromNodeImage()
.setStartCmd('npm start', waitForURL('http://localhost:3000/health'))
```
# Volume
Source: https://docs.e2b.dev/sdk-reference/js-sdk/v2.56.0/volume
### VolumeFileType
File type enum.
#### Enumeration Members
| Enumeration Member | Value |
| - | - |
| `DIRECTORY` | `"directory"` |
| `FILE` | `"file"` |
| `SYMLINK` | `"symlink"` |
| `UNKNOWN` | `"unknown"` |
## Classes
### Volume
Module for interacting with E2B volumes.
Create a `Volume` instance to interact with a volume by its ID,
or use the static methods to manage volumes.
#### Constructors
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
new Volume(
volumeId: string,
name: string,
token: string,
domain?: string,
debug?: boolean,
proxy?: string,
opts?: Pick): Volume
```
Create a local Volume instance with no API call.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `volumeId` | `string` | volume ID. |
| `name` | `string` | volume name. |
| `token` | `string` | volume auth token. |
| `domain`? | `string` | domain for the volume API. |
| `debug`? | `boolean` | whether to use debug mode. |
| `proxy`? | `string` | proxy URL for the volume content API. |
| `opts`? | `Pick`\<`ConnectionOpts`, `"httpVersion"`> | connection options kept for the volume content API. |
###### Returns
`Volume`
#### Properties
| Property | Modifier | Type | Description |
| - | - | - | - |
| `debug?` | `readonly` | `boolean` | Whether to use debug mode (connects to local volume API server). |
| `domain?` | `readonly` | `string` | Domain used for constructing the volume API URL. |
| `httpVersion?` | `readonly` | `HttpVersion` | HTTP version used for requests to the volume content API. |
| `name` | `readonly` | `string` | Volume name. |
| `proxy?` | `readonly` | `string` | Proxy URL used for requests to the volume content API. |
| `token` | `readonly` | `string` | Volume auth token. |
| `volumeId` | `readonly` | `string` | Volume ID. |
#### Methods
### exists()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
exists(path: string, opts?: VolumeApiOpts): Promise
```
Check whether a file or directory exists.
Uses getInfo under the hood. Returns `true` if the path exists,
`false` if it does not (404). Other errors are rethrown.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `path` | `string` | path to the file or directory. |
| `opts`? | `VolumeApiOpts` | connection options. |
###### Returns
`Promise`\<`boolean`>
`true` if the path exists, `false` otherwise.
### getInfo()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
getInfo(path: string, opts?: VolumeApiOpts): Promise
```
Get information about a file or directory.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `path` | `string` | path to the file or directory. |
| `opts`? | `VolumeApiOpts` | connection options. |
###### Returns
`Promise`\<`VolumeEntryStat`>
information about the entry.
### list()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
list(path: string, opts?: VolumeApiOpts & object): Promise
```
List directory contents.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `path` | `string` | path to the directory. |
| `opts`? | `VolumeApiOpts` & `object` | connection options. |
###### Returns
`Promise`\<`VolumeEntryStat`\[]>
list of entries in the directory.
### makeDir()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
makeDir(path: string, opts?: VolumeMetadataOpts & object & VolumeApiOpts): Promise
```
Create a directory.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `path` | `string` | path to the directory to create. |
| `opts`? | `VolumeMetadataOpts` & `object` & `VolumeApiOpts` | connection options. |
###### Returns
`Promise`\<`VolumeEntryStat`>
### readFile()
###### Call Signature
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
readFile(path: string, opts?: VolumeApiOpts & object & object): Promise
```
Read file content as a `string`.
You can pass `text`, `bytes`, `blob`, or `stream` to `opts.format` to change the return type.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `path` | `string` | path to the file. |
| `opts`? | `VolumeApiOpts` & `object` & `object` | connection options. |
###### Returns
`Promise`\<`string`>
file content as string
###### Call Signature
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
readFile(path: string, opts?: VolumeApiOpts & object & object): Promise>
```
Read file content as a `Uint8Array`.
You can pass `text`, `bytes`, `blob`, or `stream` to `opts.format` to change the return type.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `path` | `string` | path to the file. |
| `opts`? | `VolumeApiOpts` & `object` & `object` | connection options. |
###### Returns
`Promise`\<`Uint8Array`\<`ArrayBufferLike`>>
file content as `Uint8Array`
###### Call Signature
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
readFile(path: string, opts?: VolumeApiOpts & object & object): Promise
```
Read file content as a `Blob`.
You can pass `text`, `bytes`, `blob`, or `stream` to `opts.format` to change the return type.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `path` | `string` | path to the file. |
| `opts`? | `VolumeApiOpts` & `object` & `object` | connection options. |
###### Returns
`Promise`\<`Blob`>
file content as `Blob`
###### Call Signature
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
readFile(path: string, opts?: VolumeApiOpts & object & object): Promise>>
```
Read file content as a `ReadableStream`.
You can pass `text`, `bytes`, `blob`, or `stream` to `opts.format` to change the return type.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `path` | `string` | path to the file. |
| `opts`? | `VolumeApiOpts` & `object` & `object` | connection options. |
###### Returns
`Promise`\<`ReadableStream`\<`Uint8Array`\<`ArrayBufferLike`>>>
file content as `ReadableStream`
### remove()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
remove(path: string, opts?: VolumeApiOpts): Promise
```
Remove a file or directory.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `path` | `string` | path to the file or directory to remove. |
| `opts`? | `VolumeApiOpts` | connection options. |
###### Returns
`Promise`\<`void`>
### updateMetadata()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
updateMetadata(
path: string,
metadata: VolumeMetadataOpts,
opts?: VolumeApiOpts): Promise
```
Update file or directory metadata.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `path` | `string` | path to the file or directory. |
| `metadata` | `VolumeMetadataOpts` | metadata to update (uid, gid, mode). |
| `opts`? | `VolumeApiOpts` | connection options. |
###### Returns
`Promise`\<`VolumeEntryStat`>
updated entry information.
### writeFile()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
writeFile(
path: string,
data:
| string
| Blob
| ArrayBuffer
| ReadableStream>,
opts?: VolumeMetadataOpts & object & VolumeApiOpts): Promise
```
Write content to a file.
Writing to a file that doesn't exist creates the file.
Writing to a file that already exists overwrites the file.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `path` | `string` | path to the file. |
| `data` | \| `string` \| `Blob` \| `ArrayBuffer` \| `ReadableStream`\<`Uint8Array`\<`ArrayBufferLike`>> | data to write to the file. Data can be a string, `ArrayBuffer`, `Blob`, or `ReadableStream`. Outside the browser, `ReadableStream` data is streamed to the API instead of being buffered in memory. |
| `opts`? | `VolumeMetadataOpts` & `object` & `VolumeApiOpts` | connection options. |
###### Returns
`Promise`\<`VolumeEntryStat`>
information about the written file
### connect()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
static connect(
this: V,
volumeId: string,
opts?: ConnectionOpts): Promise>
```
Connect to an existing volume by ID.
###### Type Parameters
| Type Parameter |
| - |
| `V` *extends* *typeof* `Volume` |
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `this` | `V` | - |
| `volumeId` | `string` | volume ID. |
| `opts`? | `ConnectionOpts` | connection options. |
###### Returns
`Promise`\<`InstanceType`\<`V`>>
Volume instance.
### create()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
static create(
this: V,
name: string,
opts?: ConnectionOpts): Promise>
```
Create a new volume.
###### Type Parameters
| Type Parameter |
| - |
| `V` *extends* *typeof* `Volume` |
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `this` | `V` | - |
| `name` | `string` | name of the volume. |
| `opts`? | `ConnectionOpts` | connection options. |
###### Returns
`Promise`\<`InstanceType`\<`V`>>
new Volume instance.
### destroy()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
static destroy(volumeId: string, opts?: ConnectionOpts): Promise
```
Destroy a volume.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `volumeId` | `string` | volume ID. |
| `opts`? | `ConnectionOpts` | connection options. |
###### Returns
`Promise`\<`boolean`>
### getInfo()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
static getInfo(volumeId: string, opts?: ConnectionOpts): Promise
```
Get volume information.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `volumeId` | `string` | volume ID. |
| `opts`? | `ConnectionOpts` | connection options. |
###### Returns
`Promise`\<`VolumeAndToken`>
volume information.
### list()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
static list(opts?: ConnectionOpts): Promise
```
List all volumes.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `opts`? | `ConnectionOpts` | connection options. |
###### Returns
`Promise`\<`VolumeInfo`\[]>
list of volume information.
***
### VolumeConnectionConfig
#### Constructors
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
new VolumeConnectionConfig(volume: Volume, opts?: VolumeApiOpts): VolumeConnectionConfig
```
###### Parameters
| Parameter | Type |
| - | - |
| `volume` | `Volume` |
| `opts`? | `VolumeApiOpts` |
###### Returns
`VolumeConnectionConfig`
#### Properties
| Property | Modifier | Type |
| - | - | - |
| `apiUrl` | `readonly` | `string` |
| `debug` | `readonly` | `boolean` |
| `domain` | `readonly` | `string` |
| `headers?` | `readonly` | `Record`\<`string`, `string`> |
| `httpVersion` | `readonly` | `HttpVersion` |
| `logger?` | `readonly` | `Logger` |
| `proxy?` | `readonly` | `string` |
| `requestTimeoutMs` | `readonly` | `number` |
| `signal?` | `readonly` | `AbortSignal` |
| `token?` | `readonly` | `string` |
#### Methods
### getSignal()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
getSignal(requestTimeoutMs?: number, signal?: AbortSignal): undefined | AbortSignal
```
###### Parameters
| Parameter | Type |
| - | - |
| `requestTimeoutMs`? | `number` |
| `signal`? | `AbortSignal` |
###### Returns
`undefined` | `AbortSignal`
## Interfaces
### VolumeApiOpts
#### Properties
### domain?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional domain: string;
```
Domain to use for the volume API.
###### Default
E2B\_DOMAIN // environment variable or `e2b.app`
### headers?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional headers: Record;
```
Additional headers to send with the request.
### httpVersion?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional httpVersion: HttpVersion;
```
HTTP version for requests to the volume content API: `'1.1'` or `'2'`.
###### Default
E2B\_HTTP\_VERSION // environment variable or `'2'`
### logger?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional logger: Logger;
```
Logger to use for logging messages. It can accept any object that implements `Logger` interface—for example, console.
### proxy?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional proxy: string;
```
Proxy URL to use for requests.
###### Example
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
'http://user:pass@127.0.0.1:8080'
```
### requestTimeoutMs?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional requestTimeoutMs: number;
```
Timeout for requests to the API in **milliseconds**.
###### Default
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
60_000 // 60 seconds
```
### signal?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional signal: AbortSignal;
```
An optional `AbortSignal` that can be used to cancel the in-flight request.
When the signal is aborted, the underlying `fetch` is aborted and the
returned promise rejects with an `AbortError`.
### token?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional token: string;
```
E2B API key to use for authentication.
###### Default
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
E2B_API_KEY // environment variable
```
## Type Aliases
### VolumeAndToken
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
type VolumeAndToken = VolumeInfo & object;
```
Information about a volume and its auth token.
#### Type declaration
| Name | Type | Description |
| - | - | - |
| `domain`? | `string` | Domain to use as the destination for volume content requests, replacing the default `api.` host. Only set for teams connected to a custom (BYOC) cluster; `undefined` otherwise. |
| `token` | `string` | Volume auth token. |
***
### VolumeEntryStat
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
type VolumeEntryStat = Omit & object;
```
Volume entry stat with dates converted to Date objects.
#### Type declaration
| Name | Type | Description |
| - | - | - |
| `atime` | `Date` | Access time as a Date object. |
| `ctime` | `Date` | Creation time as a Date object. |
| `mtime` | `Date` | Modification time as a Date object. |
| `type` | `VolumeFileType` | File type. |
***
### VolumeInfo
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
type VolumeInfo = object;
```
Information about a volume.
#### Type declaration
| Name | Type | Description |
| - | - | - |
| `name` | `string` | Volume name. |
| `volumeId` | `string` | Volume ID. |
***
### ~~VolumeMetadataOptions~~
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
type VolumeMetadataOptions = VolumeMetadataOpts;
```
#### Deprecated
Use VolumeMetadataOpts instead.
***
### VolumeMetadataOpts
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
type VolumeMetadataOpts = object;
```
Options for updating file metadata.
#### Type declaration
| Name | Type | Description |
| - | - | - |
| `gid`? | `number` | Group ID of the file or directory. |
| `mode`? | `number` | Mode of the file or directory. |
| `uid`? | `number` | User ID of the file or directory. |
***
### VolumeReadOpts
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
type VolumeReadOpts = VolumeApiOpts & object;
```
Options for reading files from a volume.
#### Type declaration
| Name | Type | Description |
| - | - | - |
| `streamIdleTimeoutMs`? | `number` | Idle timeout for a streamed read (`format: 'stream'`) in **milliseconds**: abort if no chunk arrives from the server within this window *while reading*. It bounds only the wire — a slow or paused consumer never trips it (a consumer that holds the stream but stops reading is reclaimed server-side). Defaults to the request timeout; pass `0` to disable. |
***
### ~~VolumeWriteOptions~~
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
type VolumeWriteOptions = VolumeWriteOpts;
```
#### Deprecated
Use VolumeWriteOpts instead.
***
### VolumeWriteOpts
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
type VolumeWriteOpts = VolumeMetadataOpts & object;
```
Options for file and directory operations.
#### Type declaration
| Name | Type | Description |
| - | - | - |
| `force`? | `boolean` | For makeDir: Create parent directories if they don't exist. For writeFile: Force overwrite of an existing file. |
# Client
Source: https://docs.e2b.dev/sdk-reference/python-sdk/v2.56.0/client
## E2BClientParams
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
class E2BClientParams(ApiParams)
```
Params bound to an :class:`E2B` client, used as the defaults for every
call made through its resource classes. Same shape as :class:`ApiParams`.
## E2B
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
class E2B()
```
E2B client with an explicitly bound connection configuration.
The resource classes exposed by the client (`Sandbox`, `AsyncSandbox`,
`Volume`, `AsyncVolume`, `Template`, `AsyncTemplate`, `Secret`,
`AsyncSecret`) behave exactly like the top-level exports of the same name,
except the params passed to the client are used as the defaults instead of
the environment variables.
Per-call params still take precedence over the client's params.
Multiple clients are fully isolated from each other and from the top-level
env-configured exports.
**Example**:
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import E2B
client = E2B(api_key="e2b_...", domain="e2b.dev")
sandbox = client.Sandbox.create()
volumes = client.Volume.list()
exists = client.Template.exists("my-template")
```
### \_\_init\_\_
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def __init__(**opts: Unpack[E2BClientParams])
```
Create a new client with the API params bound to it.
**Arguments**:
* `opts`: API params used as the defaults for every call made
through this client's resource classes.
# Exceptions
Source: https://docs.e2b.dev/sdk-reference/python-sdk/v2.56.0/exceptions
### format\_sandbox\_unavailable\_exception
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def format_sandbox_unavailable_exception(message: str) -> Exception
```
Map a 502/UNAVAILABLE answered by the proxy in front of envd: the sandbox was
not found (killed or reached its timeout) is a `SandboxNotRunningException`;
the sandbox running but envd's port not open (e.g. its network is down) a
`SandboxUnreachableException`; any other message (e.g. envd ending a stream
because the connection to the sandbox was lost) a `SandboxUnreachableException`
too, with the sandbox state reported as unknown.
## SandboxException
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
class SandboxException(Exception)
```
Base class for all sandbox errors.
Raised when a general sandbox exception occurs.
**Arguments**:
* `status_code`: HTTP status of the API response that produced this error, when there was one.
## TimeoutException
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
class TimeoutException(SandboxException)
```
Raised when a timeout occurs.
The `unavailable` exception type is caused by sandbox timeout.
The `canceled` exception type is caused by exceeding request timeout.
The `deadline_exceeded` exception type is caused by exceeding the timeout for process, watch, etc.
The `unknown` exception type is sometimes caused by the sandbox timeout when the request is not processed correctly.
## InvalidArgumentException
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
class InvalidArgumentException(SandboxException)
```
Raised when an invalid argument is provided.
## NotEnoughSpaceException
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
class NotEnoughSpaceException(SandboxException)
```
Raised when there is not enough disk space.
## NotFoundException
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
class NotFoundException(SandboxException)
```
Raised when a resource is not found.
.. deprecated::
Use :class:`FileNotFoundException` or :class:`SandboxNotFoundException` instead.
This class will be removed in the next major version.
## FileNotFoundException
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
class FileNotFoundException(NotFoundException)
```
Raised when a file or directory is not found inside a sandbox.
## SandboxNotFoundException
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
class SandboxNotFoundException(NotFoundException)
```
Raised when a sandbox is not found (e.g. it doesn't exist or is no longer running).
## SandboxNotRunningException
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
class SandboxNotRunningException(TimeoutException)
```
Raised when the sandbox is not running anymore: the proxy in front of it
answered a request (or the health probe run after a request failed at the
connection level) with the sandbox not found -- it was killed, paused or
reached its timeout. Retrying the request will not help; create or resume a
sandbox.
The split from `SandboxUnreachableException` is only as good as the proxy's
answer and the health probe: a sandbox that is gone but could not be asked
about is a `SandboxUnreachableException`. Handlers that want every "sandbox
is gone" case should catch both this class and `SandboxNotFoundException`
(the control-plane API's answer for a sandbox that does not exist).
Subclass of `TimeoutException`, which this case surfaced as before.
## SandboxUnreachableException
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
class SandboxUnreachableException(TimeoutException)
```
Raised when the sandbox could not be reached while it is not confirmed to be
stopped: the proxy in front of the sandbox reports it running but envd's port
not open (e.g. the sandbox's network is down), the proxy or envd answered with
an unavailability the health probe could not resolve (e.g. the connection to
the sandbox was lost mid-stream while the probe found it running), or a
request failed at the connection level (the connection could not be
established or was dropped mid-request) and the follow-up health probe got no
answer from the sandbox either. A sandbox confirmed stopped (or paused) is a
`SandboxNotRunningException` -- but only as far as the probe can tell: a
sandbox that is gone and could not be asked about surfaces here.
This usually means the sandbox's network is down, envd inside it is not up
(yet), or a network issue between the client and the sandbox. Check the
sandbox state with `Sandbox.get_info()`.
Subclass of `TimeoutException`, which these cases surfaced as before.
## AuthenticationException
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
class AuthenticationException(Exception)
```
Raised when authentication fails.
## GitAuthException
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
class GitAuthException(AuthenticationException)
```
Raised when git authentication fails.
:deprecated: Run git with `sandbox.commands.run()` instead. The git module will be removed in the next major version.
## GitUpstreamException
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
class GitUpstreamException(SandboxException)
```
Raised when git upstream tracking is missing.
:deprecated: Run git with `sandbox.commands.run()` instead. The git module will be removed in the next major version.
## TemplateException
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
class TemplateException(SandboxException)
```
Exception raised when the template uses old envd version. It isn't compatible with the new SDK.
## RateLimitException
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
class RateLimitException(SandboxException)
```
Raised when the API rate limit is exceeded.
## ServiceBusyException
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
class ServiceBusyException(Exception)
```
Raised when the API refused the operation because the service is
temporarily busy (HTTP 503): no capacity to place a sandbox right now, or
the node running the sandbox declined a request it cannot serve yet.
Nothing was changed by the refused call: for example a refused pause
leaves the sandbox running with its state intact, so the same call can be
retried after a short wait.
Like `AuthenticationException` and unlike the other API errors, this is not
a `SandboxException`: it is raised for every 503 whatever the operation, so
catch it explicitly.
**Arguments**:
* `status_code`: HTTP status of the API response: always 503.
## BuildException
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
class BuildException(Exception)
```
Raised when the build fails.
## FileUploadException
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
class FileUploadException(BuildException)
```
Raised when the file upload fails.
## VolumeException
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
class VolumeException(Exception)
```
Base class for all volume errors.
Raised when general volume errors occur.
## VolumeNotFoundException
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
class VolumeNotFoundException(NotFoundException)
```
Raised when a volume is not found.
## VolumePathNotFoundException
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
class VolumePathNotFoundException(NotFoundException)
```
Raised when a file or directory is not found inside a volume.
## SecretException
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
class SecretException(Exception)
```
Base class for all secret errors.
Raised when general secret errors occur.
## SecretNotFoundException
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
class SecretNotFoundException(SecretException)
```
Raised when a secret is not found.
# Logger
Source: https://docs.e2b.dev/sdk-reference/python-sdk/v2.56.0/logger
## LogEntry
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@dataclass
class LogEntry()
```
Represents a single log entry from the template build process.
## LogEntryStart
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@dataclass
class LogEntryStart(LogEntry)
```
Special log entry indicating the start of a build process.
## LogEntryEnd
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@dataclass
class LogEntryEnd(LogEntry)
```
Special log entry indicating the end of a build process.
### TIMER\_UPDATE\_INTERVAL\_MS
Default minimum log level to display.
### DEFAULT\_LEVEL
Colored labels for each log level.
### levels
Numeric ordering of log levels for comparison (lower = less severe).
### set\_interval
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def set_interval(func, interval)
```
Returns a stop function that can be called to cancel the interval.
Similar to JavaScript's setInterval.
**Arguments**:
* `func`: Function to execute at each interval
* `interval`: Interval duration in **seconds**
**Returns**:
Stop function that can be called to cancel the interval
### default\_build\_logger
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def default_build_logger(
min_level: Optional[LogEntryLevel] = None
) -> Callable[[LogEntry], None]
```
Create a default build logger with animated timer display.
**Arguments**:
* `min_level`: Minimum log level to display (default: 'info')
**Returns**:
Logger function that accepts LogEntry instances
Example
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Template, default_build_logger
template = Template().from_python_image()
```
# Readycmd
Source: https://docs.e2b.dev/sdk-reference/python-sdk/v2.56.0/readycmd
## ReadyCmd
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
class ReadyCmd()
```
Wrapper class for ready check commands.
### wait\_for\_port
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def wait_for_port(port: int)
```
Wait for a port to be listening.
Uses `ss` command to check if a port is open and listening.
**Arguments**:
* `port`: Port number to wait for
**Returns**:
ReadyCmd that checks for the port
Example
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Template, wait_for_port
template = (
Template()
.from_python_image()
.set_start_cmd('python -m http.server 8000', wait_for_port(8000))
)
```
### wait\_for\_url
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def wait_for_url(url: str, status_code: int = 200)
```
Wait for a URL to return a specific HTTP status code.
Uses `curl` to make HTTP requests and check the response status.
**Arguments**:
* `url`: URL to check (e.g., '[http://localhost:3000/health](http://localhost:3000/health)')
* `status_code`: Expected HTTP status code (default: 200)
**Returns**:
ReadyCmd that checks the URL
Example
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Template, wait_for_url
template = (
Template()
.from_node_image()
.set_start_cmd('npm start', wait_for_url('http://localhost:3000/health'))
)
```
### wait\_for\_process
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def wait_for_process(process_name: str)
```
Wait for a process with a specific name to be running.
Uses `pgrep` to check if a process exists.
**Arguments**:
* `process_name`: Name of the process to wait for
**Returns**:
ReadyCmd that checks for the process
Example
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Template, wait_for_process
template = (
Template()
.from_base_image()
.set_start_cmd('./my-daemon', wait_for_process('my-daemon'))
)
```
### wait\_for\_file
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def wait_for_file(filename: str)
```
Wait for a file to exist.
Uses shell test command to check file existence.
**Arguments**:
* `filename`: Path to the file to wait for
**Returns**:
ReadyCmd that checks for the file
Example
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Template, wait_for_file
template = (
Template()
.from_base_image()
.set_start_cmd('./init.sh', wait_for_file('/tmp/ready'))
)
```
### wait\_for\_timeout
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def wait_for_timeout(timeout: int)
```
Wait for a specified timeout before considering the sandbox ready.
Uses `sleep` command to wait for a fixed duration.
**Arguments**:
* `timeout`: Time to wait in **milliseconds** (minimum: 1000ms / 1 second)
**Returns**:
ReadyCmd that waits for the specified duration
Example
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Template, wait_for_timeout
template = (
Template()
.from_node_image()
.set_start_cmd('npm start', wait_for_timeout(5000)) # Wait 5 seconds
)
```
# Sandbox async
Source: https://docs.e2b.dev/sdk-reference/python-sdk/v2.56.0/sandbox_async
## AsyncSandbox
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
class AsyncSandbox(SandboxApi)
```
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 isolated code
* Access the internet
Check docs [here](https://e2b.dev/docs).
Use the `AsyncSandbox.create()` to create a new sandbox.
**Example**:
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import AsyncSandbox
sandbox = await AsyncSandbox.create()
```
### files
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@property
def files() -> Filesystem
```
Module for interacting with the sandbox filesystem.
### commands
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@property
def commands() -> Commands
```
Module for running commands in the sandbox.
### pty
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@property
def pty() -> Pty
```
Module for interacting with the sandbox pseudo-terminal.
### git
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@property
def git() -> Git
```
Module for running git operations in the sandbox.
:deprecated: Run git with `sandbox.commands.run()` instead. The git module will be removed in the next major version.
### \_\_init\_\_
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def __init__(**opts: Unpack[SandboxOpts])
```
:deprecated: This constructor is deprecated
Use `AsyncSandbox.create()` to create a new sandbox instead.
### is\_running
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
async def is_running(request_timeout: Optional[float] = None) -> bool
```
Check if the sandbox is running.
**Arguments**:
* `request_timeout`: Timeout for the request in **seconds**
**Returns**:
`True` if the sandbox is running, `False` otherwise
Example
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
sandbox = await AsyncSandbox.create()
await sandbox.is_running() # Returns True
await sandbox.kill()
await sandbox.is_running() # Returns False
```
### create
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@classmethod
async def create(cls,
template: Optional[str] = None,
timeout: Optional[int] = None,
metadata: Optional[Dict[str, str]] = None,
envs: Optional[Dict[str, str]] = None,
secure: Optional[bool] = None,
allow_internet_access: Optional[bool] = None,
mcp: Optional[McpServer] = None,
network: Optional[SandboxNetworkOpts] = None,
iam: Optional[SandboxIamOpts] = None,
lifecycle: Optional[SandboxLifecycle] = None,
volume_mounts: Optional[SandboxAsyncVolumeMount] = None,
logger: Optional[logging.Logger] = None,
**opts: Unpack[ApiParams]) -> Self
```
Create a new sandbox.
By default, the sandbox is created from the default `base` sandbox template.
**Arguments**:
* `template`: Sandbox template name or ID
* `timeout`: Timeout for the sandbox in **seconds**. The maximum time a sandbox can be kept alive is 24 hours (86\_400 seconds) for Pro users and 1 hour (3\_600 seconds) for Hobby users.
* `metadata`: Custom metadata for the sandbox
* `envs`: Custom environment variables for the sandbox
* `secure`: Deprecated — every sandbox secures envd access; accepted for backward compatibility and ignored
* `allow_internet_access`: Allow sandbox to access the internet. If set to `False`, it works the same as setting network `deny_out` to `[0.0.0.0/0]`.
* `mcp`: MCP server to enable in the sandbox
* `network`: Sandbox network configuration. `allow_out`/`deny_out` may also be a callable receiving a :class:`SandboxNetworkSelectorContext` (`ctx.all_traffic`, `ctx.rules`) and returning a list of strings. Per-host transform rules are nested under `network.rules`; a rule's `transform` may be a callable receiving a :class:`SandboxNetworkTransformContext` of placeholder strings (`ctx.iam.tokens[name]`).
* `iam`: Sandbox workload identity configuration. A non-empty `tokens` map enables workload identity for the sandbox; token definitions can be created with :meth:`Secret.iam_token`. Example: `{"tokens": {"aws": Secret.iam_token(audience="sts.amazonaws.com", token_type="JWT-SVID")}}`. Registered tokens are exposed to `network.rules` `transform` callables as `ctx.iam.tokens[name]` placeholders, which the egress proxy resolves per request
* `lifecycle`: Sandbox lifecycle configuration — `on_timeout`: `"kill"` or `"pause"` (omitted from the request when unset, leaving the API's default, currently `"kill"`, in effect), or an object `{"action": "pause"|"kill", "mode": "full"|"filesystem"}` where `mode` set to `"filesystem"` makes a timeout auto-pause filesystem-only (cold-boots on resume; cannot be combined with `auto_resume`); an omitted `mode` leaves the snapshot kind to the API (`keep_memory` is a deprecated alias); `auto_resume`: leave unset to let the API pick the behavior, set `False` to opt out explicitly, or `True` (only when `on_timeout` action is `"pause"`). Example: `{"on_timeout": {"action": "pause", "mode": "filesystem"}}`
* `volume_mounts`: Dictionary mapping mount paths to AsyncVolume instances or volume names
* `logger`: Logger used for request and response logging for this sandbox. Accepts any standard library `logging.Logger`. When omitted, no request/response logging is emitted.
**Returns**:
A Sandbox instance for the new sandbox
Use this method instead of using the constructor to create a new sandbox.
### connect
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@overload
async def connect(timeout: Optional[int] = None,
*,
on_resume: SandboxOnResume = "restore",
**opts: Unpack[ApiParams]) -> Self
```
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).
**Arguments**:
* `timeout`: Timeout for the sandbox in **seconds**
For running sandboxes, the timeout will update only if the new timeout is longer than the existing one.
* `on_resume`: `"restore"` (the default) restores the memory snapshot; `"reboot"`
cold-boots from disk state, so writes not flushed before the pause may be lost.
Rejected where filesystem-only resume is not enabled; a no-op for a snapshot
without memory or a sandbox that is already running. A value outside the two
literals raises `InvalidArgumentException`.
Needs a control plane that knows this option: E2B Cloud, or a self-hosted or BYOC
deployment built from `e2b-dev/runtime` at or after the commit that added the `memory`
field to connect/resume (2026-08-20). An older control plane drops the field and
restores memory while answering as if the request had succeeded.
**Returns**:
A running sandbox instance
@example
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
sandbox = await AsyncSandbox.create()
await sandbox.pause()
same_sandbox = await sandbox.connect()
```
### connect
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@overload
@staticmethod
async def connect(sandbox_id: str,
timeout: Optional[int] = None,
logger: Optional[logging.Logger] = None,
*,
on_resume: SandboxOnResume = "restore",
**opts: Unpack[ApiParams]) -> "AsyncSandbox"
```
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).
**Arguments**:
* `sandbox_id`: Sandbox ID
* `timeout`: Timeout for the sandbox in **seconds**
For running sandboxes, the timeout will update only if the new timeout is longer than the existing one.
* `logger`: Logger used for request and response logging for this sandbox. Accepts any standard library `logging.Logger`. When omitted, no request/response logging is emitted.
* `on_resume`: `"restore"` (the default) restores the memory snapshot; `"reboot"`
cold-boots from disk state, so writes not flushed before the pause may be lost.
Rejected where filesystem-only resume is not enabled; a no-op for a snapshot
without memory or a sandbox that is already running. A value outside the two
literals raises `InvalidArgumentException`.
Needs a control plane that knows this option: E2B Cloud, or a self-hosted or BYOC
deployment built from `e2b-dev/runtime` at or after the commit that added the `memory`
field to connect/resume (2026-08-20). An older control plane drops the field and
restores memory while answering as if the request had succeeded.
**Returns**:
A running sandbox instance
@example
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
sandbox = await AsyncSandbox.create()
await AsyncSandbox.pause(sandbox.sandbox_id)
same_sandbox = await AsyncSandbox.connect(sandbox.sandbox_id)
```
### connect
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@class_method_variant("_cls_connect_sandbox")
async def connect(timeout: Optional[int] = None,
*,
on_resume: SandboxOnResume = "restore",
**opts: Unpack[ApiParams]) -> Self
```
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).
**Arguments**:
* `timeout`: Timeout for the sandbox in **seconds**
For running sandboxes, the timeout will update only if the new timeout is longer than the existing one.
* `on_resume`: `"restore"` (the default) restores the memory snapshot; `"reboot"`
cold-boots from disk state, so writes not flushed before the pause may be lost.
Rejected where filesystem-only resume is not enabled; a no-op for a snapshot
without memory or a sandbox that is already running. A value outside the two
literals raises `InvalidArgumentException`.
Needs a control plane that knows this option: E2B Cloud, or a self-hosted or BYOC
deployment built from `e2b-dev/runtime` at or after the commit that added the `memory`
field to connect/resume (2026-08-20). An older control plane drops the field and
restores memory while answering as if the request had succeeded.
**Returns**:
A running sandbox instance
@example
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
sandbox = await AsyncSandbox.create()
await sandbox.pause()
same_sandbox = await sandbox.connect()
```
### fork
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@overload
async def fork(timeout: Optional[int] = None,
count: Optional[int] = None,
**opts: Unpack[ApiParams]) -> List[Union[Self, Exception]]
```
Fork the sandbox.
The sandbox is checkpointed in place (briefly paused, snapshotted with
its full memory state, and resumed — its ID and expiration stay
untouched) and `count` new sandboxes are created from that snapshot.
All forks boot from the same snapshot, so the snapshot is captured once
regardless of count.
Each fork succeeds or fails independently — the returned list contains
one entry per requested fork, either a running `AsyncSandbox` instance
or an exception describing why that fork failed to start. Per-fork
error codes map to the same exception classes as other API errors
(e.g. 429 to `RateLimitException`).
**Arguments**:
* `timeout`: Timeout for the forked sandboxes in **seconds**.
* `count`: Number of forked sandboxes to create. An integer between 1 and 20. Omitted, the API default applies.
**Returns**:
List with one entry per requested fork — a sandbox instance or an exception
@example
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
sandbox = await AsyncSandbox.create()
fork1, fork2 = await sandbox.fork(count=2)
```
### fork
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@overload
@staticmethod
async def fork(
sandbox_id: str,
timeout: Optional[int] = None,
count: Optional[int] = None,
logger: Optional[logging.Logger] = None,
**opts: Unpack[ApiParams]) -> List[Union["AsyncSandbox", Exception]]
```
Fork a running sandbox specified by sandbox ID.
The sandbox is checkpointed in place (briefly paused, snapshotted with
its full memory state, and resumed — its ID and expiration stay
untouched) and `count` new sandboxes are created from that snapshot.
All forks boot from the same snapshot, so the snapshot is captured once
regardless of count.
Each fork succeeds or fails independently — the returned list contains
one entry per requested fork, either a running `AsyncSandbox` instance
or an exception describing why that fork failed to start. Per-fork
error codes map to the same exception classes as other API errors
(e.g. 429 to `RateLimitException`).
**Arguments**:
* `sandbox_id`: Sandbox ID
* `timeout`: Timeout for the forked sandboxes in **seconds**.
* `count`: Number of forked sandboxes to create. An integer between 1 and 20. Omitted, the API default applies.
* `logger`: Logger used for request and response logging for the forked sandboxes. Accepts any standard library `logging.Logger`. When omitted, no request/response logging is emitted.
**Returns**:
List with one entry per requested fork — a sandbox instance or an exception
@example
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
sandbox = await AsyncSandbox.create()
fork1, fork2 = await AsyncSandbox.fork(sandbox.sandbox_id, count=2)
```
### fork
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@class_method_variant("_cls_fork_sandbox")
async def fork(timeout: Optional[int] = None,
count: Optional[int] = None,
**opts: Unpack[ApiParams]) -> List[Union[Self, Exception]]
```
Fork the sandbox.
The sandbox is checkpointed in place (briefly paused, snapshotted with
its full memory state, and resumed — its ID and expiration stay
untouched) and `count` new sandboxes are created from that snapshot.
All forks boot from the same snapshot, so the snapshot is captured once
regardless of count.
Each fork succeeds or fails independently — the returned list contains
one entry per requested fork, either a running `AsyncSandbox` instance
or an exception describing why that fork failed to start. Per-fork
error codes map to the same exception classes as other API errors
(e.g. 429 to `RateLimitException`).
**Arguments**:
* `timeout`: Timeout for the forked sandboxes in **seconds**.
* `count`: Number of forked sandboxes to create. An integer between 1 and 20. Omitted, the API default applies.
**Returns**:
List with one entry per requested fork — a sandbox instance or an exception
@example
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
sandbox = await AsyncSandbox.create()
fork1, fork2 = await sandbox.fork(count=2)
```
### kill
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@overload
async def kill(**opts: Unpack[ApiParams]) -> bool
```
Kill the sandbox.
**Returns**:
`True` if the sandbox was killed, `False` if the sandbox was not found
### kill
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@overload
@staticmethod
async def kill(sandbox_id: str, **opts: Unpack[ApiParams]) -> bool
```
Kill the sandbox specified by sandbox ID.
**Arguments**:
* `sandbox_id`: Sandbox ID
**Returns**:
`True` if the sandbox was killed, `False` if the sandbox was not found
### kill
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@class_method_variant("_cls_kill")
async def kill(**opts: Unpack[ApiParams]) -> bool
```
Kill the sandbox specified by sandbox ID.
**Returns**:
`True` if the sandbox was killed, `False` if the sandbox was not found
### set\_timeout
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@overload
async def set_timeout(timeout: int, **opts: Unpack[ApiParams]) -> None
```
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 `.set_timeout`.
The maximum time a sandbox can be kept alive is 24 hours (86\_400 seconds) for Pro users and 1 hour (3\_600 seconds) for Hobby users.
**Arguments**:
* `timeout`: Timeout for the sandbox in **seconds**
### set\_timeout
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@overload
@staticmethod
async def set_timeout(sandbox_id: str, timeout: int,
**opts: Unpack[ApiParams]) -> None
```
Set the timeout of the specified sandbox.
This method can extend or reduce the sandbox timeout set when creating the sandbox or from the last call to `.set_timeout`.
The maximum time a sandbox can be kept alive is 24 hours (86\_400 seconds) for Pro users and 1 hour (3\_600 seconds) for Hobby users.
**Arguments**:
* `sandbox_id`: Sandbox ID
* `timeout`: Timeout for the sandbox in **seconds**
### set\_timeout
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@class_method_variant("_cls_set_timeout")
async def set_timeout(timeout: int, **opts: Unpack[ApiParams]) -> None
```
Set the timeout of the specified sandbox.
This method can extend or reduce the sandbox timeout set when creating the sandbox or from the last call to `.set_timeout`.
The maximum time a sandbox can be kept alive is 24 hours (86\_400 seconds) for Pro users and 1 hour (3\_600 seconds) for Hobby users.
**Arguments**:
* `timeout`: Timeout for the sandbox in **seconds**
### update\_network
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@overload
async def update_network(network: SandboxNetworkUpdate,
**opts: Unpack[ApiParams]) -> None
```
Update the network configuration of the sandbox.
Replaces the current egress configuration atomically — fields that are
omitted are cleared on the server.
**Arguments**:
* `network`: New network configuration.
### update\_network
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@overload
@staticmethod
async def update_network(sandbox_id: str, network: SandboxNetworkUpdate,
**opts: Unpack[ApiParams]) -> None
```
Update the network configuration of the sandbox specified by sandbox ID.
Replaces the current egress configuration atomically — fields that are
omitted are cleared on the server.
**Arguments**:
* `sandbox_id`: Sandbox ID.
* `network`: New network configuration.
### update\_network
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@class_method_variant("_cls_update_network")
async def update_network(network: SandboxNetworkUpdate,
**opts: Unpack[ApiParams]) -> None
```
Update the network configuration of the sandbox.
Replaces the current egress configuration atomically — fields that are
omitted are cleared on the server.
**Arguments**:
* `network`: New network configuration.
### get\_info
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@overload
async def get_info(**opts: Unpack[ApiParams]) -> SandboxInfo
```
Get sandbox information like sandbox ID, template, metadata, started at/end at date.
**Returns**:
Sandbox info
### get\_info
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@overload
@staticmethod
async def get_info(sandbox_id: str, **opts: Unpack[ApiParams]) -> SandboxInfo
```
Get sandbox information like sandbox ID, template, metadata, started at/end at date.
**Arguments**:
* `sandbox_id`: Sandbox ID
**Returns**:
Sandbox info
### get\_info
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@class_method_variant("_cls_get_info")
async def get_info(**opts: Unpack[ApiParams]) -> SandboxInfo
```
Get sandbox information like sandbox ID, template, metadata, started at/end at date.
**Returns**:
Sandbox info
### get\_metrics
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@overload
async def get_metrics(start: Optional[datetime.datetime] = None,
end: Optional[datetime.datetime] = None,
**opts: Unpack[ApiParams]) -> List[SandboxMetrics]
```
Get the metrics of the current sandbox.
**Arguments**:
* `start`: Start time for the metrics, defaults to the start of the sandbox
* `end`: End time for the metrics, defaults to the current time
**Returns**:
List of sandbox metrics containing CPU, memory and disk usage information
### get\_metrics
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@overload
@staticmethod
async def get_metrics(sandbox_id: str,
start: Optional[datetime.datetime] = None,
end: Optional[datetime.datetime] = None,
**opts: Unpack[ApiParams]) -> List[SandboxMetrics]
```
Get the metrics of the sandbox specified by sandbox ID.
**Arguments**:
* `sandbox_id`: Sandbox ID
* `start`: Start time for the metrics, defaults to the start of the sandbox
* `end`: End time for the metrics, defaults to the current time
**Returns**:
List of sandbox metrics containing CPU, memory and disk usage information
### get\_metrics
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@class_method_variant("_cls_get_metrics")
async def get_metrics(start: Optional[datetime.datetime] = None,
end: Optional[datetime.datetime] = None,
**opts: Unpack[ApiParams]) -> List[SandboxMetrics]
```
Get the metrics of the current sandbox.
**Arguments**:
* `start`: Start time for the metrics, defaults to the start of the sandbox
* `end`: End time for the metrics, defaults to the current time
**Returns**:
List of sandbox metrics containing CPU, memory and disk usage information
### pause
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@overload
async def pause(keep_memory: Optional[bool] = None,
*,
mode: Optional[SnapshotMode] = None,
**opts: Unpack[ApiParams]) -> bool
```
Pause the sandbox.
**Arguments**:
* `keep_memory`: :deprecated: Use `mode` instead: `keep_memory=False` is `mode="filesystem"`, `keep_memory=True` is `mode="full"`.
* `mode`: What the pause persists. With `"filesystem"`, the in-memory state is dropped and only the filesystem is persisted; resuming such a sandbox cold-boots (reboots) it from disk, losing running processes and open connections. Defaults to `"full"` (memory and filesystem).
**Returns**:
`True` if the sandbox got paused, `False` if the sandbox was already paused
### pause
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@overload
@staticmethod
async def pause(sandbox_id: str,
keep_memory: Optional[bool] = None,
*,
mode: Optional[SnapshotMode] = None,
**opts: Unpack[ApiParams]) -> bool
```
Pause the sandbox specified by sandbox ID.
**Arguments**:
* `sandbox_id`: Sandbox ID
* `keep_memory`: :deprecated: Use `mode` instead: `keep_memory=False` is `mode="filesystem"`, `keep_memory=True` is `mode="full"`.
* `mode`: What the pause persists. With `"filesystem"`, the in-memory state is dropped and only the filesystem is persisted; resuming such a sandbox cold-boots (reboots) it from disk, losing running processes and open connections. Defaults to `"full"` (memory and filesystem).
**Returns**:
`True` if the sandbox got paused, `False` if the sandbox was already paused
### pause
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@class_method_variant("_cls_pause")
async def pause(keep_memory: Optional[bool] = None,
*,
mode: Optional[SnapshotMode] = None,
**opts: Unpack[ApiParams]) -> bool
```
Pause the sandbox.
**Arguments**:
* `keep_memory`: :deprecated: Use `mode` instead: `keep_memory=False` is `mode="filesystem"`, `keep_memory=True` is `mode="full"`.
* `mode`: What the pause persists. With `"filesystem"`, the in-memory state is dropped and only the filesystem is persisted; resuming such a sandbox cold-boots (reboots) it from disk, losing running processes and open connections. Defaults to `"full"` (memory and filesystem).
**Returns**:
`True` if the sandbox got paused, `False` if the sandbox was already paused
### beta\_pause
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@class_method_variant("_cls_pause")
async def beta_pause(keep_memory: Optional[bool] = None,
*,
mode: Optional[SnapshotMode] = None,
**opts: Unpack[ApiParams]) -> bool
```
**Returns**:
`True` if the sandbox got paused, `False` if the sandbox was already paused
### create\_snapshot
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@overload
async def create_snapshot(name: Optional[str] = None,
*,
mode: Optional[SnapshotMode] = None,
**opts: Unpack[ApiParams]) -> SnapshotInfo
```
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 `snapshot_id` with `AsyncSandbox.create(snapshot_id)` to create a new sandbox from the snapshot.
**Arguments**:
* `name`: Optional name for the snapshot template. If a snapshot template with this name already exists, a new build will be assigned to the existing template instead of creating a new one.
* `mode`: What the snapshot persists. With `"filesystem"`, only the filesystem is persisted: the snapshot is smaller and faster to take, and sandboxes created from it cold-boot (start fresh from disk) instead of restoring memory, so they begin without the running processes, in-memory state and open connections of the source sandbox. The source sandbox keeps running either way. Defaults to `"full"` (memory and filesystem).
**Raises**:
* `SandboxException`: when the API refuses a filesystem-only snapshot (`mode="filesystem"`). `status_code` 400 means the feature is not enabled for the team (`snapshot_filesystem_only_disabled`); 409 means the sandbox's node runs an orchestrator that predates the option (`snapshot_filesystem_only_unsupported_node`). A full memory snapshot still works in both cases; for 409, pause and resume the sandbox and retry.
**Returns**:
Snapshot information including the snapshot ID and names
### create\_snapshot
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@overload
@staticmethod
async def create_snapshot(sandbox_id: str,
name: Optional[str] = None,
*,
mode: Optional[SnapshotMode] = None,
**opts: Unpack[ApiParams]) -> SnapshotInfo
```
Create a snapshot from the sandbox specified by sandbox ID.
The sandbox will be paused while the snapshot is being created.
**Arguments**:
* `sandbox_id`: Sandbox ID
* `name`: Optional name for the snapshot template. If a snapshot template with this name already exists, a new build will be assigned to the existing template instead of creating a new one.
* `mode`: What the snapshot persists. With `"filesystem"`, only the filesystem is persisted: the snapshot is smaller and faster to take, and sandboxes created from it cold-boot (start fresh from disk) instead of restoring memory, so they begin without the running processes, in-memory state and open connections of the source sandbox. The source sandbox keeps running either way. Defaults to `"full"` (memory and filesystem).
**Raises**:
* `SandboxException`: when the API refuses a filesystem-only snapshot (`mode="filesystem"`). `status_code` 400 means the feature is not enabled for the team (`snapshot_filesystem_only_disabled`); 409 means the sandbox's node runs an orchestrator that predates the option (`snapshot_filesystem_only_unsupported_node`). A full memory snapshot still works in both cases; for 409, pause and resume the sandbox and retry.
**Returns**:
Snapshot information including the snapshot ID and names
### create\_snapshot
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@class_method_variant("_cls_create_snapshot")
async def create_snapshot(name: Optional[str] = None,
*,
mode: Optional[SnapshotMode] = None,
**opts: Unpack[ApiParams]) -> SnapshotInfo
```
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 `snapshot_id` with `AsyncSandbox.create(snapshot_id)` to create a new sandbox from the snapshot.
**Arguments**:
* `name`: Optional name for the snapshot template. If a snapshot template with this name already exists, a new build will be assigned to the existing template instead of creating a new one.
* `mode`: What the snapshot persists. With `"filesystem"`, only the filesystem is persisted: the snapshot is smaller and faster to take, and sandboxes created from it cold-boot (start fresh from disk) instead of restoring memory, so they begin without the running processes, in-memory state and open connections of the source sandbox. The source sandbox keeps running either way. Defaults to `"full"` (memory and filesystem).
**Raises**:
* `SandboxException`: when the API refuses a filesystem-only snapshot (`mode="filesystem"`). `status_code` 400 means the feature is not enabled for the team (`snapshot_filesystem_only_disabled`); 409 means the sandbox's node runs an orchestrator that predates the option (`snapshot_filesystem_only_unsupported_node`). A full memory snapshot still works in both cases; for 409, pause and resume the sandbox and retry.
**Returns**:
Snapshot information including the snapshot ID and names
### list\_snapshots
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@overload
def list_snapshots(limit: Optional[int] = None,
next_token: Optional[str] = None,
name: Optional[str] = None,
**opts: Unpack[ApiParams]) -> AsyncSnapshotPaginator
```
List snapshots for this sandbox.
**Arguments**:
* `limit`: Maximum number of snapshots to return per page
* `next_token`: Token for pagination
* `name`: Filter snapshots by name or ID, optionally tag-qualified (e.g. "my-snapshot", "my-project/my-snapshot" or "my-snapshot:v1")
**Returns**:
Paginator for listing snapshots
### list\_snapshots
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@overload
@staticmethod
def list_snapshots(sandbox_id: Optional[str] = None,
limit: Optional[int] = None,
next_token: Optional[str] = None,
name: Optional[str] = None,
**opts: Unpack[ApiParams]) -> AsyncSnapshotPaginator
```
List all snapshots.
**Arguments**:
* `sandbox_id`: Filter snapshots by source sandbox ID
* `limit`: Maximum number of snapshots to return per page
* `next_token`: Token for pagination
* `name`: Filter snapshots by name or ID, optionally tag-qualified (e.g. "my-snapshot", "my-project/my-snapshot" or "my-snapshot:v1")
**Returns**:
Paginator for listing snapshots
### list\_snapshots
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@class_method_variant("_cls_list_snapshots")
def list_snapshots(limit: Optional[int] = None,
next_token: Optional[str] = None,
name: Optional[str] = None,
**opts: Unpack[ApiParams]) -> AsyncSnapshotPaginator
```
List snapshots for this sandbox.
**Arguments**:
* `limit`: Maximum number of snapshots to return per page
* `next_token`: Token for pagination
* `name`: Filter snapshots by name or ID, optionally tag-qualified (e.g. "my-snapshot", "my-project/my-snapshot" or "my-snapshot:v1")
**Returns**:
Paginator for listing snapshots
### delete\_snapshot
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@classmethod
async def delete_snapshot(cls, snapshot_id: str,
**opts: Unpack[ApiParams]) -> bool
```
Delete a snapshot.
**Arguments**:
* `snapshot_id`: Snapshot ID
**Returns**:
`True` if the snapshot was deleted, `False` if it was not found
### get\_mcp\_token
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
async def get_mcp_token() -> Optional[str]
```
Get the MCP token for the sandbox.
**Returns**:
MCP token for the sandbox, or None if MCP is not enabled.
## AsyncSandboxPaginator
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
class AsyncSandboxPaginator(SandboxPaginatorBase)
```
Paginator for listing sandboxes.
**Example**:
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
paginator = AsyncSandbox.list()
while paginator.has_next:
sandboxes = await paginator.next_items()
print(sandboxes)
```
### next\_items
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
async def next_items(**opts: Unpack[ApiParams]) -> List[SandboxInfo]
```
Returns the next page of sandboxes.
Call this method only if `has_next` is `True`, otherwise it will raise an exception.
**Arguments**:
* `opts`: Per-call connection options (e.g. `api_key`, `domain`,
`headers`, `request_timeout`). When provided, this call uses these
options on top of the ones the paginator was constructed with.
**Returns**:
List of sandboxes
## AsyncSnapshotPaginator
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
class AsyncSnapshotPaginator(SnapshotPaginatorBase)
```
Paginator for listing snapshots.
**Example**:
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
paginator = AsyncSandbox.list_snapshots()
while paginator.has_next:
snapshots = await paginator.next_items()
print(snapshots)
```
### next\_items
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
async def next_items(**opts: Unpack[ApiParams]) -> List[SnapshotInfo]
```
Returns the next page of snapshots.
Call this method only if `has_next` is `True`, otherwise it will raise an exception.
**Arguments**:
* `opts`: Per-call connection options (e.g. `api_key`, `domain`,
`headers`, `request_timeout`). When provided, this call uses these
options on top of the ones the paginator was constructed with.
**Returns**:
List of snapshots
## Filesystem
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
class Filesystem()
```
Module for interacting with the filesystem in the sandbox.
### read
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@overload
async def read(path: str,
format: Literal["text"] = "text",
user: Optional[Username] = None,
request_timeout: Optional[float] = None,
gzip: bool = False) -> str
```
Read file content as a `str`.
**Arguments**:
* `path`: Path to the file
* `user`: Run the operation as this user
* `format`: Format of the file content—`text` by default
* `request_timeout`: Timeout for the request in **seconds**
* `gzip`: Use gzip compression for the request
**Returns**:
File content as a `str`
### read
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@overload
async def read(path: str,
format: Literal["bytes"],
user: Optional[Username] = None,
request_timeout: Optional[float] = None,
gzip: bool = False) -> bytearray
```
Read file content as a `bytearray`.
**Arguments**:
* `path`: Path to the file
* `user`: Run the operation as this user
* `format`: Format of the file content—`bytes`
* `request_timeout`: Timeout for the request in **seconds**
* `gzip`: Use gzip compression for the request
**Returns**:
File content as a `bytearray`
### read
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@overload
async def read(
path: str,
format: Literal["stream"],
user: Optional[Username] = None,
request_timeout: Optional[float] = None,
gzip: bool = False,
stream_idle_timeout: Optional[float] = None) -> AsyncFileStreamReader
```
Read file content as an `AsyncFileStreamReader` (an `AsyncIterator[bytes]`).
A `request_timeout` set explicitly for this call is the deadline for
the whole transfer; by default the download is not bounded in total.
A stalled stream is reclaimed by `stream_idle_timeout` (raising
`httpx.ReadTimeout`). The reader releases its connection once fully
consumed; if you don't read it to the end, use it as an async context
manager or call `aclose()` for deterministic cleanup. There is no
garbage-collection safety net—an abandoned stream holds its
connection until the idle timeout fires or the client is closed.
**Arguments**:
* `path`: Path to the file
* `user`: Run the operation as this user
* `format`: Format of the file content—`stream`
* `request_timeout`: Deadline for the whole transfer in **seconds**
* `gzip`: Use gzip compression for the request
* `stream_idle_timeout`: Idle timeout in **seconds** for the streamed
body—abort if the response head or the next chunk doesn't arrive
within this window. Resets on every chunk, so it bounds a stalled
stream without limiting total transfer time. Defaults to a
transport-wide idle read timeout (60 seconds); pass `0` to
disable.
**Returns**:
File content as an `AsyncFileStreamReader`
### write
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
async def write(path: str,
data: Union[str, bytes, IO],
user: Optional[Username] = None,
request_timeout: Optional[float] = None,
gzip: bool = False,
use_octet_stream: Optional[bool] = None,
metadata: Optional[Dict[str, str]] = None) -> WriteInfo
```
Write content to a file on the path.
Writing to a file that doesn't exist creates the file.
Writing to a file that already exists overwrites the file.
Writing to a file at path that doesn't exist creates the necessary directories.
**Arguments**:
* `path`: Path to the file
* `data`: Data to write to the file, can be a `str`, `bytes`, or `IO`. File-like objects are streamed in chunks instead of being buffered in memory.
* `user`: Run the operation as this user
* `request_timeout`: Timeout for the request in **seconds**
* `gzip`: Use gzip compression for the upload. Implies the `application/octet-stream` upload. Requires envd 0.5.7 or later — when not supported, the upload falls back to uncompressed `multipart/form-data`.
* `use_octet_stream`: Upload using `application/octet-stream` instead of `multipart/form-data`. Defaults to `None`, which uses octet-stream when `data` is a file-like object (so streamed uploads aren't buffered) and `multipart/form-data` otherwise. Requires envd 0.5.7 or later — when not supported, the upload falls back to `multipart/form-data`, which reads text-mode file-like data into memory (httpx only streams binary file objects in a multipart body).
* `metadata`: User-defined metadata to persist on the uploaded file as extended attributes. Keys are lowercased by the sandbox; invalid keys or values raise an `InvalidArgumentException`. Requires envd 0.6.2 or later.
**Returns**:
Information about the written file
### write\_files
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
async def write_files(
files: List[WriteEntry],
user: Optional[Username] = None,
request_timeout: Optional[float] = None,
gzip: bool = False,
use_octet_stream: Optional[bool] = None,
metadata: Optional[Dict[str, str]] = None) -> List[WriteInfo]
```
Writes multiple files.
Writes a list of files to the filesystem.
When writing to a file that doesn't exist, the file will get created.
When writing to a file that already exists, the file will get overwritten.
When writing to a file at path that doesn't exist, the necessary directories will be created.
**Arguments**:
* `files`: list of files to write as `WriteEntry` objects, each containing `path` and `data`
* `user`: Run the operation as this user
* `request_timeout`: Timeout for the request
* `gzip`: Use gzip compression for the upload. Implies the `application/octet-stream` upload. Requires envd 0.5.7 or later — when not supported, the upload falls back to uncompressed `multipart/form-data`.
* `use_octet_stream`: Upload using `application/octet-stream` instead of `multipart/form-data`. Defaults to `None`, which uses octet-stream when any entry is a file-like object (so streamed uploads aren't buffered) and `multipart/form-data` otherwise. Requires envd 0.5.7 or later — when not supported, the upload falls back to `multipart/form-data`, which reads text-mode file-like data into memory (httpx only streams binary file objects in a multipart body).
* `metadata`: User-defined metadata to persist on each uploaded file as extended attributes; the same map is applied to every file. Keys are lowercased by the sandbox; invalid keys or values raise an `InvalidArgumentException`. Requires envd 0.6.2 or later.
**Returns**:
Information about the written files
### list
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
async def list(path: str,
depth: Optional[int] = 1,
user: Optional[Username] = None,
request_timeout: Optional[float] = None) -> List[EntryInfo]
```
List entries in a directory.
**Arguments**:
* `path`: Path to the directory
* `depth`: Depth of the directory to list
* `user`: Run the operation as this user
* `request_timeout`: Timeout for the request in **seconds**
**Returns**:
List of entries in the directory
### exists
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
async def exists(path: str,
user: Optional[Username] = None,
request_timeout: Optional[float] = None) -> bool
```
Check if a file or a directory exists.
**Arguments**:
* `path`: Path to a file or a directory
* `user`: Run the operation as this user
* `request_timeout`: Timeout for the request in **seconds**
**Returns**:
`True` if the file or directory exists, `False` otherwise
### get\_info
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
async def get_info(path: str,
user: Optional[Username] = None,
request_timeout: Optional[float] = None) -> EntryInfo
```
Get information about a file or directory.
**Arguments**:
* `path`: Path to a file or a directory
* `user`: Run the operation as this user
* `request_timeout`: Timeout for the request in **seconds**
**Returns**:
Information about the file or directory like name, type, and path
### remove
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
async def remove(path: str,
user: Optional[Username] = None,
request_timeout: Optional[float] = None) -> None
```
Remove a file or a directory.
**Arguments**:
* `path`: Path to a file or a directory
* `user`: Run the operation as this user
* `request_timeout`: Timeout for the request in **seconds**
### rename
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
async def rename(old_path: str,
new_path: str,
user: Optional[Username] = None,
request_timeout: Optional[float] = None) -> EntryInfo
```
Rename a file or directory.
**Arguments**:
* `old_path`: Path to the file or directory to rename
* `new_path`: New path to the file or directory
* `user`: Run the operation as this user
* `request_timeout`: Timeout for the request in **seconds**
**Returns**:
Information about the renamed file or directory
### make\_dir
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
async def make_dir(path: str,
user: Optional[Username] = None,
request_timeout: Optional[float] = None) -> bool
```
Create a new directory and all directories along the way if needed on the specified path.
**Arguments**:
* `path`: Path to a new directory. For example '/dirA/dirB' when creating 'dirB'.
* `user`: Run the operation as this user
* `request_timeout`: Timeout for the request in **seconds**
**Returns**:
`True` if the directory was created, `False` if the directory already exists
### watch\_dir
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
async def watch_dir(path: str,
on_event: OutputHandler[FilesystemEvent],
on_exit: Optional[OutputHandler[
Optional[Exception]]] = None,
user: Optional[Username] = None,
request_timeout: Optional[float] = None,
timeout: Optional[float] = 60,
recursive: bool = False,
include_entry: bool = False,
allow_network_mounts: bool = False) -> AsyncWatchHandle
```
Watch directory for filesystem events.
**Arguments**:
* `path`: Path to a directory to watch
* `on_event`: Callback to call on each event in the directory
* `on_exit`: Callback to call when the watching ends. It receives the error that ended the watch, or `None` on a clean end or when `stop()` is called
* `user`: Run the operation as this user
* `request_timeout`: Timeout for opening the stream in **seconds** — the wait until envd confirms with a start event. The running watch is bounded by `timeout`
* `timeout`: Timeout for the watch operation in **seconds**. Using `0` will not limit the watch time
* `recursive`: Watch directory recursively
* `include_entry`: Include the `EntryInfo` of the affected entry in each event, when available. Requires envd 0.6.3 or later
* `allow_network_mounts`: Allow watching paths on network filesystem mounts (NFS, CIFS, SMB, FUSE), which are rejected by default. Events on network mounts may be unreliable or not delivered at all. Requires envd 0.6.4 or later
**Returns**:
`AsyncWatchHandle` object for stopping watching directory
## AsyncWatchHandle
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
class AsyncWatchHandle()
```
Handle for watching a directory in the sandbox filesystem.
Use `.stop()` to stop watching the directory.
### stop
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
async def stop()
```
Stop watching the directory.
## SandboxApi
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
class SandboxApi(SandboxBase)
```
### list
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@classmethod
def list(cls,
query: Optional[SandboxQuery] = None,
limit: Optional[int] = None,
next_token: Optional[str] = None,
order: Optional[SandboxListOrder] = None,
**opts: Unpack[ApiParams]) -> AsyncSandboxPaginator
```
List sandboxes.
By default (no `query.state` set), returns sandboxes in both `running`
and `paused` states. To filter by state, pass `query=SandboxQuery(state=[...])`.
**Arguments**:
* `query`: Filter the list of sandboxes by metadata, state, start time, or template, e.g. `SandboxQuery(metadata={"key": "value"})` or `SandboxQuery(state=[SandboxState.RUNNING])`
* `limit`: Maximum number of sandboxes to return per page
* `next_token`: Token for pagination
* `order`: Sort order of the list of sandboxes by start time, applied across the whole result set before pagination (not within a page)
**Returns**:
An `AsyncSandboxPaginator` that yields pages of sandboxes (running and paused by default). Iterate pages via `await paginator.next_items()` while `paginator.has_next` is True.
## Pty
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
class Pty()
```
Module for interacting with PTYs (pseudo-terminals) in the sandbox.
### kill
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
async def kill(pid: int, request_timeout: Optional[float] = None) -> bool
```
Kill PTY.
**Arguments**:
* `pid`: Process ID of the PTY
* `request_timeout`: Timeout for the request in **seconds**
**Returns**:
`true` if the PTY was killed, `false` if the PTY was not found
### send\_stdin
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
async def send_stdin(pid: int,
data: bytes,
request_timeout: Optional[float] = None) -> None
```
Send input to a PTY.
**Arguments**:
* `pid`: Process ID of the PTY
* `data`: Input data to send
* `request_timeout`: Timeout for the request in **seconds**
### create
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
async def create(
size: PtySize,
on_data: OutputHandler[PtyOutput],
user: Optional[Username] = None,
cwd: Optional[str] = None,
envs: Optional[Dict[str, str]] = None,
timeout: Optional[float] = 60,
request_timeout: Optional[float] = None) -> AsyncCommandHandle
```
Start a new PTY (pseudo-terminal).
**Arguments**:
* `size`: Size of the PTY
* `on_data`: Callback to handle PTY data
* `user`: User to use for the PTY
* `cwd`: Working directory for the PTY
* `envs`: Environment variables for the PTY
* `timeout`: Timeout for the PTY in **seconds**
* `request_timeout`: Timeout for opening the stream in **seconds** — the wait until envd confirms with a start event. The running stream is bounded by `timeout`
**Returns**:
Handle to interact with the PTY
### connect
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
async def connect(
pid: int,
on_data: OutputHandler[PtyOutput],
timeout: Optional[float] = 60,
request_timeout: Optional[float] = None) -> AsyncCommandHandle
```
Connect to a running PTY.
**Arguments**:
* `pid`: Process ID of the PTY to connect to. You can get the list of running PTYs using `sandbox.pty.list()`.
* `on_data`: Callback to handle PTY data
* `timeout`: Timeout for the PTY connection in **seconds**. Using `0` will not limit the connection time
* `request_timeout`: Timeout for opening the stream in **seconds** — the wait until envd confirms with a start event. The running stream is bounded by `timeout`
**Returns**:
Handle to interact with the PTY
### resize
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
async def resize(pid: int,
size: PtySize,
request_timeout: Optional[float] = None) -> None
```
Resize PTY.
Call this when the terminal window is resized and the number of columns and rows has changed.
**Arguments**:
* `pid`: Process ID of the PTY
* `size`: New size of the PTY
* `request_timeout`: Timeout for the request in **seconds**
## Commands
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
class Commands()
```
Module for executing commands in the sandbox.
### list
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
async def list(request_timeout: Optional[float] = None) -> List[ProcessInfo]
```
Lists all running commands and PTY sessions.
**Arguments**:
* `request_timeout`: Timeout for the request in **seconds**
**Returns**:
List of running commands and PTY sessions
### kill
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
async def kill(pid: int, request_timeout: Optional[float] = None) -> bool
```
Kill a running command specified by its process ID.
It uses `SIGKILL` signal to kill the command.
**Arguments**:
* `pid`: Process ID of the command. You can get the list of processes using `sandbox.commands.list()`
* `request_timeout`: Timeout for the request in **seconds**
**Returns**:
`True` if the command was killed, `False` if the command was not found
### send\_stdin
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
async def send_stdin(pid: int,
data: Union[str, bytes],
request_timeout: Optional[float] = None) -> None
```
Send data to command stdin.
:param pid Process ID of the command. You can get the list of processes using `sandbox.commands.list()`.
:param data: Data to send to the command
:param request\_timeout: Timeout for the request in **seconds**
### close\_stdin
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
async def close_stdin(pid: int,
request_timeout: Optional[float] = None) -> None
```
Close the command stdin.
This signals EOF to the command. The command must have been started with `stdin=True`.
:param pid Process ID of the command. You can get the list of processes using `sandbox.commands.list()`.
:param request\_timeout: Timeout for the request in **seconds**
### run
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@overload
async def run(cmd: str,
background: Union[Literal[False], None] = None,
envs: Optional[Dict[str, str]] = None,
user: Optional[Username] = None,
cwd: Optional[str] = None,
on_stdout: Optional[OutputHandler[Stdout]] = None,
on_stderr: Optional[OutputHandler[Stderr]] = None,
stdin: Optional[bool] = None,
timeout: Optional[float] = 60,
request_timeout: Optional[float] = None) -> CommandResult
```
Start a new command and wait until it finishes executing.
**Arguments**:
* `cmd`: Command to execute
* `background`: **`False` if the command should be executed in the foreground**, `True` if the command should be executed in the background
* `envs`: Environment variables used for the command
* `user`: User to run the command as
* `cwd`: Working directory to run the command
* `on_stdout`: Callback for command stdout output
* `on_stderr`: Callback for command stderr output
* `stdin`: If `True`, the command will have a stdin stream that you can send data to using `sandbox.commands.send_stdin()`
* `timeout`: Timeout for the command connection in **seconds**. Using `0` will not limit the command connection time
* `request_timeout`: Timeout for opening the stream in **seconds** — the wait until envd confirms with a start event. The running stream is bounded by `timeout`
**Returns**:
`CommandResult` result of the command execution
### run
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@overload
async def run(cmd: str,
background: Literal[True],
envs: Optional[Dict[str, str]] = None,
user: Optional[Username] = None,
cwd: Optional[str] = None,
on_stdout: Optional[OutputHandler[Stdout]] = None,
on_stderr: Optional[OutputHandler[Stderr]] = None,
stdin: Optional[bool] = None,
timeout: Optional[float] = 60,
request_timeout: Optional[float] = None) -> AsyncCommandHandle
```
Start a new command and return a handle to interact with it.
**Arguments**:
* `cmd`: Command to execute
* `background`: `False` if the command should be executed in the foreground, **`True` if the command should be executed in the background**
* `envs`: Environment variables used for the command
* `user`: User to run the command as
* `cwd`: Working directory to run the command
* `on_stdout`: Callback for command stdout output
* `on_stderr`: Callback for command stderr output
* `stdin`: If `True`, the command will have a stdin stream that you can send data to using `sandbox.commands.send_stdin()`
* `timeout`: Timeout for the command connection in **seconds**. Using `0` will not limit the command connection time
* `request_timeout`: Timeout for opening the stream in **seconds** — the wait until envd confirms with a start event. The running stream is bounded by `timeout`
**Returns**:
`AsyncCommandHandle` handle to interact with the running command
### connect
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
async def connect(
pid: int,
timeout: Optional[float] = 60,
request_timeout: Optional[float] = None,
on_stdout: Optional[OutputHandler[Stdout]] = None,
on_stderr: Optional[OutputHandler[Stderr]] = None
) -> AsyncCommandHandle
```
Connects to a running command.
You can use `AsyncCommandHandle.wait()` to wait for the command to finish and get execution results.
**Arguments**:
* `pid`: Process ID of the command to connect to. You can get the list of processes using `sandbox.commands.list()`
* `request_timeout`: Timeout for opening the stream in **seconds** — the wait until envd confirms with a start event. The running stream is bounded by `timeout`
* `timeout`: Timeout for the command connection in **seconds**. Using `0` will not limit the command connection time
* `on_stdout`: Callback for command stdout output
* `on_stderr`: Callback for command stderr output
**Returns**:
`AsyncCommandHandle` handle to interact with the running command
## AsyncCommandHandle
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
class AsyncCommandHandle()
```
Command execution handle.
It provides methods for waiting for the command to finish, retrieving stdout/stderr, and killing the command.
### pid
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@property
def pid()
```
Command process ID.
### stdout
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@property
def stdout()
```
Command stdout output.
### stderr
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@property
def stderr()
```
Command stderr output.
### error
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@property
def error()
```
Command execution error message.
### exit\_code
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@property
def exit_code()
```
Command execution exit code.
`0` if the command finished successfully.
It is `None` if the command is still running.
### disconnect
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
async def disconnect() -> None
```
Disconnects from the command.
The command is not killed, but SDK stops receiving events from the command.
You can reconnect to the command using `sandbox.commands.connect` method.
### wait
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
async def wait() -> CommandResult
```
Wait for the command to finish and return the result.
If the command exits with a non-zero exit code, it throws a `CommandExitException`.
**Returns**:
`CommandResult` result of command execution
### kill
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
async def kill() -> bool
```
Kills the command.
It uses `SIGKILL` signal to kill the command
**Returns**:
`True` if the command was killed successfully, `False` if the command was not found
### send\_stdin
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
async def send_stdin(data: Union[str, bytes],
request_timeout: Optional[float] = None) -> None
```
Send data to the command stdin.
The command must have been started with `stdin=True`.
**Arguments**:
* `data`: Data to send to the command
* `request_timeout`: Timeout for the request in **seconds**
### close\_stdin
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
async def close_stdin(request_timeout: Optional[float] = None) -> None
```
Close the command stdin.
This signals EOF to the command. The command must have been started with `stdin=True`.
**Arguments**:
* `request_timeout`: Timeout for the request in **seconds**
## Git
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
class Git()
```
Async module for running git operations in the sandbox.
:deprecated: Run git with `sandbox.commands.run()` instead. The git module will be removed in the next major version.
### \_\_init\_\_
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def __init__(commands: Commands) -> None
```
Create a Git helper bound to the sandbox command runner.
**Arguments**:
* `commands`: Command runner used to execute git commands
### clone
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
async def clone(url: str,
path: Optional[str] = None,
branch: Optional[str] = None,
depth: Optional[int] = None,
username: Optional[str] = None,
password: Optional[str] = None,
envs: Optional[Dict[str, str]] = None,
user: Optional[str] = None,
cwd: Optional[str] = None,
timeout: Optional[float] = None,
request_timeout: Optional[float] = None,
dangerously_store_credentials: bool = False)
```
Clone a git repository into the sandbox.
**Arguments**:
* `url`: Git repository URL
* `path`: Destination path for the clone
* `branch`: Branch to check out
* `depth`: If set, perform a shallow clone with this depth
* `username`: Username for HTTP(S) authentication
* `password`: Password or token for HTTP(S) authentication
* `envs`: Environment variables used for the command
* `user`: User to run the command as
* `cwd`: Working directory to run the command
* `timeout`: Timeout for the command connection in **seconds**
* `request_timeout`: Timeout for the request in **seconds**
* `dangerously_store_credentials`: Store credentials in the cloned repository when True
**Returns**:
Command result from the command runner
### init
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
async def init(path: str,
bare: bool = False,
initial_branch: Optional[str] = None,
envs: Optional[Dict[str, str]] = None,
user: Optional[str] = None,
cwd: Optional[str] = None,
timeout: Optional[float] = None,
request_timeout: Optional[float] = None)
```
Initialize a new git repository.
**Arguments**:
* `path`: Destination path for the repository
* `bare`: Create a bare repository when True
* `initial_branch`: Initial branch name (for example, "main")
* `envs`: Environment variables used for the command
* `user`: User to run the command as
* `cwd`: Working directory to run the command
* `timeout`: Timeout for the command connection in **seconds**
* `request_timeout`: Timeout for the request in **seconds**
**Returns**:
Command result from the command runner
### remote\_add
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
async def remote_add(path: str,
name: str,
url: str,
fetch: bool = False,
overwrite: bool = False,
envs: Optional[Dict[str, str]] = None,
user: Optional[str] = None,
cwd: Optional[str] = None,
timeout: Optional[float] = None,
request_timeout: Optional[float] = None)
```
Add (or update) a remote for a repository.
**Arguments**:
* `path`: Repository path
* `name`: Remote name (for example, "origin")
* `url`: Remote URL
* `fetch`: Fetch the remote after adding it when True
* `overwrite`: Overwrite the remote URL if it already exists when True
* `envs`: Environment variables used for the command
* `user`: User to run the command as
* `cwd`: Working directory to run the command
* `timeout`: Timeout for the command connection in **seconds**
* `request_timeout`: Timeout for the request in **seconds**
**Returns**:
Command result from the command runner
### remote\_get
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
async def remote_get(path: str,
name: str,
envs: Optional[Dict[str, str]] = None,
user: Optional[str] = None,
cwd: Optional[str] = None,
timeout: Optional[float] = None,
request_timeout: Optional[float] = None) -> Optional[str]
```
Get the URL for a git remote.
Returns `None` when the remote does not exist.
**Arguments**:
* `path`: Repository path
* `name`: Remote name (for example, "origin")
* `envs`: Environment variables used for the command
* `user`: User to run the command as
* `cwd`: Working directory to run the command
* `timeout`: Timeout for the command connection in **seconds**
* `request_timeout`: Timeout for the request in **seconds**
**Returns**:
Remote URL if present, otherwise `None`
### status
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
async def status(path: str,
envs: Optional[Dict[str, str]] = None,
user: Optional[str] = None,
cwd: Optional[str] = None,
timeout: Optional[float] = None,
request_timeout: Optional[float] = None) -> GitStatus
```
Get repository status information.
**Arguments**:
* `path`: Repository path
* `envs`: Environment variables used for the command
* `user`: User to run the command as
* `cwd`: Working directory to run the command
* `timeout`: Timeout for the command connection in **seconds**
* `request_timeout`: Timeout for the request in **seconds**
**Returns**:
Parsed git status
### branches
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
async def branches(path: str,
envs: Optional[Dict[str, str]] = None,
user: Optional[str] = None,
cwd: Optional[str] = None,
timeout: Optional[float] = None,
request_timeout: Optional[float] = None) -> GitBranches
```
List branches in a repository.
**Arguments**:
* `path`: Repository path
* `envs`: Environment variables used for the command
* `user`: User to run the command as
* `cwd`: Working directory to run the command
* `timeout`: Timeout for the command connection in **seconds**
* `request_timeout`: Timeout for the request in **seconds**
**Returns**:
Parsed branch list
### create\_branch
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
async def create_branch(path: str,
branch: str,
envs: Optional[Dict[str, str]] = None,
user: Optional[str] = None,
cwd: Optional[str] = None,
timeout: Optional[float] = None,
request_timeout: Optional[float] = None)
```
Create and check out a new branch.
**Arguments**:
* `path`: Repository path
* `branch`: Branch name to create
* `envs`: Environment variables used for the command
* `user`: User to run the command as
* `cwd`: Working directory to run the command
* `timeout`: Timeout for the command connection in **seconds**
* `request_timeout`: Timeout for the request in **seconds**
**Returns**:
Command result from the command runner
### checkout\_branch
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
async def checkout_branch(path: str,
branch: str,
envs: Optional[Dict[str, str]] = None,
user: Optional[str] = None,
cwd: Optional[str] = None,
timeout: Optional[float] = None,
request_timeout: Optional[float] = None)
```
Check out an existing branch.
**Arguments**:
* `path`: Repository path
* `branch`: Branch name to check out
* `envs`: Environment variables used for the command
* `user`: User to run the command as
* `cwd`: Working directory to run the command
* `timeout`: Timeout for the command connection in **seconds**
* `request_timeout`: Timeout for the request in **seconds**
**Returns**:
Command result from the command runner
### delete\_branch
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
async def delete_branch(path: str,
branch: str,
force: bool = False,
envs: Optional[Dict[str, str]] = None,
user: Optional[str] = None,
cwd: Optional[str] = None,
timeout: Optional[float] = None,
request_timeout: Optional[float] = None)
```
Delete a branch.
**Arguments**:
* `path`: Repository path
* `branch`: Branch name to delete
* `force`: Force deletion with `-D` when `True`
* `envs`: Environment variables used for the command
* `user`: User to run the command as
* `cwd`: Working directory to run the command
* `timeout`: Timeout for the command connection in **seconds**
* `request_timeout`: Timeout for the request in **seconds**
**Returns**:
Command result from the command runner
### add
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
async def add(path: str,
files: Optional[List[str]] = None,
all: bool = True,
envs: Optional[Dict[str, str]] = None,
user: Optional[str] = None,
cwd: Optional[str] = None,
timeout: Optional[float] = None,
request_timeout: Optional[float] = None)
```
Stage files for commit.
**Arguments**:
* `path`: Repository path
* `files`: Files to add; when omitted, adds the current directory
* `all`: When `True` and `files` is omitted, stage all changes
* `envs`: Environment variables used for the command
* `user`: User to run the command as
* `cwd`: Working directory to run the command
* `timeout`: Timeout for the command connection in **seconds**
* `request_timeout`: Timeout for the request in **seconds**
**Returns**:
Command result from the command runner
### commit
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
async def commit(path: str,
message: str,
author_name: Optional[str] = None,
author_email: Optional[str] = None,
allow_empty: bool = False,
envs: Optional[Dict[str, str]] = None,
user: Optional[str] = None,
cwd: Optional[str] = None,
timeout: Optional[float] = None,
request_timeout: Optional[float] = None)
```
Create a commit in the repository.
**Arguments**:
* `path`: Repository path
* `message`: Commit message
* `author_name`: Commit author name
* `author_email`: Commit author email
* `allow_empty`: Allow empty commits when `True`
* `envs`: Environment variables used for the command
* `user`: User to run the command as
* `cwd`: Working directory to run the command
* `timeout`: Timeout for the command connection in **seconds**
* `request_timeout`: Timeout for the request in **seconds**
**Returns**:
Command result from the command runner
### reset
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
async def reset(path: str,
mode: Optional[GitResetMode] = None,
target: Optional[str] = None,
paths: Optional[List[str]] = None,
envs: Optional[Dict[str, str]] = None,
user: Optional[str] = None,
cwd: Optional[str] = None,
timeout: Optional[float] = None,
request_timeout: Optional[float] = None)
```
Reset the current HEAD to a specified state.
**Arguments**:
* `path`: Repository path
* `mode`: Reset mode (soft, mixed, hard, merge, keep)
* `target`: Commit, branch, or ref to reset to (defaults to HEAD)
* `paths`: Paths to reset
* `envs`: Environment variables used for the command
* `user`: User to run the command as
* `cwd`: Working directory to run the command
* `timeout`: Timeout for the command connection in **seconds**
* `request_timeout`: Timeout for the request in **seconds**
**Returns**:
Command result from the command runner
### restore
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
async def restore(path: str,
paths: List[str],
staged: Optional[bool] = None,
worktree: Optional[bool] = None,
source: Optional[str] = None,
envs: Optional[Dict[str, str]] = None,
user: Optional[str] = None,
cwd: Optional[str] = None,
timeout: Optional[float] = None,
request_timeout: Optional[float] = None)
```
Restore working tree files or unstage changes.
**Arguments**:
* `path`: Repository path
* `paths`: Paths to restore (use \["."] for all)
* `staged`: When True, restore the index (unstage)
* `worktree`: When True, restore working tree files
* `source`: Restore from the given source (commit, branch, or ref)
* `envs`: Environment variables used for the command
* `user`: User to run the command as
* `cwd`: Working directory to run the command
* `timeout`: Timeout for the command connection in **seconds**
* `request_timeout`: Timeout for the request in **seconds**
**Returns**:
Command result from the command runner
### push
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
async def push(path: str,
remote: Optional[str] = None,
branch: Optional[str] = None,
set_upstream: bool = True,
username: Optional[str] = None,
password: Optional[str] = None,
envs: Optional[Dict[str, str]] = None,
user: Optional[str] = None,
cwd: Optional[str] = None,
timeout: Optional[float] = None,
request_timeout: Optional[float] = None)
```
Push commits to a remote.
**Arguments**:
* `path`: Repository path
* `remote`: Remote name, e.g. `origin`
* `branch`: Branch name to push
* `set_upstream`: Set upstream tracking when `True`
* `username`: Username for HTTP(S) authentication
* `password`: Password or token for HTTP(S) authentication
* `envs`: Environment variables used for the command
* `user`: User to run the command as
* `cwd`: Working directory to run the command
* `timeout`: Timeout for the command connection in **seconds**
* `request_timeout`: Timeout for the request in **seconds**
**Returns**:
Command result from the command runner
### pull
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
async def pull(path: str,
remote: Optional[str] = None,
branch: Optional[str] = None,
username: Optional[str] = None,
password: Optional[str] = None,
envs: Optional[Dict[str, str]] = None,
user: Optional[str] = None,
cwd: Optional[str] = None,
timeout: Optional[float] = None,
request_timeout: Optional[float] = None)
```
Pull changes from a remote.
**Arguments**:
* `path`: Repository path
* `remote`: Remote name, e.g. `origin`
* `branch`: Branch name to pull
* `username`: Username for HTTP(S) authentication
* `password`: Password or token for HTTP(S) authentication
* `envs`: Environment variables used for the command
* `user`: User to run the command as
* `cwd`: Working directory to run the command
* `timeout`: Timeout for the command connection in **seconds**
* `request_timeout`: Timeout for the request in **seconds**
**Returns**:
Command result from the command runner
### set\_config
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
async def set_config(key: str,
value: str,
scope: str = "global",
path: Optional[str] = None,
envs: Optional[Dict[str, str]] = None,
user: Optional[str] = None,
cwd: Optional[str] = None,
timeout: Optional[float] = None,
request_timeout: Optional[float] = None)
```
Set a git config value.
Use `scope="local"` together with `path` to configure a specific repository.
**Arguments**:
* `key`: Git config key (e.g. `pull.rebase`)
* `value`: Git config value
* `scope`: Config scope: `global`, `local`, or `system`
* `path`: Repository path required when `scope` is `local`
* `envs`: Environment variables used for the command
* `user`: User to run the command as
* `cwd`: Working directory to run the command
* `timeout`: Timeout for the command connection in **seconds**
* `request_timeout`: Timeout for the request in **seconds**
**Returns**:
Command result from the command runner
### get\_config
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
async def get_config(key: str,
scope: str = "global",
path: Optional[str] = None,
envs: Optional[Dict[str, str]] = None,
user: Optional[str] = None,
cwd: Optional[str] = None,
timeout: Optional[float] = None,
request_timeout: Optional[float] = None) -> Optional[str]
```
Get a git config value.
Returns `None` when the key is not set in the requested scope.
**Arguments**:
* `key`: Git config key (e.g. `pull.rebase`)
* `scope`: Config scope: `global`, `local`, or `system`
* `path`: Repository path required when `scope` is `local`
* `envs`: Environment variables used for the command
* `user`: User to run the command as
* `cwd`: Working directory to run the command
* `timeout`: Timeout for the command connection in **seconds**
* `request_timeout`: Timeout for the request in **seconds**
**Returns**:
Config value if present, otherwise `None`
### dangerously\_authenticate
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
async def dangerously_authenticate(username: str,
password: str,
host: str = "github.com",
protocol: str = "https",
envs: Optional[Dict[str, str]] = None,
user: Optional[str] = None,
cwd: Optional[str] = None,
timeout: Optional[float] = None,
request_timeout: Optional[float] = None)
```
Dangerously authenticate git globally via the credential helper.
This persists credentials in the credential store and may be accessible to agents running on the sandbox.
Prefer short-lived credentials when possible.
**Arguments**:
* `username`: Username for HTTP(S) authentication
* `password`: Password or token for HTTP(S) authentication
* `host`: Host to authenticate for, defaults to `github.com`
* `protocol`: Protocol to authenticate for, defaults to `https`
* `envs`: Environment variables used for the command
* `user`: User to run the command as
* `cwd`: Working directory to run the command
* `timeout`: Timeout for the command connection in **seconds**
* `request_timeout`: Timeout for the request in **seconds**
**Returns**:
Command result from the command runner
### configure\_user
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
async def configure_user(name: str,
email: str,
scope: str = "global",
path: Optional[str] = None,
envs: Optional[Dict[str, str]] = None,
user: Optional[str] = None,
cwd: Optional[str] = None,
timeout: Optional[float] = None,
request_timeout: Optional[float] = None)
```
Configure git user name and email.
**Arguments**:
* `name`: Git user name
* `email`: Git user email
* `scope`: Config scope: `global`, `local`, or `system`
* `path`: Repository path required when `scope` is `local`
* `envs`: Environment variables used for the command
* `user`: User to run the command as
* `cwd`: Working directory to run the command
* `timeout`: Timeout for the command connection in **seconds**
* `request_timeout`: Timeout for the request in **seconds**
**Returns**:
Command result from the command runner
# Sandbox sync
Source: https://docs.e2b.dev/sdk-reference/python-sdk/v2.56.0/sandbox_sync
## Sandbox
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
class Sandbox(SandboxApi)
```
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 isolated code
* Access the internet
Check docs [here](https://e2b.dev/docs).
Use the `Sandbox.create()` to create a new sandbox.
**Example**:
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Sandbox
sandbox = Sandbox.create()
```
### files
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@property
def files() -> Filesystem
```
Module for interacting with the sandbox filesystem.
### commands
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@property
def commands() -> Commands
```
Module for running commands in the sandbox.
### pty
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@property
def pty() -> Pty
```
Module for interacting with the sandbox pseudo-terminal.
### git
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@property
def git() -> Git
```
Module for running git operations in the sandbox.
:deprecated: Run git with `sandbox.commands.run()` instead. The git module will be removed in the next major version.
### \_\_init\_\_
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def __init__(**opts: Unpack[SandboxOpts])
```
:deprecated: This constructor is deprecated
Use `Sandbox.create()` to create a new sandbox instead.
### is\_running
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def is_running(request_timeout: Optional[float] = None) -> bool
```
Check if the sandbox is running.
**Arguments**:
* `request_timeout`: Timeout for the request in **seconds**
**Returns**:
`True` if the sandbox is running, `False` otherwise
Example
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
sandbox = Sandbox.create()
sandbox.is_running() # Returns True
sandbox.kill()
sandbox.is_running() # Returns False
```
### create
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@classmethod
def create(cls,
template: Optional[str] = None,
timeout: Optional[int] = None,
metadata: Optional[Dict[str, str]] = None,
envs: Optional[Dict[str, str]] = None,
secure: Optional[bool] = None,
allow_internet_access: Optional[bool] = None,
mcp: Optional[McpServer] = None,
network: Optional[SandboxNetworkOpts] = None,
iam: Optional[SandboxIamOpts] = None,
lifecycle: Optional[SandboxLifecycle] = None,
volume_mounts: Optional[SandboxVolumeMount] = None,
logger: Optional[logging.Logger] = None,
**opts: Unpack[ApiParams]) -> Self
```
Create a new sandbox.
By default, the sandbox is created from the default `base` sandbox template.
**Arguments**:
* `template`: Sandbox template name or ID
* `timeout`: Timeout for the sandbox in **seconds**. The maximum time a sandbox can be kept alive is 24 hours (86\_400 seconds) for Pro users and 1 hour (3\_600 seconds) for Hobby users.
* `metadata`: Custom metadata for the sandbox
* `envs`: Custom environment variables for the sandbox
* `secure`: Deprecated — every sandbox secures envd access; accepted for backward compatibility and ignored
* `allow_internet_access`: Allow sandbox to access the internet. If set to `False`, it works the same as setting network `deny_out` to `[0.0.0.0/0]`.
* `mcp`: MCP server to enable in the sandbox
* `network`: Sandbox network configuration. `allow_out`/`deny_out` may also be a callable receiving a :class:`SandboxNetworkSelectorContext` (`ctx.all_traffic`, `ctx.rules`) and returning a list of strings. Per-host transform rules are nested under `network.rules`; a rule's `transform` may be a callable receiving a :class:`SandboxNetworkTransformContext` of placeholder strings (`ctx.iam.tokens[name]`).
* `iam`: Sandbox workload identity configuration. A non-empty `tokens` map enables workload identity for the sandbox; token definitions can be created with :meth:`Secret.iam_token`. Example: `{"tokens": {"aws": Secret.iam_token(audience="sts.amazonaws.com", token_type="JWT-SVID")}}`. Registered tokens are exposed to `network.rules` `transform` callables as `ctx.iam.tokens[name]` placeholders, which the egress proxy resolves per request
* `lifecycle`: Sandbox lifecycle configuration — `on_timeout`: `"kill"` or `"pause"` (omitted from the request when unset, leaving the API's default, currently `"kill"`, in effect), or an object `{"action": "pause"|"kill", "mode": "full"|"filesystem"}` where `mode` set to `"filesystem"` makes a timeout auto-pause filesystem-only (cold-boots on resume; cannot be combined with `auto_resume`); an omitted `mode` leaves the snapshot kind to the API (`keep_memory` is a deprecated alias); `auto_resume`: leave unset to let the API pick the behavior, set `False` to opt out explicitly, or `True` (only when `on_timeout` action is `"pause"`). Example: `{"on_timeout": {"action": "pause", "mode": "filesystem"}}`
* `volume_mounts`: Dictionary mapping mount paths to Volume instances or volume names
* `logger`: Logger used for request and response logging for this sandbox. Accepts any standard library `logging.Logger`. When omitted, no request/response logging is emitted.
**Returns**:
A Sandbox instance for the new sandbox
Use this method instead of using the constructor to create a new sandbox.
### connect
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@overload
def connect(timeout: Optional[int] = None,
*,
on_resume: SandboxOnResume = "restore",
**opts: Unpack[ApiParams]) -> Self
```
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).
**Arguments**:
* `timeout`: Timeout for the sandbox in **seconds**
For running sandboxes, the timeout will update only if the new timeout is longer than the existing one.
* `on_resume`: `"restore"` (the default) restores the memory snapshot; `"reboot"`
cold-boots from disk state, so writes not flushed before the pause may be lost.
Rejected where filesystem-only resume is not enabled; a no-op for a snapshot
without memory or a sandbox that is already running. A value outside the two
literals raises `InvalidArgumentException`.
Needs a control plane that knows this option: E2B Cloud, or a self-hosted or BYOC
deployment built from `e2b-dev/runtime` at or after the commit that added the `memory`
field to connect/resume (2026-08-20). An older control plane drops the field and
restores memory while answering as if the request had succeeded.
**Returns**:
A running sandbox instance
@example
````python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
sandbox = Sandbox.create()
sandbox.pause()
same_sandbox = sandbox.connect()
### connect
```python
@overload
@staticmethod
def connect(sandbox_id: str,
timeout: Optional[int] = None,
logger: Optional[logging.Logger] = None,
*,
on_resume: SandboxOnResume = "restore",
**opts: Unpack[ApiParams]) -> "Sandbox"
````
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).
**Arguments**:
* `sandbox_id`: Sandbox ID
* `timeout`: Timeout for the sandbox in **seconds**.
For running sandboxes, the timeout will update only if the new timeout is longer than the existing one.
* `logger`: Logger used for request and response logging for this sandbox. Accepts any standard library `logging.Logger`. When omitted, no request/response logging is emitted.
* `on_resume`: `"restore"` (the default) restores the memory snapshot; `"reboot"`
cold-boots from disk state, so writes not flushed before the pause may be lost.
Rejected where filesystem-only resume is not enabled; a no-op for a snapshot
without memory or a sandbox that is already running. A value outside the two
literals raises `InvalidArgumentException`.
Needs a control plane that knows this option: E2B Cloud, or a self-hosted or BYOC
deployment built from `e2b-dev/runtime` at or after the commit that added the `memory`
field to connect/resume (2026-08-20). An older control plane drops the field and
restores memory while answering as if the request had succeeded.
**Returns**:
A running sandbox instance
@example
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
sandbox = Sandbox.create()
Sandbox.pause(sandbox.sandbox_id)
same_sandbox = Sandbox.connect(sandbox.sandbox_id)
```
### connect
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@class_method_variant("_cls_connect_sandbox")
def connect(timeout: Optional[int] = None,
*,
on_resume: SandboxOnResume = "restore",
**opts: Unpack[ApiParams]) -> Self
```
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).
**Arguments**:
* `timeout`: Timeout for the sandbox in **seconds**.
For running sandboxes, the timeout will update only if the new timeout is longer than the existing one.
* `on_resume`: `"restore"` (the default) restores the memory snapshot; `"reboot"`
cold-boots from disk state, so writes not flushed before the pause may be lost.
Rejected where filesystem-only resume is not enabled; a no-op for a snapshot
without memory or a sandbox that is already running. A value outside the two
literals raises `InvalidArgumentException`.
Needs a control plane that knows this option: E2B Cloud, or a self-hosted or BYOC
deployment built from `e2b-dev/runtime` at or after the commit that added the `memory`
field to connect/resume (2026-08-20). An older control plane drops the field and
restores memory while answering as if the request had succeeded.
**Returns**:
A running sandbox instance
@example
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
sandbox = Sandbox.create()
sandbox.pause()
same_sandbox = sandbox.connect()
```
### fork
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@overload
def fork(timeout: Optional[int] = None,
count: Optional[int] = None,
**opts: Unpack[ApiParams]) -> List[Union[Self, Exception]]
```
Fork the sandbox.
The sandbox is checkpointed in place (briefly paused, snapshotted with
its full memory state, and resumed — its ID and expiration stay
untouched) and `count` new sandboxes are created from that snapshot.
All forks boot from the same snapshot, so the snapshot is captured once
regardless of count.
Each fork succeeds or fails independently — the returned list contains
one entry per requested fork, either a running `Sandbox` instance
or an exception describing why that fork failed to start. Per-fork
error codes map to the same exception classes as other API errors
(e.g. 429 to `RateLimitException`).
**Arguments**:
* `timeout`: Timeout for the forked sandboxes in **seconds**.
* `count`: Number of forked sandboxes to create. An integer between 1 and 20. Omitted, the API default applies.
**Returns**:
List with one entry per requested fork — a sandbox instance or an exception
@example
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
sandbox = Sandbox.create()
fork1, fork2 = sandbox.fork(count=2)
```
### fork
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@overload
@staticmethod
def fork(sandbox_id: str,
timeout: Optional[int] = None,
count: Optional[int] = None,
logger: Optional[logging.Logger] = None,
**opts: Unpack[ApiParams]) -> List[Union["Sandbox", Exception]]
```
Fork a running sandbox specified by sandbox ID.
The sandbox is checkpointed in place (briefly paused, snapshotted with
its full memory state, and resumed — its ID and expiration stay
untouched) and `count` new sandboxes are created from that snapshot.
All forks boot from the same snapshot, so the snapshot is captured once
regardless of count.
Each fork succeeds or fails independently — the returned list contains
one entry per requested fork, either a running `Sandbox` instance
or an exception describing why that fork failed to start. Per-fork
error codes map to the same exception classes as other API errors
(e.g. 429 to `RateLimitException`).
**Arguments**:
* `sandbox_id`: Sandbox ID
* `timeout`: Timeout for the forked sandboxes in **seconds**.
* `count`: Number of forked sandboxes to create. An integer between 1 and 20. Omitted, the API default applies.
* `logger`: Logger used for request and response logging for the forked sandboxes. Accepts any standard library `logging.Logger`. When omitted, no request/response logging is emitted.
**Returns**:
List with one entry per requested fork — a sandbox instance or an exception
@example
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
sandbox = Sandbox.create()
fork1, fork2 = Sandbox.fork(sandbox.sandbox_id, count=2)
```
### fork
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@class_method_variant("_cls_fork_sandbox")
def fork(timeout: Optional[int] = None,
count: Optional[int] = None,
**opts: Unpack[ApiParams]) -> List[Union[Self, Exception]]
```
Fork the sandbox.
The sandbox is checkpointed in place (briefly paused, snapshotted with
its full memory state, and resumed — its ID and expiration stay
untouched) and `count` new sandboxes are created from that snapshot.
All forks boot from the same snapshot, so the snapshot is captured once
regardless of count.
Each fork succeeds or fails independently — the returned list contains
one entry per requested fork, either a running `Sandbox` instance
or an exception describing why that fork failed to start. Per-fork
error codes map to the same exception classes as other API errors
(e.g. 429 to `RateLimitException`).
**Arguments**:
* `timeout`: Timeout for the forked sandboxes in **seconds**.
* `count`: Number of forked sandboxes to create. An integer between 1 and 20. Omitted, the API default applies.
**Returns**:
List with one entry per requested fork — a sandbox instance or an exception
@example
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
sandbox = Sandbox.create()
fork1, fork2 = sandbox.fork(count=2)
```
### kill
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@overload
def kill(**opts: Unpack[ApiParams]) -> bool
```
Kill the sandbox.
**Returns**:
`True` if the sandbox was killed, `False` if the sandbox was not found
### kill
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@overload
@staticmethod
def kill(sandbox_id: str, **opts: Unpack[ApiParams]) -> bool
```
Kill the sandbox specified by sandbox ID.
**Arguments**:
* `sandbox_id`: Sandbox ID
**Returns**:
`True` if the sandbox was killed, `False` if the sandbox was not found
### kill
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@class_method_variant("_cls_kill")
def kill(**opts: Unpack[ApiParams]) -> bool
```
Kill the sandbox specified by sandbox ID.
**Returns**:
`True` if the sandbox was killed, `False` if the sandbox was not found
### set\_timeout
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@overload
def set_timeout(timeout: int, **opts: Unpack[ApiParams]) -> None
```
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 `.set_timeout`.
The maximum time a sandbox can be kept alive is 24 hours (86\_400 seconds) for Pro users and 1 hour (3\_600 seconds) for Hobby users.
**Arguments**:
* `timeout`: Timeout for the sandbox in **seconds**
### set\_timeout
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@overload
@staticmethod
def set_timeout(sandbox_id: str, timeout: int,
**opts: Unpack[ApiParams]) -> None
```
Set the timeout of the sandbox specified by sandbox ID.
This method can extend or reduce the sandbox timeout set when creating the sandbox or from the last call to `.set_timeout`.
The maximum time a sandbox can be kept alive is 24 hours (86\_400 seconds) for Pro users and 1 hour (3\_600 seconds) for Hobby users.
**Arguments**:
* `sandbox_id`: Sandbox ID
* `timeout`: Timeout for the sandbox in **seconds**
### set\_timeout
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@class_method_variant("_cls_set_timeout")
def set_timeout(timeout: int, **opts: Unpack[ApiParams]) -> None
```
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 `.set_timeout`.
The maximum time a sandbox can be kept alive is 24 hours (86\_400 seconds) for Pro users and 1 hour (3\_600 seconds) for Hobby users.
**Arguments**:
* `timeout`: Timeout for the sandbox in **seconds**
### update\_network
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@overload
def update_network(network: SandboxNetworkUpdate,
**opts: Unpack[ApiParams]) -> None
```
Update the network configuration of the sandbox.
Replaces the current egress configuration atomically — fields that are
omitted are cleared on the server.
**Arguments**:
* `network`: New network configuration.
### update\_network
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@overload
@staticmethod
def update_network(sandbox_id: str, network: SandboxNetworkUpdate,
**opts: Unpack[ApiParams]) -> None
```
Update the network configuration of the sandbox specified by sandbox ID.
Replaces the current egress configuration atomically — fields that are
omitted are cleared on the server.
**Arguments**:
* `sandbox_id`: Sandbox ID.
* `network`: New network configuration.
### update\_network
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@class_method_variant("_cls_update_network")
def update_network(network: SandboxNetworkUpdate,
**opts: Unpack[ApiParams]) -> None
```
Update the network configuration of the sandbox.
Replaces the current egress configuration atomically — fields that are
omitted are cleared on the server.
**Arguments**:
* `network`: New network configuration.
### get\_info
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@overload
def get_info(**opts: Unpack[ApiParams]) -> SandboxInfo
```
Get sandbox information like sandbox ID, template, metadata, started at/end at date.
**Returns**:
Sandbox info
### get\_info
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@overload
@staticmethod
def get_info(sandbox_id: str, **opts: Unpack[ApiParams]) -> SandboxInfo
```
Get sandbox information like sandbox ID, template, metadata, started at/end at date.
**Arguments**:
* `sandbox_id`: Sandbox ID
**Returns**:
Sandbox info
### get\_info
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@class_method_variant("_cls_get_info")
def get_info(**opts: Unpack[ApiParams]) -> SandboxInfo
```
Get sandbox information like sandbox ID, template, metadata, started at/end at date.
**Returns**:
Sandbox info
### get\_metrics
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@overload
def get_metrics(start: Optional[datetime.datetime] = None,
end: Optional[datetime.datetime] = None,
**opts: Unpack[ApiParams]) -> List[SandboxMetrics]
```
Get the metrics of the current sandbox.
**Arguments**:
* `start`: Start time for the metrics, defaults to the start of the sandbox
* `end`: End time for the metrics, defaults to the current time
**Returns**:
List of sandbox metrics containing CPU, memory and disk usage information
### get\_metrics
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@overload
@staticmethod
def get_metrics(sandbox_id: str,
start: Optional[datetime.datetime] = None,
end: Optional[datetime.datetime] = None,
**opts: Unpack[ApiParams]) -> List[SandboxMetrics]
```
Get the metrics of the sandbox specified by sandbox ID.
**Arguments**:
* `sandbox_id`: Sandbox ID
* `start`: Start time for the metrics, defaults to the start of the sandbox
* `end`: End time for the metrics, defaults to the current time
**Returns**:
List of sandbox metrics containing CPU, memory and disk usage information
### get\_metrics
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@class_method_variant("_cls_get_metrics")
def get_metrics(start: Optional[datetime.datetime] = None,
end: Optional[datetime.datetime] = None,
**opts: Unpack[ApiParams]) -> List[SandboxMetrics]
```
Get the metrics of the current sandbox.
**Arguments**:
* `start`: Start time for the metrics, defaults to the start of the sandbox
* `end`: End time for the metrics, defaults to the current time
**Returns**:
List of sandbox metrics containing CPU, memory and disk usage information
### pause
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@overload
def pause(keep_memory: Optional[bool] = None,
*,
mode: Optional[SnapshotMode] = None,
**opts: Unpack[ApiParams]) -> bool
```
Pause the sandbox.
**Arguments**:
* `keep_memory`: :deprecated: Use `mode` instead: `keep_memory=False` is `mode="filesystem"`, `keep_memory=True` is `mode="full"`.
* `mode`: What the pause persists. With `"filesystem"`, the in-memory state is dropped and only the filesystem is persisted; resuming such a sandbox cold-boots (reboots) it from disk, losing running processes and open connections. Defaults to `"full"` (memory and filesystem).
**Returns**:
`True` if the sandbox got paused, `False` if the sandbox was already paused
### pause
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@overload
@staticmethod
def pause(sandbox_id: str,
keep_memory: Optional[bool] = None,
*,
mode: Optional[SnapshotMode] = None,
**opts: Unpack[ApiParams]) -> bool
```
Pause the sandbox specified by sandbox ID.
**Arguments**:
* `sandbox_id`: Sandbox ID
* `keep_memory`: :deprecated: Use `mode` instead: `keep_memory=False` is `mode="filesystem"`, `keep_memory=True` is `mode="full"`.
* `mode`: What the pause persists. With `"filesystem"`, the in-memory state is dropped and only the filesystem is persisted; resuming such a sandbox cold-boots (reboots) it from disk, losing running processes and open connections. Defaults to `"full"` (memory and filesystem).
**Returns**:
`True` if the sandbox got paused, `False` if the sandbox was already paused
### pause
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@class_method_variant("_cls_pause")
def pause(keep_memory: Optional[bool] = None,
*,
mode: Optional[SnapshotMode] = None,
**opts: Unpack[ApiParams]) -> bool
```
Pause the sandbox.
**Arguments**:
* `keep_memory`: :deprecated: Use `mode` instead: `keep_memory=False` is `mode="filesystem"`, `keep_memory=True` is `mode="full"`.
* `mode`: What the pause persists. With `"filesystem"`, the in-memory state is dropped and only the filesystem is persisted; resuming such a sandbox cold-boots (reboots) it from disk, losing running processes and open connections. Defaults to `"full"` (memory and filesystem).
**Returns**:
`True` if the sandbox got paused, `False` if the sandbox was already paused
### beta\_pause
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@class_method_variant("_cls_pause")
def beta_pause(keep_memory: Optional[bool] = None,
*,
mode: Optional[SnapshotMode] = None,
**opts: Unpack[ApiParams]) -> bool
```
**Returns**:
`True` if the sandbox got paused, `False` if the sandbox was already paused
### create\_snapshot
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@overload
def create_snapshot(name: Optional[str] = None,
*,
mode: Optional[SnapshotMode] = None,
**opts: Unpack[ApiParams]) -> SnapshotInfo
```
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 `snapshot_id` with `Sandbox.create(snapshot_id)` to create a new sandbox from the snapshot.
**Arguments**:
* `name`: Optional name for the snapshot template. If a snapshot template with this name already exists, a new build will be assigned to the existing template instead of creating a new one.
* `mode`: What the snapshot persists. With `"filesystem"`, only the filesystem is persisted: the snapshot is smaller and faster to take, and sandboxes created from it cold-boot (start fresh from disk) instead of restoring memory, so they begin without the running processes, in-memory state and open connections of the source sandbox. The source sandbox keeps running either way. Defaults to `"full"` (memory and filesystem).
**Raises**:
* `SandboxException`: when the API refuses a filesystem-only snapshot (`mode="filesystem"`). `status_code` 400 means the feature is not enabled for the team (`snapshot_filesystem_only_disabled`); 409 means the sandbox's node runs an orchestrator that predates the option (`snapshot_filesystem_only_unsupported_node`). A full memory snapshot still works in both cases; for 409, pause and resume the sandbox and retry.
**Returns**:
Snapshot information including the snapshot ID and names
### create\_snapshot
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@overload
@staticmethod
def create_snapshot(sandbox_id: str,
name: Optional[str] = None,
*,
mode: Optional[SnapshotMode] = None,
**opts: Unpack[ApiParams]) -> SnapshotInfo
```
Create a snapshot from the sandbox specified by sandbox ID.
The sandbox will be paused while the snapshot is being created.
**Arguments**:
* `sandbox_id`: Sandbox ID
* `name`: Optional name for the snapshot template. If a snapshot template with this name already exists, a new build will be assigned to the existing template instead of creating a new one.
* `mode`: What the snapshot persists. With `"filesystem"`, only the filesystem is persisted: the snapshot is smaller and faster to take, and sandboxes created from it cold-boot (start fresh from disk) instead of restoring memory, so they begin without the running processes, in-memory state and open connections of the source sandbox. The source sandbox keeps running either way. Defaults to `"full"` (memory and filesystem).
**Raises**:
* `SandboxException`: when the API refuses a filesystem-only snapshot (`mode="filesystem"`). `status_code` 400 means the feature is not enabled for the team (`snapshot_filesystem_only_disabled`); 409 means the sandbox's node runs an orchestrator that predates the option (`snapshot_filesystem_only_unsupported_node`). A full memory snapshot still works in both cases; for 409, pause and resume the sandbox and retry.
**Returns**:
Snapshot information including the snapshot ID and names
### create\_snapshot
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@class_method_variant("_cls_create_snapshot")
def create_snapshot(name: Optional[str] = None,
*,
mode: Optional[SnapshotMode] = None,
**opts: Unpack[ApiParams]) -> SnapshotInfo
```
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 `snapshot_id` with `Sandbox.create(snapshot_id)` to create a new sandbox from the snapshot.
**Arguments**:
* `name`: Optional name for the snapshot template. If a snapshot template with this name already exists, a new build will be assigned to the existing template instead of creating a new one.
* `mode`: What the snapshot persists. With `"filesystem"`, only the filesystem is persisted: the snapshot is smaller and faster to take, and sandboxes created from it cold-boot (start fresh from disk) instead of restoring memory, so they begin without the running processes, in-memory state and open connections of the source sandbox. The source sandbox keeps running either way. Defaults to `"full"` (memory and filesystem).
**Raises**:
* `SandboxException`: when the API refuses a filesystem-only snapshot (`mode="filesystem"`). `status_code` 400 means the feature is not enabled for the team (`snapshot_filesystem_only_disabled`); 409 means the sandbox's node runs an orchestrator that predates the option (`snapshot_filesystem_only_unsupported_node`). A full memory snapshot still works in both cases; for 409, pause and resume the sandbox and retry.
**Returns**:
Snapshot information including the snapshot ID and names
### list\_snapshots
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@overload
def list_snapshots(limit: Optional[int] = None,
next_token: Optional[str] = None,
name: Optional[str] = None,
**opts: Unpack[ApiParams]) -> SnapshotPaginator
```
List snapshots for this sandbox.
**Arguments**:
* `limit`: Maximum number of snapshots to return per page
* `next_token`: Token for pagination
* `name`: Filter snapshots by name or ID, optionally tag-qualified (e.g. "my-snapshot", "my-project/my-snapshot" or "my-snapshot:v1")
**Returns**:
Paginator for listing snapshots
### list\_snapshots
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@overload
@staticmethod
def list_snapshots(sandbox_id: Optional[str] = None,
limit: Optional[int] = None,
next_token: Optional[str] = None,
name: Optional[str] = None,
**opts: Unpack[ApiParams]) -> SnapshotPaginator
```
List all snapshots.
**Arguments**:
* `sandbox_id`: Filter snapshots by source sandbox ID
* `limit`: Maximum number of snapshots to return per page
* `next_token`: Token for pagination
* `name`: Filter snapshots by name or ID, optionally tag-qualified (e.g. "my-snapshot", "my-project/my-snapshot" or "my-snapshot:v1")
**Returns**:
Paginator for listing snapshots
### list\_snapshots
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@class_method_variant("_cls_list_snapshots")
def list_snapshots(limit: Optional[int] = None,
next_token: Optional[str] = None,
name: Optional[str] = None,
**opts: Unpack[ApiParams]) -> SnapshotPaginator
```
List snapshots for this sandbox.
**Arguments**:
* `limit`: Maximum number of snapshots to return per page
* `next_token`: Token for pagination
* `name`: Filter snapshots by name or ID, optionally tag-qualified (e.g. "my-snapshot", "my-project/my-snapshot" or "my-snapshot:v1")
**Returns**:
Paginator for listing snapshots
### delete\_snapshot
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@classmethod
def delete_snapshot(cls, snapshot_id: str, **opts: Unpack[ApiParams]) -> bool
```
Delete a snapshot.
**Arguments**:
* `snapshot_id`: Snapshot ID
**Returns**:
`True` if the snapshot was deleted, `False` if it was not found
### get\_mcp\_token
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def get_mcp_token() -> Optional[str]
```
Get the MCP token for the sandbox.
**Returns**:
MCP token for the sandbox, or None if MCP is not enabled.
## SandboxPaginator
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
class SandboxPaginator(SandboxPaginatorBase)
```
Paginator for listing sandboxes.
**Example**:
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
paginator = Sandbox.list()
while paginator.has_next:
sandboxes = paginator.next_items()
print(sandboxes)
```
### next\_items
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def next_items(**opts: Unpack[ApiParams]) -> List[SandboxInfo]
```
Returns the next page of sandboxes.
Call this method only if `has_next` is `True`, otherwise it will raise an exception.
**Arguments**:
* `opts`: Per-call connection options (e.g. `api_key`, `domain`,
`headers`, `request_timeout`). When provided, this call uses these
options on top of the ones the paginator was constructed with.
**Returns**:
List of sandboxes
## SnapshotPaginator
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
class SnapshotPaginator(SnapshotPaginatorBase)
```
Paginator for listing snapshots.
**Example**:
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
paginator = Sandbox.list_snapshots()
while paginator.has_next:
snapshots = paginator.next_items()
print(snapshots)
```
### next\_items
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def next_items(**opts: Unpack[ApiParams]) -> List[SnapshotInfo]
```
Returns the next page of snapshots.
Call this method only if `has_next` is `True`, otherwise it will raise an exception.
**Arguments**:
* `opts`: Per-call connection options (e.g. `api_key`, `domain`,
`headers`, `request_timeout`). When provided, this call uses these
options on top of the ones the paginator was constructed with.
**Returns**:
List of snapshots
## Filesystem
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
class Filesystem()
```
Module for interacting with the filesystem in the sandbox.
### read
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@overload
def read(path: str,
format: Literal["text"] = "text",
user: Optional[Username] = None,
request_timeout: Optional[float] = None,
gzip: bool = False) -> str
```
Read file content as a `str`.
**Arguments**:
* `path`: Path to the file
* `user`: Run the operation as this user
* `format`: Format of the file content—`text` by default
* `request_timeout`: Timeout for the request in **seconds**
* `gzip`: Use gzip compression for the request
**Returns**:
File content as a `str`
### read
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@overload
def read(path: str,
format: Literal["bytes"],
user: Optional[Username] = None,
request_timeout: Optional[float] = None,
gzip: bool = False) -> bytearray
```
Read file content as a `bytearray`.
**Arguments**:
* `path`: Path to the file
* `user`: Run the operation as this user
* `format`: Format of the file content—`bytes`
* `request_timeout`: Timeout for the request in **seconds**
* `gzip`: Use gzip compression for the request
**Returns**:
File content as a `bytearray`
### read
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@overload
def read(path: str,
format: Literal["stream"],
user: Optional[Username] = None,
request_timeout: Optional[float] = None,
gzip: bool = False,
stream_idle_timeout: Optional[float] = None) -> FileStreamReader
```
Read file content as a `FileStreamReader` (an `Iterator[bytes]`).
A `request_timeout` set explicitly for this call is the deadline for
the whole transfer; by default the download is not bounded in total.
A stalled stream is reclaimed by a transport-wide idle read timeout
(raising `httpx.ReadTimeout`). The reader releases its connection
once fully consumed; if you don't read it to the end, use it as a
context manager or call `close()` for deterministic cleanup.
**Arguments**:
* `path`: Path to the file
* `user`: Run the operation as this user
* `format`: Format of the file content—`stream`
* `request_timeout`: Deadline for the whole transfer in **seconds**
* `gzip`: Use gzip compression for the request
* `stream_idle_timeout`: Ignored — the sync client cannot
interrupt a blocking read. A stalled streamed read is bounded by
a transport-wide idle read timeout instead (60 seconds), which
resets on every chunk. (`AsyncSandbox.files.read` honors this
parameter.)
**Returns**:
File content as a `FileStreamReader`
### write
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def write(path: str,
data: Union[str, bytes, IO],
user: Optional[Username] = None,
request_timeout: Optional[float] = None,
gzip: bool = False,
use_octet_stream: Optional[bool] = None,
metadata: Optional[Dict[str, str]] = None) -> WriteInfo
```
Write content to a file on the path.
Writing to a file that doesn't exist creates the file.
Writing to a file that already exists overwrites the file.
Writing to a file at path that doesn't exist creates the necessary directories.
**Arguments**:
* `path`: Path to the file
* `data`: Data to write to the file, can be a `str`, `bytes`, or `IO`. File-like objects are streamed in chunks instead of being buffered in memory.
* `user`: Run the operation as this user
* `request_timeout`: Timeout for the request in **seconds**
* `gzip`: Use gzip compression for the upload. Implies the `application/octet-stream` upload. Requires envd 0.5.7 or later — when not supported, the upload falls back to uncompressed `multipart/form-data`.
* `use_octet_stream`: Upload using `application/octet-stream` instead of `multipart/form-data`. Defaults to `None`, which uses octet-stream when `data` is a file-like object (so streamed uploads aren't buffered) and `multipart/form-data` otherwise. Requires envd 0.5.7 or later — when not supported, the upload falls back to `multipart/form-data`, which reads text-mode file-like data into memory (httpx only streams binary file objects in a multipart body).
* `metadata`: User-defined metadata to persist on the uploaded file as extended attributes. Keys are lowercased by the sandbox; invalid keys or values raise an `InvalidArgumentException`. Requires envd 0.6.2 or later.
**Returns**:
Information about the written file
### write\_files
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def write_files(files: List[WriteEntry],
user: Optional[Username] = None,
request_timeout: Optional[float] = None,
gzip: bool = False,
use_octet_stream: Optional[bool] = None,
metadata: Optional[Dict[str, str]] = None) -> List[WriteInfo]
```
Writes multiple files.
Writes a list of files to the filesystem.
When writing to a file that doesn't exist, the file will get created.
When writing to a file that already exists, the file will get overwritten.
When writing to a file at path that doesn't exist, the necessary directories will be created.
**Arguments**:
* `files`: list of files to write as `WriteEntry` objects, each containing `path` and `data`
* `user`: Run the operation as this user
* `request_timeout`: Timeout for the request
* `gzip`: Use gzip compression for the upload. Implies the `application/octet-stream` upload. Requires envd 0.5.7 or later — when not supported, the upload falls back to uncompressed `multipart/form-data`.
* `use_octet_stream`: Upload using `application/octet-stream` instead of `multipart/form-data`. Defaults to `None`, which uses octet-stream when any entry is a file-like object (so streamed uploads aren't buffered) and `multipart/form-data` otherwise. Requires envd 0.5.7 or later — when not supported, the upload falls back to `multipart/form-data`, which reads text-mode file-like data into memory (httpx only streams binary file objects in a multipart body).
* `metadata`: User-defined metadata to persist on each uploaded file as extended attributes; the same map is applied to every file. Keys are lowercased by the sandbox; invalid keys or values raise an `InvalidArgumentException`. Requires envd 0.6.2 or later.
**Returns**:
Information about the written files
### list
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def list(path: str,
depth: Optional[int] = 1,
user: Optional[Username] = None,
request_timeout: Optional[float] = None) -> List[EntryInfo]
```
List entries in a directory.
**Arguments**:
* `path`: Path to the directory
* `depth`: Depth of the directory to list
* `user`: Run the operation as this user
* `request_timeout`: Timeout for the request in **seconds**
**Returns**:
List of entries in the directory
### exists
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def exists(path: str,
user: Optional[Username] = None,
request_timeout: Optional[float] = None) -> bool
```
Check if a file or a directory exists.
**Arguments**:
* `path`: Path to a file or a directory
* `user`: Run the operation as this user
* `request_timeout`: Timeout for the request in **seconds**
**Returns**:
`True` if the file or directory exists, `False` otherwise
### get\_info
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def get_info(path: str,
user: Optional[Username] = None,
request_timeout: Optional[float] = None) -> EntryInfo
```
Get information about a file or directory.
**Arguments**:
* `path`: Path to a file or a directory
* `user`: Run the operation as this user
* `request_timeout`: Timeout for the request in **seconds**
**Returns**:
Information about the file or directory like name, type, and path
### remove
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def remove(path: str,
user: Optional[Username] = None,
request_timeout: Optional[float] = None) -> None
```
Remove a file or a directory.
**Arguments**:
* `path`: Path to a file or a directory
* `user`: Run the operation as this user
* `request_timeout`: Timeout for the request in **seconds**
### rename
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def rename(old_path: str,
new_path: str,
user: Optional[Username] = None,
request_timeout: Optional[float] = None) -> EntryInfo
```
Rename a file or directory.
**Arguments**:
* `old_path`: Path to the file or directory to rename
* `new_path`: New path to the file or directory
* `user`: Run the operation as this user
* `request_timeout`: Timeout for the request in **seconds**
**Returns**:
Information about the renamed file or directory
### make\_dir
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def make_dir(path: str,
user: Optional[Username] = None,
request_timeout: Optional[float] = None) -> bool
```
Create a new directory and all directories along the way if needed on the specified path.
**Arguments**:
* `path`: Path to a new directory. For example '/dirA/dirB' when creating 'dirB'.
* `user`: Run the operation as this user
* `request_timeout`: Timeout for the request in **seconds**
**Returns**:
`True` if the directory was created, `False` if the directory already exists
### watch\_dir
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def watch_dir(path: str,
user: Optional[Username] = None,
request_timeout: Optional[float] = None,
recursive: bool = False,
include_entry: bool = False,
allow_network_mounts: bool = False) -> WatchHandle
```
Watch directory for filesystem events.
**Arguments**:
* `path`: Path to a directory to watch
* `user`: Run the operation as this user
* `request_timeout`: Timeout for the request in **seconds**
* `recursive`: Watch directory recursively
* `include_entry`: Include the `EntryInfo` of the affected entry in each event, when available. Requires envd 0.6.3 or later
* `allow_network_mounts`: Allow watching paths on network filesystem mounts (NFS, CIFS, SMB, FUSE), which are rejected by default. Events on network mounts may be unreliable or not delivered at all. Requires envd 0.6.4 or later
**Returns**:
`WatchHandle` object for stopping watching directory
## WatchHandle
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
class WatchHandle()
```
Handle for watching filesystem events.
It is used to get the latest events that have occurred in the watched directory.
Use `.stop()` to stop watching the directory.
### stop
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def stop(request_timeout: Optional[float] = None)
```
Stop watching the directory.
After you stop the watcher you won't be able to get the events anymore.
**Arguments**:
* `request_timeout`: Timeout for the request in **seconds**
### get\_new\_events
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def get_new_events(
request_timeout: Optional[float] = None) -> List[FilesystemEvent]
```
Get the latest events that have occurred in the watched directory since the last call, or from the beginning of the watching, up until now.
**Arguments**:
* `request_timeout`: Timeout for the request in **seconds**
**Returns**:
List of filesystem events
## SandboxApi
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
class SandboxApi(SandboxBase)
```
### list
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@classmethod
def list(cls,
query: Optional[SandboxQuery] = None,
limit: Optional[int] = None,
next_token: Optional[str] = None,
order: Optional[SandboxListOrder] = None,
**opts: Unpack[ApiParams]) -> SandboxPaginator
```
List sandboxes.
By default (no `query.state` set), returns sandboxes in both `running`
and `paused` states. To filter by state, pass `query=SandboxQuery(state=[...])`.
**Arguments**:
* `query`: Filter the list of sandboxes by metadata, state, start time, or template, e.g. `SandboxQuery(metadata={"key": "value"})` or `SandboxQuery(state=[SandboxState.RUNNING])`
* `limit`: Maximum number of sandboxes to return per page
* `next_token`: Token for pagination
* `order`: Sort order of the list of sandboxes by start time, applied across the whole result set before pagination (not within a page)
**Returns**:
A `SandboxPaginator` that yields pages of sandboxes (running and paused by default). Iterate pages via `paginator.next_items()` while `paginator.has_next` is True.
## Pty
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
class Pty()
```
Module for interacting with PTYs (pseudo-terminals) in the sandbox.
### kill
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def kill(pid: int, request_timeout: Optional[float] = None) -> bool
```
Kill PTY.
**Arguments**:
* `pid`: Process ID of the PTY
* `request_timeout`: Timeout for the request in **seconds**
**Returns**:
`true` if the PTY was killed, `false` if the PTY was not found
### send\_stdin
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def send_stdin(pid: int,
data: bytes,
request_timeout: Optional[float] = None) -> None
```
Send input to a PTY.
**Arguments**:
* `pid`: Process ID of the PTY
* `data`: Input data to send
* `request_timeout`: Timeout for the request in **seconds**
### create
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def create(size: PtySize,
user: Optional[Username] = None,
cwd: Optional[str] = None,
envs: Optional[Dict[str, str]] = None,
timeout: Optional[float] = 60,
request_timeout: Optional[float] = None) -> CommandHandle
```
Start a new PTY (pseudo-terminal).
**Arguments**:
* `size`: Size of the PTY
* `user`: User to use for the PTY
* `cwd`: Working directory for the PTY
* `envs`: Environment variables for the PTY
* `timeout`: Timeout for the PTY in **seconds**
* `request_timeout`: Not applied to this streaming call — both opening the stream and the stream itself are bounded by `timeout` (unlimited when `0`)
**Returns**:
Handle to interact with the PTY
### connect
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def connect(pid: int,
timeout: Optional[float] = 60,
request_timeout: Optional[float] = None) -> CommandHandle
```
Connect to a running PTY.
**Arguments**:
* `pid`: Process ID of the PTY to connect to. You can get the list of running PTYs using `sandbox.pty.list()`.
* `timeout`: Timeout for the PTY connection in **seconds**. Using `0` will not limit the connection time
* `request_timeout`: Not applied to this streaming call — both opening the stream and the stream itself are bounded by `timeout` (unlimited when `0`)
**Returns**:
Handle to interact with the PTY
### resize
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def resize(pid: int,
size: PtySize,
request_timeout: Optional[float] = None) -> None
```
Resize PTY.
Call this when the terminal window is resized and the number of columns and rows has changed.
**Arguments**:
* `pid`: Process ID of the PTY
* `size`: New size of the PTY
* `request_timeout`: Timeout for the request in **seconds**
## Commands
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
class Commands()
```
Module for executing commands in the sandbox.
### list
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def list(request_timeout: Optional[float] = None) -> List[ProcessInfo]
```
Lists all running commands and PTY sessions.
**Arguments**:
* `request_timeout`: Timeout for the request in **seconds**
**Returns**:
List of running commands and PTY sessions
### kill
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def kill(pid: int, request_timeout: Optional[float] = None) -> bool
```
Kill a running command specified by its process ID.
It uses `SIGKILL` signal to kill the command.
**Arguments**:
* `pid`: Process ID of the command. You can get the list of processes using `sandbox.commands.list()`
* `request_timeout`: Timeout for the request in **seconds**
**Returns**:
`True` if the command was killed, `False` if the command was not found
### send\_stdin
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def send_stdin(pid: int,
data: Union[str, bytes],
request_timeout: Optional[float] = None)
```
Send data to command stdin.
:param pid Process ID of the command. You can get the list of processes using `sandbox.commands.list()`.
:param data: Data to send to the command
:param request\_timeout: Timeout for the request in **seconds**
### close\_stdin
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def close_stdin(pid: int, request_timeout: Optional[float] = None) -> None
```
Close the command stdin.
This signals EOF to the command. The command must have been started with `stdin=True`.
:param pid Process ID of the command. You can get the list of processes using `sandbox.commands.list()`.
:param request\_timeout: Timeout for the request in **seconds**
### run
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@overload
def run(cmd: str,
background: Union[Literal[False], None] = None,
envs: Optional[Dict[str, str]] = None,
user: Optional[Username] = None,
cwd: Optional[str] = None,
on_stdout: Optional[Callable[[str], None]] = None,
on_stderr: Optional[Callable[[str], None]] = None,
stdin: Optional[bool] = None,
timeout: Optional[float] = 60,
request_timeout: Optional[float] = None) -> CommandResult
```
Start a new command and wait until it finishes executing.
**Arguments**:
* `cmd`: Command to execute
* `background`: **`False` if the command should be executed in the foreground**, `True` if the command should be executed in the background
* `envs`: Environment variables used for the command
* `user`: User to run the command as
* `cwd`: Working directory to run the command
* `on_stdout`: Callback for command stdout output
* `on_stderr`: Callback for command stderr output
* `stdin`: If `True`, the command will have a stdin stream that you can send data to using `sandbox.commands.send_stdin()`
* `timeout`: Timeout for the command connection in **seconds**. Using `0` will not limit the command connection time
* `request_timeout`: Not applied to this streaming call — both opening the stream and the stream itself are bounded by `timeout` (unlimited when `0`)
**Returns**:
`CommandResult` result of the command execution
### run
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@overload
def run(cmd: str,
background: Literal[True],
envs: Optional[Dict[str, str]] = None,
user: Optional[Username] = None,
cwd: Optional[str] = None,
on_stdout: None = None,
on_stderr: None = None,
stdin: Optional[bool] = None,
timeout: Optional[float] = 60,
request_timeout: Optional[float] = None) -> CommandHandle
```
Start a new command and return a handle to interact with it.
**Arguments**:
* `cmd`: Command to execute
* `background`: `False` if the command should be executed in the foreground, **`True` if the command should be executed in the background**
* `envs`: Environment variables used for the command
* `user`: User to run the command as
* `cwd`: Working directory to run the command
* `stdin`: If `True`, the command will have a stdin stream that you can send data to using `sandbox.commands.send_stdin()`
* `timeout`: Timeout for the command connection in **seconds**. Using `0` will not limit the command connection time
* `request_timeout`: Not applied to this streaming call — both opening the stream and the stream itself are bounded by `timeout` (unlimited when `0`)
**Returns**:
`CommandHandle` handle to interact with the running command
### connect
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def connect(pid: int,
timeout: Optional[float] = 60,
request_timeout: Optional[float] = None)
```
Connects to a running command.
You can use `CommandHandle.wait()` to wait for the command to finish and get execution results.
**Arguments**:
* `pid`: Process ID of the command to connect to. You can get the list of processes using `sandbox.commands.list()`
* `timeout`: Timeout for the connection in **seconds**. Using `0` will not limit the connection time
* `request_timeout`: Not applied to this streaming call — both opening the stream and the stream itself are bounded by `timeout` (unlimited when `0`)
**Returns**:
`CommandHandle` handle to interact with the running command
## CommandHandle
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
class CommandHandle()
```
Command execution handle.
It provides methods for waiting for the command to finish, retrieving stdout/stderr, and killing the command.
### pid
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@property
def pid()
```
Command process ID.
### \_\_iter\_\_
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def __iter__()
```
Iterate over the command output.
**Returns**:
Generator of command outputs
### disconnect
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def disconnect() -> None
```
Disconnect from the command.
The command is not killed, but SDK stops receiving events from the command.
You can reconnect to the command using `sandbox.commands.connect` method.
### wait
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def wait(on_pty: Optional[Callable[[PtyOutput], None]] = None,
on_stdout: Optional[Callable[[str], None]] = None,
on_stderr: Optional[Callable[[str], None]] = None) -> CommandResult
```
Wait for the command to finish and returns the result.
If the command exits with a non-zero exit code, it throws a `CommandExitException`.
**Arguments**:
* `on_pty`: Callback for pty output
* `on_stdout`: Callback for stdout output
* `on_stderr`: Callback for stderr output
**Returns**:
`CommandResult` result of command execution
### kill
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def kill() -> bool
```
Kills the command.
It uses `SIGKILL` signal to kill the command.
**Returns**:
Whether the command was killed successfully
### send\_stdin
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def send_stdin(data: Union[str, bytes],
request_timeout: Optional[float] = None) -> None
```
Send data to the command stdin.
The command must have been started with `stdin=True`.
**Arguments**:
* `data`: Data to send to the command
* `request_timeout`: Timeout for the request in **seconds**
### close\_stdin
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def close_stdin(request_timeout: Optional[float] = None) -> None
```
Close the command stdin.
This signals EOF to the command. The command must have been started with `stdin=True`.
**Arguments**:
* `request_timeout`: Timeout for the request in **seconds**
## Git
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
class Git()
```
Module for running git operations in the sandbox.
:deprecated: Run git with `sandbox.commands.run()` instead. The git module will be removed in the next major version.
### \_\_init\_\_
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def __init__(commands: Commands) -> None
```
Create a Git helper bound to the sandbox command runner.
**Arguments**:
* `commands`: Command runner used to execute git commands
### clone
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def clone(url: str,
path: Optional[str] = None,
branch: Optional[str] = None,
depth: Optional[int] = None,
username: Optional[str] = None,
password: Optional[str] = None,
envs: Optional[Dict[str, str]] = None,
user: Optional[str] = None,
cwd: Optional[str] = None,
timeout: Optional[float] = None,
request_timeout: Optional[float] = None,
dangerously_store_credentials: bool = False)
```
Clone a git repository into the sandbox.
**Arguments**:
* `url`: Git repository URL
* `path`: Destination path for the clone
* `branch`: Branch to check out
* `depth`: If set, perform a shallow clone with this depth
* `username`: Username for HTTP(S) authentication
* `password`: Password or token for HTTP(S) authentication
* `envs`: Environment variables used for the command
* `user`: User to run the command as
* `cwd`: Working directory to run the command
* `timeout`: Timeout for the command connection in **seconds**
* `request_timeout`: Timeout for the request in **seconds**
* `dangerously_store_credentials`: Store credentials in the cloned repository when True
**Returns**:
Command result from the command runner
### init
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def init(path: str,
bare: bool = False,
initial_branch: Optional[str] = None,
envs: Optional[Dict[str, str]] = None,
user: Optional[str] = None,
cwd: Optional[str] = None,
timeout: Optional[float] = None,
request_timeout: Optional[float] = None)
```
Initialize a new git repository.
**Arguments**:
* `path`: Destination path for the repository
* `bare`: Create a bare repository when True
* `initial_branch`: Initial branch name (for example, "main")
* `envs`: Environment variables used for the command
* `user`: User to run the command as
* `cwd`: Working directory to run the command
* `timeout`: Timeout for the command connection in **seconds**
* `request_timeout`: Timeout for the request in **seconds**
**Returns**:
Command result from the command runner
### remote\_add
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def remote_add(path: str,
name: str,
url: str,
fetch: bool = False,
overwrite: bool = False,
envs: Optional[Dict[str, str]] = None,
user: Optional[str] = None,
cwd: Optional[str] = None,
timeout: Optional[float] = None,
request_timeout: Optional[float] = None)
```
Add (or update) a remote for a repository.
**Arguments**:
* `path`: Repository path
* `name`: Remote name (for example, "origin")
* `url`: Remote URL
* `fetch`: Fetch the remote after adding it when True
* `overwrite`: Overwrite the remote URL if it already exists when True
* `envs`: Environment variables used for the command
* `user`: User to run the command as
* `cwd`: Working directory to run the command
* `timeout`: Timeout for the command connection in **seconds**
* `request_timeout`: Timeout for the request in **seconds**
**Returns**:
Command result from the command runner
### remote\_get
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def remote_get(path: str,
name: str,
envs: Optional[Dict[str, str]] = None,
user: Optional[str] = None,
cwd: Optional[str] = None,
timeout: Optional[float] = None,
request_timeout: Optional[float] = None) -> Optional[str]
```
Get the URL for a git remote.
Returns `None` when the remote does not exist.
**Arguments**:
* `path`: Repository path
* `name`: Remote name (for example, "origin")
* `envs`: Environment variables used for the command
* `user`: User to run the command as
* `cwd`: Working directory to run the command
* `timeout`: Timeout for the command connection in **seconds**
* `request_timeout`: Timeout for the request in **seconds**
**Returns**:
Remote URL if present, otherwise `None`
### status
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def status(path: str,
envs: Optional[Dict[str, str]] = None,
user: Optional[str] = None,
cwd: Optional[str] = None,
timeout: Optional[float] = None,
request_timeout: Optional[float] = None) -> GitStatus
```
Get repository status information.
**Arguments**:
* `path`: Repository path
* `envs`: Environment variables used for the command
* `user`: User to run the command as
* `cwd`: Working directory to run the command
* `timeout`: Timeout for the command connection in **seconds**
* `request_timeout`: Timeout for the request in **seconds**
**Returns**:
Parsed git status
### branches
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def branches(path: str,
envs: Optional[Dict[str, str]] = None,
user: Optional[str] = None,
cwd: Optional[str] = None,
timeout: Optional[float] = None,
request_timeout: Optional[float] = None) -> GitBranches
```
List branches in a repository.
**Arguments**:
* `path`: Repository path
* `envs`: Environment variables used for the command
* `user`: User to run the command as
* `cwd`: Working directory to run the command
* `timeout`: Timeout for the command connection in **seconds**
* `request_timeout`: Timeout for the request in **seconds**
**Returns**:
Parsed branch list
### create\_branch
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def create_branch(path: str,
branch: str,
envs: Optional[Dict[str, str]] = None,
user: Optional[str] = None,
cwd: Optional[str] = None,
timeout: Optional[float] = None,
request_timeout: Optional[float] = None)
```
Create and check out a new branch.
**Arguments**:
* `path`: Repository path
* `branch`: Branch name to create
* `envs`: Environment variables used for the command
* `user`: User to run the command as
* `cwd`: Working directory to run the command
* `timeout`: Timeout for the command connection in **seconds**
* `request_timeout`: Timeout for the request in **seconds**
**Returns**:
Command result from the command runner
### checkout\_branch
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def checkout_branch(path: str,
branch: str,
envs: Optional[Dict[str, str]] = None,
user: Optional[str] = None,
cwd: Optional[str] = None,
timeout: Optional[float] = None,
request_timeout: Optional[float] = None)
```
Check out an existing branch.
**Arguments**:
* `path`: Repository path
* `branch`: Branch name to check out
* `envs`: Environment variables used for the command
* `user`: User to run the command as
* `cwd`: Working directory to run the command
* `timeout`: Timeout for the command connection in **seconds**
* `request_timeout`: Timeout for the request in **seconds**
**Returns**:
Command result from the command runner
### delete\_branch
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def delete_branch(path: str,
branch: str,
force: bool = False,
envs: Optional[Dict[str, str]] = None,
user: Optional[str] = None,
cwd: Optional[str] = None,
timeout: Optional[float] = None,
request_timeout: Optional[float] = None)
```
Delete a branch.
**Arguments**:
* `path`: Repository path
* `branch`: Branch name to delete
* `force`: Force deletion with `-D` when `True`
* `envs`: Environment variables used for the command
* `user`: User to run the command as
* `cwd`: Working directory to run the command
* `timeout`: Timeout for the command connection in **seconds**
* `request_timeout`: Timeout for the request in **seconds**
**Returns**:
Command result from the command runner
### add
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def add(path: str,
files: Optional[List[str]] = None,
all: bool = True,
envs: Optional[Dict[str, str]] = None,
user: Optional[str] = None,
cwd: Optional[str] = None,
timeout: Optional[float] = None,
request_timeout: Optional[float] = None)
```
Stage files for commit.
**Arguments**:
* `path`: Repository path
* `files`: Files to add; when omitted, adds the current directory
* `all`: When `True` and `files` is omitted, stage all changes
* `envs`: Environment variables used for the command
* `user`: User to run the command as
* `cwd`: Working directory to run the command
* `timeout`: Timeout for the command connection in **seconds**
* `request_timeout`: Timeout for the request in **seconds**
**Returns**:
Command result from the command runner
### commit
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def commit(path: str,
message: str,
author_name: Optional[str] = None,
author_email: Optional[str] = None,
allow_empty: bool = False,
envs: Optional[Dict[str, str]] = None,
user: Optional[str] = None,
cwd: Optional[str] = None,
timeout: Optional[float] = None,
request_timeout: Optional[float] = None)
```
Create a commit in the repository.
**Arguments**:
* `path`: Repository path
* `message`: Commit message
* `author_name`: Commit author name
* `author_email`: Commit author email
* `allow_empty`: Allow empty commits when `True`
* `envs`: Environment variables used for the command
* `user`: User to run the command as
* `cwd`: Working directory to run the command
* `timeout`: Timeout for the command connection in **seconds**
* `request_timeout`: Timeout for the request in **seconds**
**Returns**:
Command result from the command runner
### reset
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def reset(path: str,
mode: Optional[GitResetMode] = None,
target: Optional[str] = None,
paths: Optional[List[str]] = None,
envs: Optional[Dict[str, str]] = None,
user: Optional[str] = None,
cwd: Optional[str] = None,
timeout: Optional[float] = None,
request_timeout: Optional[float] = None)
```
Reset the current HEAD to a specified state.
**Arguments**:
* `path`: Repository path
* `mode`: Reset mode (soft, mixed, hard, merge, keep)
* `target`: Commit, branch, or ref to reset to (defaults to HEAD)
* `paths`: Paths to reset
* `envs`: Environment variables used for the command
* `user`: User to run the command as
* `cwd`: Working directory to run the command
* `timeout`: Timeout for the command connection in **seconds**
* `request_timeout`: Timeout for the request in **seconds**
**Returns**:
Command result from the command runner
### restore
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def restore(path: str,
paths: List[str],
staged: Optional[bool] = None,
worktree: Optional[bool] = None,
source: Optional[str] = None,
envs: Optional[Dict[str, str]] = None,
user: Optional[str] = None,
cwd: Optional[str] = None,
timeout: Optional[float] = None,
request_timeout: Optional[float] = None)
```
Restore working tree files or unstage changes.
**Arguments**:
* `path`: Repository path
* `paths`: Paths to restore (use \["."] for all)
* `staged`: When True, restore the index (unstage)
* `worktree`: When True, restore working tree files
* `source`: Restore from the given source (commit, branch, or ref)
* `envs`: Environment variables used for the command
* `user`: User to run the command as
* `cwd`: Working directory to run the command
* `timeout`: Timeout for the command connection in **seconds**
* `request_timeout`: Timeout for the request in **seconds**
**Returns**:
Command result from the command runner
### push
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def push(path: str,
remote: Optional[str] = None,
branch: Optional[str] = None,
set_upstream: bool = True,
username: Optional[str] = None,
password: Optional[str] = None,
envs: Optional[Dict[str, str]] = None,
user: Optional[str] = None,
cwd: Optional[str] = None,
timeout: Optional[float] = None,
request_timeout: Optional[float] = None)
```
Push commits to a remote.
**Arguments**:
* `path`: Repository path
* `remote`: Remote name, e.g. `origin`
* `branch`: Branch name to push
* `set_upstream`: Set upstream tracking when `True`
* `username`: Username for HTTP(S) authentication
* `password`: Password or token for HTTP(S) authentication
* `envs`: Environment variables used for the command
* `user`: User to run the command as
* `cwd`: Working directory to run the command
* `timeout`: Timeout for the command connection in **seconds**
* `request_timeout`: Timeout for the request in **seconds**
**Returns**:
Command result from the command runner
### pull
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def pull(path: str,
remote: Optional[str] = None,
branch: Optional[str] = None,
username: Optional[str] = None,
password: Optional[str] = None,
envs: Optional[Dict[str, str]] = None,
user: Optional[str] = None,
cwd: Optional[str] = None,
timeout: Optional[float] = None,
request_timeout: Optional[float] = None)
```
Pull changes from a remote.
**Arguments**:
* `path`: Repository path
* `remote`: Remote name, e.g. `origin`
* `branch`: Branch name to pull
* `username`: Username for HTTP(S) authentication
* `password`: Password or token for HTTP(S) authentication
* `envs`: Environment variables used for the command
* `user`: User to run the command as
* `cwd`: Working directory to run the command
* `timeout`: Timeout for the command connection in **seconds**
* `request_timeout`: Timeout for the request in **seconds**
**Returns**:
Command result from the command runner
### set\_config
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def set_config(key: str,
value: str,
scope: str = "global",
path: Optional[str] = None,
envs: Optional[Dict[str, str]] = None,
user: Optional[str] = None,
cwd: Optional[str] = None,
timeout: Optional[float] = None,
request_timeout: Optional[float] = None)
```
Set a git config value.
Use `scope="local"` together with `path` to configure a specific repository.
**Arguments**:
* `key`: Git config key (e.g. `pull.rebase`)
* `value`: Git config value
* `scope`: Config scope: `global`, `local`, or `system`
* `path`: Repository path required when `scope` is `local`
* `envs`: Environment variables used for the command
* `user`: User to run the command as
* `cwd`: Working directory to run the command
* `timeout`: Timeout for the command connection in **seconds**
* `request_timeout`: Timeout for the request in **seconds**
**Returns**:
Command result from the command runner
### get\_config
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def get_config(key: str,
scope: str = "global",
path: Optional[str] = None,
envs: Optional[Dict[str, str]] = None,
user: Optional[str] = None,
cwd: Optional[str] = None,
timeout: Optional[float] = None,
request_timeout: Optional[float] = None) -> Optional[str]
```
Get a git config value.
Returns `None` when the key is not set in the requested scope.
**Arguments**:
* `key`: Git config key (e.g. `pull.rebase`)
* `scope`: Config scope: `global`, `local`, or `system`
* `path`: Repository path required when `scope` is `local`
* `envs`: Environment variables used for the command
* `user`: User to run the command as
* `cwd`: Working directory to run the command
* `timeout`: Timeout for the command connection in **seconds**
* `request_timeout`: Timeout for the request in **seconds**
**Returns**:
Config value if present, otherwise `None`
### dangerously\_authenticate
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def dangerously_authenticate(username: str,
password: str,
host: str = "github.com",
protocol: str = "https",
envs: Optional[Dict[str, str]] = None,
user: Optional[str] = None,
cwd: Optional[str] = None,
timeout: Optional[float] = None,
request_timeout: Optional[float] = None)
```
Dangerously authenticate git globally via the credential helper.
This persists credentials in the credential store and may be accessible to agents running on the sandbox.
Prefer short-lived credentials when possible.
**Arguments**:
* `username`: Username for HTTP(S) authentication
* `password`: Password or token for HTTP(S) authentication
* `host`: Host to authenticate for, defaults to `github.com`
* `protocol`: Protocol to authenticate for, defaults to `https`
* `envs`: Environment variables used for the command
* `user`: User to run the command as
* `cwd`: Working directory to run the command
* `timeout`: Timeout for the command connection in **seconds**
* `request_timeout`: Timeout for the request in **seconds**
**Returns**:
Command result from the command runner
### configure\_user
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def configure_user(name: str,
email: str,
scope: str = "global",
path: Optional[str] = None,
envs: Optional[Dict[str, str]] = None,
user: Optional[str] = None,
cwd: Optional[str] = None,
timeout: Optional[float] = None,
request_timeout: Optional[float] = None)
```
Configure git user name and email.
**Arguments**:
* `name`: Git user name
* `email`: Git user email
* `scope`: Config scope: `global`, `local`, or `system`
* `path`: Repository path required when `scope` is `local`
* `envs`: Environment variables used for the command
* `user`: User to run the command as
* `cwd`: Working directory to run the command
* `timeout`: Timeout for the command connection in **seconds**
* `request_timeout`: Timeout for the request in **seconds**
**Returns**:
Command result from the command runner
# Secret
Source: https://docs.e2b.dev/sdk-reference/python-sdk/v2.56.0/secret
## SecretPaginator
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
class SecretPaginator(SecretPaginatorBase)
```
Paginator for listing secrets.
**Example**:
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
paginator = Secret.list()
while paginator.has_next:
secrets = paginator.next_items()
print(secrets)
```
### next\_items
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def next_items(**opts: Unpack[ApiParams]) -> List[SecretInfo]
```
Returns the next page of secrets.
Call this method only if `has_next` is `True`, otherwise it will raise an exception.
**Arguments**:
* `opts`: Per-call connection options (e.g. `api_key`, `domain`,
`headers`, `request_timeout`). When provided, this call uses these
options instead of the ones the paginator was constructed with.
**Returns**:
List of secret metadata.
## Secret
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
class Secret(SecretBase)
```
Module for managing E2B secrets and workload identity helpers.
Secret values are write-only: they are accepted by `create` and
`update` but never returned by any read surface.
### create
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@classmethod
def create(cls,
name: str,
value: str,
metadata: Optional[Dict[str, str]] = None,
**opts: Unpack[ApiParams]) -> SecretInfo
```
Create a new secret and its first value.
**Arguments**:
* `name`: Name of the secret, unique within the project.
* `value`: Secret value. Write-only — never returned by the API.
* `metadata`: Customer metadata to store with the secret.
**Returns**:
The secret's ID, name, current version (`1` for a new
secret), metadata, and creation and update times.
### update
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@classmethod
def update(cls,
secret: str,
value: str,
metadata: Optional[Dict[str, str]] = None,
**opts: Unpack[ApiParams]) -> SecretInfo
```
Update a secret's value by storing it as the secret's new version.
**Arguments**:
* `secret`: Secret ID or name.
* `value`: New secret value. Write-only — never returned by the API.
* `metadata`: Customer metadata to store with the secret. When
provided, replaces the stored metadata.
**Returns**:
The secret's ID, name, new current version, metadata, and
creation and update times.
### get\_info
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@classmethod
def get_info(cls, secret: str, **opts: Unpack[ApiParams]) -> SecretInfo
```
Get a secret's metadata.
**Arguments**:
* `secret`: Secret ID or name.
**Returns**:
The secret's ID, name, current version, metadata, and
creation and update times.
### list
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@classmethod
def list(cls,
limit: Optional[int] = None,
next_token: Optional[str] = None,
**opts: Unpack[ApiParams]) -> SecretPaginator
```
List the project's secrets.
**Arguments**:
* `limit`: Number of secrets to return per page.
* `next_token`: Token to the next page.
**Returns**:
Paginator over the project's secrets. Drain it page by page:
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
paginator = Secret.list(limit=50)
while paginator.has_next:
secrets = paginator.next_items()
```
### exists
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@classmethod
def exists(cls, secret: str, **opts: Unpack[ApiParams]) -> bool
```
Check whether a secret exists.
**Arguments**:
* `secret`: Secret ID or name.
**Returns**:
`True` if the secret exists, `False` otherwise.
### destroy
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@classmethod
def destroy(cls, secret: str, **opts: Unpack[ApiParams]) -> bool
```
Destroy a secret, making all its versions inaccessible.
**Arguments**:
* `secret`: Secret ID or name.
**Returns**:
`True` if the secret was destroyed, `False` if it was not found.
## SecretBase
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
class SecretBase(ClientFactory)
```
Module for managing E2B secrets and workload identity helpers.
Secret values are write-only: they are accepted by `create` and
`update` but never returned by any read surface.
### fill
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@staticmethod
def fill(secret: str) -> str
```
Format a placeholder that the runtime resolves to the secret's
current value.
This is a local formatting helper and makes no network call — it does
not check whether the named secret exists. An unknown reference fails
server-side when the placeholder is resolved.
**Arguments**:
* `secret`: Secret name.
**Returns**:
Placeholder string resolving to the secret's value.
Example:
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
Secret.fill("openai-api-key")
```
### iam\_token
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@staticmethod
def iam_token(*, audience: str,
token_type: SandboxIamTokenType) -> SandboxIamToken
```
Define a workload identity token to pass to `iam.tokens` when
creating a sandbox.
**Arguments**:
* `audience`: Audience of the workload token, stored exactly as provided.
* `token_type`: Workload token type.
**Returns**:
A token definition passable to `iam.tokens`.
Example:
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
sandbox = Sandbox.create(
iam={
"tokens": {
"aws": Secret.iam_token(
audience="sts.amazonaws.com",
token_type="JWT-SVID",
),
},
},
)
```
## SecretInfo
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@dataclass
class SecretInfo()
```
Metadata of a secret. Secret values are write-only and never returned.
### secret\_id
Secret ID.
### name
Secret name, unique within the project.
### version
Version served to readers that do not name one.
### metadata
Customer metadata of the secret.
### created\_at
Time when the secret was created.
### updated\_at
Time when the secret was last updated.
## AsyncSecretPaginator
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
class AsyncSecretPaginator(SecretPaginatorBase)
```
Paginator for listing secrets.
**Example**:
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
paginator = AsyncSecret.list()
while paginator.has_next:
secrets = await paginator.next_items()
print(secrets)
```
### next\_items
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
async def next_items(**opts: Unpack[ApiParams]) -> List[SecretInfo]
```
Returns the next page of secrets.
Call this method only if `has_next` is `True`, otherwise it will raise an exception.
**Arguments**:
* `opts`: Per-call connection options (e.g. `api_key`, `domain`,
`headers`, `request_timeout`). When provided, this call uses these
options instead of the ones the paginator was constructed with.
**Returns**:
List of secret metadata.
## AsyncSecret
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
class AsyncSecret(SecretBase)
```
Module for managing E2B secrets and workload identity helpers.
Secret values are write-only: they are accepted by `create` and
`update` but never returned by any read surface.
### create
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@classmethod
async def create(cls,
name: str,
value: str,
metadata: Optional[Dict[str, str]] = None,
**opts: Unpack[ApiParams]) -> SecretInfo
```
Create a new secret and its first value.
**Arguments**:
* `name`: Name of the secret, unique within the project.
* `value`: Secret value. Write-only — never returned by the API.
* `metadata`: Customer metadata to store with the secret.
**Returns**:
The secret's ID, name, current version (`1` for a new
secret), metadata, and creation and update times.
### update
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@classmethod
async def update(cls,
secret: str,
value: str,
metadata: Optional[Dict[str, str]] = None,
**opts: Unpack[ApiParams]) -> SecretInfo
```
Update a secret's value by storing it as the secret's new version.
**Arguments**:
* `secret`: Secret ID or name.
* `value`: New secret value. Write-only — never returned by the API.
* `metadata`: Customer metadata to store with the secret. When
provided, replaces the stored metadata.
**Returns**:
The secret's ID, name, new current version, metadata, and
creation and update times.
### get\_info
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@classmethod
async def get_info(cls, secret: str, **opts: Unpack[ApiParams]) -> SecretInfo
```
Get a secret's metadata.
**Arguments**:
* `secret`: Secret ID or name.
**Returns**:
The secret's ID, name, current version, metadata, and
creation and update times.
### list
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@classmethod
def list(cls,
limit: Optional[int] = None,
next_token: Optional[str] = None,
**opts: Unpack[ApiParams]) -> AsyncSecretPaginator
```
List the project's secrets.
**Arguments**:
* `limit`: Number of secrets to return per page.
* `next_token`: Token to the next page.
**Returns**:
Paginator over the project's secrets. Drain it page by page:
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
paginator = AsyncSecret.list(limit=50)
while paginator.has_next:
secrets = await paginator.next_items()
```
### exists
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@classmethod
async def exists(cls, secret: str, **opts: Unpack[ApiParams]) -> bool
```
Check whether a secret exists.
**Arguments**:
* `secret`: Secret ID or name.
**Returns**:
`True` if the secret exists, `False` otherwise.
### destroy
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@classmethod
async def destroy(cls, secret: str, **opts: Unpack[ApiParams]) -> bool
```
Destroy a secret, making all its versions inaccessible.
**Arguments**:
* `secret`: Secret ID or name.
**Returns**:
`True` if the secret was destroyed, `False` if it was not found.
# Template
Source: https://docs.e2b.dev/sdk-reference/python-sdk/v2.56.0/template
## TemplateBuilder
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
class TemplateBuilder()
```
Builder class for adding instructions to an E2B template.
All methods return self to allow method chaining.
### copy
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def copy(src: Union[Union[str, Path], List[Union[str, Path]]],
dest: Union[str, Path],
force_upload: Optional[Literal[True]] = None,
user: Optional[str] = None,
mode: Optional[int] = None,
resolve_symlinks: Optional[bool] = None,
gzip: Optional[bool] = None) -> "TemplateBuilder"
```
Copy files or directories from the local filesystem into the template.
**Arguments**:
* `src`: Source file(s) or directory path(s) to copy
* `dest`: Destination path in the template
* `force_upload`: Force upload even if files are cached
* `user`: User and optionally group (user:group) to own the files
* `mode`: File permissions in octal format (e.g., 0o755)
* `resolve_symlinks`: Whether to resolve symlinks
* `gzip`: Whether to gzip the files before upload (default: True)
**Returns**:
`TemplateBuilder` class
Example
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
template.copy('requirements.txt', '/home/user/')
template.copy(['app.py', 'config.py'], '/app/', mode=0o755)
```
### copy\_items
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def copy_items(items: List[CopyItem]) -> "TemplateBuilder"
```
Copy multiple files or directories using a list of copy items.
**Arguments**:
* `items`: List of CopyItem dictionaries with src, dest, and optional parameters
**Returns**:
`TemplateBuilder` class
Example
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
template.copy_items([
{'src': 'app.py', 'dest': '/app/'},
{'src': 'config.py', 'dest': '/app/', 'mode': 0o644}
])
```
### remove
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def remove(path: Union[Union[str, Path], List[Union[str, Path]]],
force: bool = False,
recursive: bool = False,
user: Optional[str] = None) -> "TemplateBuilder"
```
Remove files or directories in the template.
**Arguments**:
* `path`: File(s) or directory path(s) to remove
* `force`: Force removal without prompting
* `recursive`: Remove directories recursively
* `user`: User to run the command as
**Returns**:
`TemplateBuilder` class
Example
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
template.remove('/tmp/cache', recursive=True, force=True)
template.remove('/tmp/cache', recursive=True, force=True, user='root')
```
### rename
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def rename(src: Union[str, Path],
dest: Union[str, Path],
force: bool = False,
user: Optional[str] = None) -> "TemplateBuilder"
```
Rename or move a file or directory in the template.
**Arguments**:
* `src`: Source path
* `dest`: Destination path
* `force`: Force rename without prompting
* `user`: User to run the command as
**Returns**:
`TemplateBuilder` class
Example
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
template.rename('/tmp/old.txt', '/tmp/new.txt')
template.rename('/tmp/old.txt', '/tmp/new.txt', user='root')
```
### make\_dir
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def make_dir(path: Union[Union[str, Path], List[Union[str, Path]]],
mode: Optional[int] = None,
user: Optional[str] = None) -> "TemplateBuilder"
```
Create directory(ies) in the template.
**Arguments**:
* `path`: Directory path(s) to create
* `mode`: Directory permissions in octal format (e.g., 0o755)
* `user`: User to run the command as
**Returns**:
`TemplateBuilder` class
Example
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
template.make_dir('/app/data', mode=0o755)
template.make_dir(['/app/logs', '/app/cache'])
template.make_dir('/app/data', mode=0o755, user='root')
```
### make\_symlink
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def make_symlink(src: Union[str, Path],
dest: Union[str, Path],
user: Optional[str] = None,
force: bool = False) -> "TemplateBuilder"
```
Create a symbolic link in the template.
**Arguments**:
* `src`: Source path (target of the symlink)
* `dest`: Destination path (location of the symlink)
* `user`: User to run the command as
* `force`: Force symlink without prompting
**Returns**:
`TemplateBuilder` class
Example
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
template.make_symlink('/usr/bin/python3', '/usr/bin/python')
template.make_symlink('/usr/bin/python3', '/usr/bin/python', user='root')
template.make_symlink('/usr/bin/python3', '/usr/bin/python', force=True)
```
### run\_cmd
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def run_cmd(command: Union[str, List[str]],
user: Optional[str] = None) -> "TemplateBuilder"
```
Run a shell command during template build.
**Arguments**:
* `command`: Command string or list of commands to run (joined with &&)
* `user`: User to run the command as
**Returns**:
`TemplateBuilder` class
Example
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
template.run_cmd('apt-get update')
template.run_cmd(['pip install numpy', 'pip install pandas'])
template.run_cmd('apt-get install vim', user='root')
```
### set\_workdir
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def set_workdir(workdir: Union[str, Path]) -> "TemplateBuilder"
```
Set the working directory for subsequent commands in the template.
**Arguments**:
* `workdir`: Path to set as the working directory
**Returns**:
`TemplateBuilder` class
Example
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
template.set_workdir('/app')
```
### set\_user
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def set_user(user: str) -> "TemplateBuilder"
```
Set the user for subsequent commands in the template.
**Arguments**:
* `user`: Username to set
**Returns**:
`TemplateBuilder` class
Example
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
template.set_user('root')
```
### pip\_install
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def pip_install(packages: Optional[Union[str, List[str]]] = None,
g: bool = True) -> "TemplateBuilder"
```
Install Python packages using pip.
**Arguments**:
* `packages`: Package name(s) to install. If None, runs 'pip install .' in the current directory
* `g`: Install packages globally (default: True). If False, installs for user only
**Returns**:
`TemplateBuilder` class
Example
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
template.pip_install('numpy')
template.pip_install(['pandas', 'scikit-learn'])
template.pip_install('numpy', g=False) # Install for user only
template.pip_install() # Installs from current directory
```
### npm\_install
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def npm_install(packages: Optional[Union[str, List[str]]] = None,
g: Optional[bool] = False,
dev: Optional[bool] = False) -> "TemplateBuilder"
```
Install Node.js packages using npm.
**Arguments**:
* `packages`: Package name(s) to install. If None, installs from package.json
* `g`: Install packages globally
* `dev`: Install packages as dev dependencies
**Returns**:
`TemplateBuilder` class
Example
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
template.npm_install('express')
template.npm_install(['lodash', 'axios'])
template.npm_install('typescript', g=True)
template.npm_install() # Installs from package.json
```
### bun\_install
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def bun_install(packages: Optional[Union[str, List[str]]] = None,
g: Optional[bool] = False,
dev: Optional[bool] = False) -> "TemplateBuilder"
```
Install Bun packages using bun.
**Arguments**:
* `packages`: Package name(s) to install. If None, installs from package.json
* `g`: Install packages globally
* `dev`: Install packages as dev dependencies
**Returns**:
`TemplateBuilder` class
Example
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
template.bun_install('express')
template.bun_install(['lodash', 'axios'])
template.bun_install('tsx', g=True)
template.bun_install('typescript', dev=True)
template.bun_install() # Installs from package.json
```
### apt\_install
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def apt_install(packages: Union[str, List[str]],
no_install_recommends: bool = False,
fix_missing: bool = False) -> "TemplateBuilder"
```
Install system packages using apt-get.
**Arguments**:
* `packages`: Package name(s) to install
* `no_install_recommends`: Whether to skip installing recommended packages
* `fix_missing`: Whether to fix missing packages
**Returns**:
`TemplateBuilder` class
Example
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
template.apt_install('vim')
template.apt_install(['git', 'curl', 'wget'])
template.apt_install('vim', fix_missing=True)
```
### add\_mcp\_server
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def add_mcp_server(servers: Union[str, List[str]]) -> "TemplateBuilder"
```
Install MCP servers using mcp-gateway.
Note: Requires a base image with mcp-gateway pre-installed (e.g., mcp-gateway).
**Arguments**:
* `servers`: MCP server name(s)
**Returns**:
`TemplateBuilder` class
Example
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
template.add_mcp_server('exa')
template.add_mcp_server(['brave', 'firecrawl', 'duckduckgo'])
```
### git\_clone
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def git_clone(url: str,
path: Optional[Union[str, Path]] = None,
branch: Optional[str] = None,
depth: Optional[int] = None,
user: Optional[str] = None) -> "TemplateBuilder"
```
Clone a git repository into the template.
**Arguments**:
* `url`: Git repository URL
* `path`: Destination path for the clone
* `branch`: Branch to clone
* `depth`: Clone depth for shallow clones
* `user`: User to run the command as
**Returns**:
`TemplateBuilder` class
Example
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
template.git_clone('https://github.com/user/repo.git', '/app/repo')
template.git_clone('https://github.com/user/repo.git', branch='main', depth=1)
template.git_clone('https://github.com/user/repo.git', '/app/repo', user='root')
```
### beta\_dev\_container\_prebuild
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def beta_dev_container_prebuild(
devcontainer_directory: Union[str, Path]) -> "TemplateBuilder"
```
Prebuild a devcontainer from the specified directory during the build process.
**Arguments**:
* `devcontainer_directory`: Path to the devcontainer directory
**Returns**:
`TemplateBuilder` class
Example
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
template.git_clone('https://myrepo.com/project.git', '/my-devcontainer')
template.beta_dev_container_prebuild('/my-devcontainer')
```
### beta\_set\_dev\_container\_start
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def beta_set_dev_container_start(
devcontainer_directory: Union[str, Path]) -> "TemplateFinal"
```
Start a devcontainer from the specified directory and set it as the start command.
This method returns `TemplateFinal`, which means it must be the last method in the chain.
**Arguments**:
* `devcontainer_directory`: Path to the devcontainer directory
**Returns**:
`TemplateFinal` class
Example
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
template.git_clone('https://myrepo.com/project.git', '/my-devcontainer')
template.beta_set_devcontainer_start('/my-devcontainer')
template.git_clone('https://myrepo.com/project.git', '/my-devcontainer')
template.beta_dev_container_prebuild('/my-devcontainer')
template.beta_set_dev_container_start('/my-devcontainer')
```
### set\_envs
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def set_envs(envs: Dict[str, str]) -> "TemplateBuilder"
```
Set environment variables.
Note: Environment variables defined here are available only during template build.
**Arguments**:
* `envs`: Dictionary of environment variable names and values
**Returns**:
`TemplateBuilder` class
Example
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
template.set_envs({'NODE_ENV': 'production', 'PORT': '8080'})
```
### skip\_cache
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def skip_cache() -> "TemplateBuilder"
```
Skip cache for all subsequent build instructions from this point.
Call this before any instruction to force it and all following layers
to be rebuilt, ignoring any cached layers.
**Returns**:
`TemplateBuilder` class
Example
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
template.skip_cache().run_cmd('apt-get update')
```
### set\_start\_cmd
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def set_start_cmd(start_cmd: str,
ready_cmd: Union[str, ReadyCmd]) -> "TemplateFinal"
```
Set the command to start when the sandbox launches and the ready check command.
**Arguments**:
* `start_cmd`: Command to run when the sandbox starts
* `ready_cmd`: Command or ReadyCmd to check if the sandbox is ready
**Returns**:
`TemplateFinal` class
Example
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
template.set_start_cmd(
'python app.py',
'curl http://localhost:8000/health'
)
from e2b import wait_for_port, wait_for_url
template.set_start_cmd(
'python -m http.server 8000',
wait_for_port(8000)
)
template.set_start_cmd(
'npm start',
wait_for_url('http://localhost:3000/health', 200)
)
```
### set\_ready\_cmd
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def set_ready_cmd(ready_cmd: Union[str, ReadyCmd]) -> "TemplateFinal"
```
Set the command to check if the sandbox is ready.
**Arguments**:
* `ready_cmd`: Command or ReadyCmd to check if the sandbox is ready
**Returns**:
`TemplateFinal` class
Example
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
template.set_ready_cmd('curl http://localhost:8000/health')
from e2b import wait_for_port, wait_for_file, wait_for_process
template.set_ready_cmd(wait_for_port(3000))
template.set_ready_cmd(wait_for_file('/tmp/ready'))
template.set_ready_cmd(wait_for_process('nginx'))
```
## TemplateFinal
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
class TemplateFinal()
```
Final template state after start/ready commands are set.
## TemplateBase
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
class TemplateBase(ClientFactory)
```
Base class for building E2B sandbox templates.
### \_\_init\_\_
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def __init__(file_context_path: Optional[Union[str, Path]] = None,
file_ignore_patterns: Optional[List[str]] = None)
```
Create a new template builder instance.
**Arguments**:
* `file_context_path`: Base path for resolving relative file paths in copy operations
* `file_ignore_patterns`: Patterns in `.dockerignore` syntax for files to exclude when copying. They are applied after the `.dockerignore` file in the context directory, so they take precedence over it. An invalid pattern (such as an unterminated `[`) raises a :class:`TemplateException`. Brace expansion (`{a,b}`) is not supported, as in Docker
### skip\_cache
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def skip_cache() -> "TemplateBase"
```
Skip cache for all subsequent build instructions from this point.
**Returns**:
`TemplateBase` class
Example
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
template.skip_cache().from_python_image('3.11')
```
### from\_debian\_image
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def from_debian_image(variant: str = "stable") -> TemplateBuilder
```
Start template from a Debian base image.
**Arguments**:
* `variant`: Debian image variant
**Returns**:
`TemplateBuilder` class
Example
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
Template().from_debian_image('bookworm')
```
### from\_ubuntu\_image
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def from_ubuntu_image(variant: str = "latest") -> TemplateBuilder
```
Start template from an Ubuntu base image.
**Arguments**:
* `variant`: Ubuntu image variant (default: 'latest')
**Returns**:
`TemplateBuilder` class
Example
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
Template().from_ubuntu_image('24.04')
```
### from\_fedora\_image
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def from_fedora_image(variant: str = "44") -> TemplateBuilder
```
Start template from a Fedora base image.
**Arguments**:
* `variant`: Fedora image variant (default: '44')
**Returns**:
`TemplateBuilder` class
Example
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
Template().from_fedora_image('44')
```
### from\_alpine\_image
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def from_alpine_image(variant: str = "3.24") -> TemplateBuilder
```
Start template from an Alpine base image.
**Arguments**:
* `variant`: Alpine image variant (default: '3.24')
**Returns**:
`TemplateBuilder` class
Example
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
Template().from_alpine_image('3.24')
```
### from\_arch\_image
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def from_arch_image(variant: str = "latest") -> TemplateBuilder
```
Start template from an Arch Linux base image.
Defaults to `latest`: Arch is a rolling release and template
provisioning runs `pacman -Syu`, so pinning a tag would not change
the built result.
**Arguments**:
* `variant`: Arch Linux image variant (default: 'latest')
**Returns**:
`TemplateBuilder` class
Example
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
Template().from_arch_image('base-devel')
```
### from\_python\_image
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def from_python_image(version: str = "3") -> TemplateBuilder
```
Start template from a Python base image.
**Arguments**:
* `version`: Python version (default: '3')
**Returns**:
`TemplateBuilder` class
Example
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
Template().from_python_image('3')
```
### from\_node\_image
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def from_node_image(variant: str = "lts") -> TemplateBuilder
```
Start template from a Node.js base image.
**Arguments**:
* `variant`: Node.js image variant (default: 'lts')
**Returns**:
`TemplateBuilder` class
Example
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
Template().from_node_image('24')
```
### from\_bun\_image
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def from_bun_image(variant: str = "latest") -> TemplateBuilder
```
Start template from a Bun base image.
**Arguments**:
* `variant`: Bun image variant (default: 'latest')
**Returns**:
`TemplateBuilder` class
### from\_base\_image
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def from_base_image() -> TemplateBuilder
```
Start template from the E2B base image (e2bdev/base:latest).
**Returns**:
`TemplateBuilder` class
Example
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
Template().from_base_image()
```
### from\_image
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def from_image(image: str,
username: Optional[str] = None,
password: Optional[str] = None) -> TemplateBuilder
```
Start template from a Docker image.
**Arguments**:
* `image`: Docker image name (e.g., 'ubuntu:24.04')
* `username`: Username for private registry authentication
* `password`: Password for private registry authentication
**Returns**:
`TemplateBuilder` class
Example
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
Template().from_image('python:3')
Template().from_image('myregistry.com/myimage:latest', username='user', password='pass')
```
### from\_template
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def from_template(template: str) -> TemplateBuilder
```
Start template from an existing E2B template.
**Arguments**:
* `template`: E2B template ID or alias
**Returns**:
`TemplateBuilder` class
Example
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
Template().from_template('my-base-template')
```
### from\_dockerfile
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def from_dockerfile(dockerfile_content_or_path: str) -> TemplateBuilder
```
Parse a Dockerfile and convert it to Template SDK format.
**Arguments**:
* `dockerfile_content_or_path`: Either the Dockerfile content as a string, or a path to a Dockerfile file
**Returns**:
`TemplateBuilder` class
Example
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
Template().from_dockerfile('Dockerfile')
Template().from_dockerfile('FROM python:3\nRUN pip install numpy')
```
### from\_aws\_registry
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def from_aws_registry(image: str, access_key_id: str, secret_access_key: str,
region: str) -> TemplateBuilder
```
Start template from an AWS ECR registry image.
**Arguments**:
* `image`: Docker image name from AWS ECR
* `access_key_id`: AWS access key ID
* `secret_access_key`: AWS secret access key
* `region`: AWS region
**Returns**:
`TemplateBuilder` class
Example
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
Template().from_aws_registry(
'123456789.dkr.ecr.us-west-2.amazonaws.com/myimage:latest',
access_key_id='AKIA...',
secret_access_key='...',
region='us-west-2'
)
```
### from\_gcp\_registry
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def from_gcp_registry(
image: str, service_account_json: Union[str, dict]) -> TemplateBuilder
```
Start template from a GCP Artifact Registry or Container Registry image.
**Arguments**:
* `image`: Docker image name from GCP registry
* `service_account_json`: Service account JSON string, dict, or path to JSON file
**Returns**:
`TemplateBuilder` class
Example
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
Template().from_gcp_registry(
'gcr.io/myproject/myimage:latest',
service_account_json='path/to/service-account.json'
)
```
### to\_json
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@staticmethod
def to_json(template: "TemplateClass") -> str
```
Convert a template to JSON representation.
**Arguments**:
* `template`: The template to convert (TemplateBuilder or TemplateFinal instance)
**Returns**:
JSON string representation of the template
Example
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
template = Template().from_python_image('3').copy('app.py', '/app/')
json_str = TemplateBase.to_json(template)
```
### to\_dockerfile
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@staticmethod
def to_dockerfile(template: "TemplateClass") -> str
```
Convert a template to Dockerfile format.
Note: Templates based on other E2B templates cannot be converted to Dockerfile.
**Arguments**:
* `template`: The template to convert (TemplateBuilder or TemplateFinal instance)
**Raises**:
* `ValueError`: If the template is based on another E2B template or has no base image
Example
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
template = Template().from_python_image('3').copy('app.py', '/app/')
dockerfile = TemplateBase.to_dockerfile(template)
```
**Returns**:
Dockerfile string representation
### make\_traceback
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def make_traceback(
caller_frame: Optional[FrameType]) -> Optional[TracebackType]
```
Create a TracebackType from a caller frame for error reporting.
**Arguments**:
* `caller_frame`: The caller's frame object, or None
**Returns**:
A TracebackType object for use with exception.with\_traceback(), or None
### validate\_relative\_path
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def validate_relative_path(src: str,
stack_trace: Optional[TracebackType]) -> None
```
Validate that a source path for copy operations is a relative path that stays
within the context directory. This prevents path traversal attacks and ensures
files are copied from within the expected directory.
**Arguments**:
* `src`: The source path to validate
* `stack_trace`: Optional stack trace for error reporting
**Raises**:
* `TemplateException`: If the path is absolute or escapes the context directory
Invalid paths:
* Absolute paths: /absolute/path, C:\Windows\path
* Parent directory escapes: ../foo, foo/../../bar, ./foo/../../../bar
Valid paths:
* Simple relative: foo, foo/bar
* Current directory prefix: ./foo, ./foo/bar
* Internal parent refs that don't escape: foo/../bar (stays within context)
### normalize\_build\_arguments
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def normalize_build_arguments(name: Optional[str] = None,
alias: Optional[str] = None) -> str
```
Normalize build arguments from different parameter signatures.
Handles string name or legacy alias parameter.
**Arguments**:
* `name`: Template name in 'name' or 'name:tag' format
* `alias`: (Deprecated) Alias name for the template. Use name instead.
**Raises**:
* `TemplateException`: If no template name is provided
**Returns**:
Normalized template name
### read\_dockerignore
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def read_dockerignore(context_path: str) -> List[str]
```
Read and parse a .dockerignore file.
**Arguments**:
* `context_path`: Directory path containing the .dockerignore file
**Returns**:
Array of ignore patterns (empty lines and comments are filtered out)
### normalize\_path
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def normalize_path(path: str) -> str
```
Normalize path separators to forward slashes for glob patterns (glob expects / even on Windows).
**Arguments**:
* `path`: The path to normalize
**Returns**:
The normalized path
### get\_all\_files\_in\_path
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def get_all_files_in_path(src: str,
context_path: str,
ignore_patterns: List[str],
include_directories: bool = True) -> List[str]
```
Get all files for a given path and ignore patterns.
Ignore patterns follow `.dockerignore` semantics: they are relative to the
context root, a pattern matching a directory excludes everything under it,
and `!` patterns re-include paths (the last matching pattern wins).
The context root is never excluded.
**Arguments**:
* `src`: Path to the source directory
* `context_path`: Base directory for resolving relative paths
* `ignore_patterns`: Ignore patterns in `.dockerignore` syntax
* `include_directories`: Whether to include directories
**Returns**:
Array of files
### calculate\_files\_hash
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def calculate_files_hash(src: str, dest: str, context_path: str,
ignore_patterns: List[str], resolve_symlinks: bool,
stack_trace: Optional[TracebackType]) -> str
```
Calculate a hash of files being copied to detect changes for cache invalidation.
The hash includes file content, metadata (mode, size), and relative paths.
Note: uid, gid, and mtime are excluded to ensure stable hashes across environments.
**Arguments**:
* `src`: Source path pattern for files to copy
* `dest`: Destination path where files will be copied
* `context_path`: Base directory for resolving relative paths
* `ignore_patterns`: Ignore patterns in `.dockerignore` syntax
* `resolve_symlinks`: Whether to resolve symbolic links when hashing
* `stack_trace`: Optional stack trace for error reporting
**Raises**:
* `ValueError`: If no files match the source pattern
**Returns**:
Hex string hash of all files
### tar\_file\_stream
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def tar_file_stream(file_name: str, file_context_path: str,
ignore_patterns: List[str], resolve_symlinks: bool,
gzip: bool) -> IO[bytes]
```
Create a tar archive of files matching a pattern in a temporary file.
The archive is spooled to disk so it can be uploaded as a stream instead
of being buffered in memory. The temporary file is deleted when closed.
**Arguments**:
* `file_name`: Glob pattern for files to include
* `file_context_path`: Base directory for resolving file paths
* `ignore_patterns`: Ignore patterns
* `resolve_symlinks`: Whether to resolve symbolic links
* `gzip`: Whether to gzip the archive
**Returns**:
Binary file object positioned at the start of the archive
### strip\_ansi\_escape\_codes
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def strip_ansi_escape_codes(text: str) -> str
```
Strip ANSI escape codes from a string.
Source: [https://github.com/chalk/ansi-regex/blob/main/index.js](https://github.com/chalk/ansi-regex/blob/main/index.js)
**Arguments**:
* `text`: String with ANSI escape codes
**Returns**:
String without ANSI escape codes
### get\_caller\_frame
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def get_caller_frame() -> Optional[FrameType]
```
Get the caller's stack frame in user code.
This is used to provide better error messages and debugging information
by tracking where template methods were called from in user code.
The caller is the first frame whose file lies outside the SDK package.
Selecting frames by boundary rather than by fixed depth keeps the result
stable no matter how many SDK-internal frames sit in between.
**Returns**:
The caller frame, or None if not available
### get\_caller\_directory
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def get_caller_directory() -> Optional[str]
```
Get the directory of the caller in user code.
This is used to determine the file\_context\_path when creating a template,
so file paths are resolved relative to the user's template file location.
**Returns**:
The caller's directory path, or None if not available
### pad\_octal
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def pad_octal(mode: int) -> str
```
Convert a numeric file mode to a zero-padded octal string.
**Arguments**:
* `mode`: File mode as a number (e.g., 493 for 0o755)
**Returns**:
Zero-padded 4-digit octal string (e.g., "0755")
Example
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
pad_octal(0o755) # Returns "0755"
pad_octal(0o644) # Returns "0644"
```
### get\_build\_step\_index
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def get_build_step_index(step: str, stack_traces_length: int) -> int
```
Get the array index for a build step based on its name.
Special steps:
* BASE\_STEP\_NAME: Returns 0 (first step)
* FINALIZE\_STEP\_NAME: Returns the last index
* Numeric strings: Converted to number
**Arguments**:
* `step`: Build step name or number as string
* `stack_traces_length`: Total number of stack traces (used for FINALIZE\_STEP\_NAME)
**Returns**:
Index for the build step
### read\_gcp\_service\_account\_json
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def read_gcp_service_account_json(context_path: str,
path_or_content: Union[str, dict]) -> str
```
Read GCP service account JSON from a file or object.
**Arguments**:
* `context_path`: Base directory for resolving relative file paths
* `path_or_content`: Either a path to a JSON file or a service account object
**Returns**:
Service account JSON as a string
## LogEntry
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@dataclass
class LogEntry()
```
Represents a single log entry from the template build process.
## LogEntryStart
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@dataclass
class LogEntryStart(LogEntry)
```
Special log entry indicating the start of a build process.
## LogEntryEnd
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@dataclass
class LogEntryEnd(LogEntry)
```
Special log entry indicating the end of a build process.
### TIMER\_UPDATE\_INTERVAL\_MS
Default minimum log level to display.
### DEFAULT\_LEVEL
Colored labels for each log level.
### levels
Numeric ordering of log levels for comparison (lower = less severe).
### set\_interval
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def set_interval(func, interval)
```
Returns a stop function that can be called to cancel the interval.
Similar to JavaScript's setInterval.
**Arguments**:
* `func`: Function to execute at each interval
* `interval`: Interval duration in **seconds**
**Returns**:
Stop function that can be called to cancel the interval
### default\_build\_logger
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def default_build_logger(
min_level: Optional[LogEntryLevel] = None
) -> Callable[[LogEntry], None]
```
Create a default build logger with animated timer display.
**Arguments**:
* `min_level`: Minimum log level to display (default: 'info')
**Returns**:
Logger function that accepts LogEntry instances
Example
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Template, default_build_logger
template = Template().from_python_image()
```
## DockerfFileFinalParserInterface
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
class DockerfFileFinalParserInterface(Protocol)
```
Protocol defining the final interface for Dockerfile parsing callbacks.
## DockerfileParserInterface
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
class DockerfileParserInterface(Protocol)
```
Protocol defining the interface for Dockerfile parsing callbacks.
### run\_cmd
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def run_cmd(command: Union[str, List[str]],
user: Optional[str] = None) -> "DockerfileParserInterface"
```
Handle RUN instruction.
### copy
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def copy(src: str,
dest: str,
force_upload: Optional[Literal[True]] = None,
user: Optional[str] = None,
mode: Optional[int] = None,
resolve_symlinks: Optional[bool] = None,
gzip: Optional[bool] = None) -> "DockerfileParserInterface"
```
Handle COPY instruction.
### set\_workdir
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def set_workdir(workdir: str) -> "DockerfileParserInterface"
```
Handle WORKDIR instruction.
### set\_user
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def set_user(user: str) -> "DockerfileParserInterface"
```
Handle USER instruction.
### set\_envs
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def set_envs(envs: Dict[str, str]) -> "DockerfileParserInterface"
```
Handle ENV instruction.
### set\_start\_cmd
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def set_start_cmd(start_cmd: str,
ready_cmd: str) -> "DockerfFileFinalParserInterface"
```
Handle CMD/ENTRYPOINT instruction.
### parse\_dockerfile
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def parse_dockerfile(dockerfile_content_or_path: str,
template_builder: DockerfileParserInterface) -> str
```
Parse a Dockerfile and convert it to Template SDK format.
**Arguments**:
* `dockerfile_content_or_path`: Either the Dockerfile content as a string, or a path to a Dockerfile file
* `template_builder`: Interface providing template builder methods
**Raises**:
* `ValueError`: If the Dockerfile is invalid or unsupported
**Returns**:
The base image from the Dockerfile
## TemplateBuildStatus
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
class TemplateBuildStatus(str, Enum)
```
Status of a template build.
## BuildStatusReason
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@dataclass
class BuildStatusReason()
```
Reason for the current build status (typically for errors).
### message
Message with the status reason.
### step
Step that failed.
### log\_entries
Log entries related to the status reason.
## TemplateBuildStatusResponse
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@dataclass
class TemplateBuildStatusResponse()
```
Response from getting build status.
### build\_id
Build identifier.
### template\_id
Template identifier.
### status
Current status of the build.
### log\_entries
Build log entries.
### logs
Build logs (raw strings). Always empty since the V1 build path was removed.
:deprecated: Use `log_entries` instead. Will be removed in the next major version.
### reason
Reason for the current status (typically for errors).
## TemplateTagInfo
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@dataclass
class TemplateTagInfo()
```
Information about assigned template tags.
### build\_id
Build identifier associated with this tag.
### tags
Assigned tags of the template.
## TemplateTag
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@dataclass
class TemplateTag()
```
Detailed information about a single template tag.
### tag
Name of the tag.
### build\_id
Build identifier associated with this tag.
### created\_at
When this tag was assigned.
## InstructionType
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
class InstructionType(str, Enum)
```
Types of instructions that can be used in a template.
## CopyItem
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
class CopyItem(TypedDict)
```
Configuration for a single file/directory copy operation.
## Instruction
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
class Instruction(TypedDict)
```
Represents a single instruction in the template build process.
## GenericDockerRegistry
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
class GenericDockerRegistry(TypedDict)
```
Configuration for a generic Docker registry with basic authentication.
## AWSRegistry
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
class AWSRegistry(TypedDict)
```
Configuration for AWS Elastic Container Registry (ECR).
## GCPRegistry
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
class GCPRegistry(TypedDict)
```
Configuration for Google Container Registry (GCR) or Artifact Registry.
## TemplateType
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
class TemplateType(TypedDict)
```
Internal representation of a template for the E2B build API.
## BuildInfo
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@dataclass
class BuildInfo()
```
Information about a built template.
Special step name for the finalization phase of template building.
This is the last step that runs after all user-defined instructions.
### FINALIZE\_STEP\_NAME
Special step name for the base image phase of template building.
This is the first step that sets up the base image.
### BASE\_STEP\_NAME
Default setting for whether to resolve symbolic links when copying files.
When False, symlinks are copied as symlinks rather than following them.
### RESOLVE\_SYMLINKS
Default setting for whether to gzip files when copying them into the
template. When True, the upload archive is gzipped before being uploaded.
### GZIP
Default timeout (in seconds) for uploading the build-context archive to the
S3 presigned URL. Uploads of large archives can take far longer than the 60s
general API request timeout, so the upload uses a 1-hour default unless the
caller passes an explicit `request_timeout`. This matches the JS SDK's
`FILE_UPLOAD_TIMEOUT_MS`.
## ReadyCmd
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
class ReadyCmd()
```
Wrapper class for ready check commands.
### wait\_for\_port
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def wait_for_port(port: int)
```
Wait for a port to be listening.
Uses `ss` command to check if a port is open and listening.
**Arguments**:
* `port`: Port number to wait for
**Returns**:
ReadyCmd that checks for the port
Example
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Template, wait_for_port
template = (
Template()
.from_python_image()
.set_start_cmd('python -m http.server 8000', wait_for_port(8000))
)
```
### wait\_for\_url
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def wait_for_url(url: str, status_code: int = 200)
```
Wait for a URL to return a specific HTTP status code.
Uses `curl` to make HTTP requests and check the response status.
**Arguments**:
* `url`: URL to check (e.g., '[http://localhost:3000/health](http://localhost:3000/health)')
* `status_code`: Expected HTTP status code (default: 200)
**Returns**:
ReadyCmd that checks the URL
Example
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Template, wait_for_url
template = (
Template()
.from_node_image()
.set_start_cmd('npm start', wait_for_url('http://localhost:3000/health'))
)
```
### wait\_for\_process
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def wait_for_process(process_name: str)
```
Wait for a process with a specific name to be running.
Uses `pgrep` to check if a process exists.
**Arguments**:
* `process_name`: Name of the process to wait for
**Returns**:
ReadyCmd that checks for the process
Example
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Template, wait_for_process
template = (
Template()
.from_base_image()
.set_start_cmd('./my-daemon', wait_for_process('my-daemon'))
)
```
### wait\_for\_file
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def wait_for_file(filename: str)
```
Wait for a file to exist.
Uses shell test command to check file existence.
**Arguments**:
* `filename`: Path to the file to wait for
**Returns**:
ReadyCmd that checks for the file
Example
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Template, wait_for_file
template = (
Template()
.from_base_image()
.set_start_cmd('./init.sh', wait_for_file('/tmp/ready'))
)
```
### wait\_for\_timeout
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def wait_for_timeout(timeout: int)
```
Wait for a specified timeout before considering the sandbox ready.
Uses `sleep` command to wait for a fixed duration.
**Arguments**:
* `timeout`: Time to wait in **milliseconds** (minimum: 1000ms / 1 second)
**Returns**:
ReadyCmd that waits for the specified duration
Example
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Template, wait_for_timeout
template = (
Template()
.from_node_image()
.set_start_cmd('npm start', wait_for_timeout(5000)) # Wait 5 seconds
)
```
# Template async
Source: https://docs.e2b.dev/sdk-reference/python-sdk/v2.56.0/template_async
## AsyncTemplate
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
class AsyncTemplate(TemplateBase)
```
Asynchronous template builder for E2B sandboxes.
### build
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@classmethod
async def build(cls,
template: TemplateClass,
name: Optional[str] = None,
*,
alias: Optional[str] = None,
tags: Optional[List[str]] = None,
cpu_count: Optional[int] = None,
memory_mb: Optional[int] = None,
min_free_disk_mb: Optional[int] = None,
skip_cache: bool = False,
on_build_logs: Optional[Callable[[LogEntry], None]] = None,
**opts: Unpack[ApiParams]) -> BuildInfo
```
Build and deploy a template to E2B infrastructure.
**Arguments**:
* `template`: The template to build
* `name`: Template name in 'name' or 'name:tag' format
* `alias`: (Deprecated) Alias name for the template. Use name instead.
* `tags`: Optional additional tags to assign to the template
* `cpu_count`: Number of CPUs allocated to the sandbox.
* `memory_mb`: Amount of memory in MB allocated to the sandbox.
* `min_free_disk_mb`: Requested minimum free space after the build steps, in MiB. Growth is best effort and the filesystem is never shrunk. Omit to use the team default or set to 0 to request no growth.
* `skip_cache`: If True, forces a complete rebuild ignoring cache
* `on_build_logs`: Callback function to receive build logs during the build process
Example
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import AsyncTemplate
template = (
AsyncTemplate()
.from_python_image('3')
.copy('requirements.txt', '/home/user/')
.run_cmd('pip install -r /home/user/requirements.txt')
)
await AsyncTemplate.build(template, 'my-python-env:v1.0')
await AsyncTemplate.build(template, 'my-python-env', tags=['v1.1.0', 'stable'])
```
### build\_in\_background
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@classmethod
async def build_in_background(cls,
template: TemplateClass,
name: Optional[str] = None,
*,
alias: Optional[str] = None,
tags: Optional[List[str]] = None,
cpu_count: Optional[int] = None,
memory_mb: Optional[int] = None,
min_free_disk_mb: Optional[int] = None,
skip_cache: bool = False,
on_build_logs: Optional[Callable[[LogEntry],
None]] = None,
**opts: Unpack[ApiParams]) -> BuildInfo
```
Build and deploy a template to E2B infrastructure without waiting for completion.
**Arguments**:
* `template`: The template to build
* `name`: Template name in 'name' or 'name:tag' format
* `alias`: (Deprecated) Alias name for the template. Use name instead.
* `tags`: Optional additional tags to assign to the template
* `cpu_count`: Number of CPUs allocated to the sandbox.
* `memory_mb`: Amount of memory in MB allocated to the sandbox.
* `min_free_disk_mb`: Requested minimum free space after the build steps, in MiB. Growth is best effort and the filesystem is never shrunk. Omit to use the team default or set to 0 to request no growth.
* `skip_cache`: If True, forces a complete rebuild ignoring cache
**Returns**:
BuildInfo containing the template ID and build ID
Example
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import AsyncTemplate
template = (
AsyncTemplate()
.from_python_image('3')
.run_cmd('echo "test"')
.set_start_cmd('echo "Hello"', 'sleep 1')
)
build_info = await AsyncTemplate.build_in_background(template, 'my-python-env:v1.0')
build_info = await AsyncTemplate.build_in_background(template, 'my-python-env', tags=['v1.1.0', 'stable'])
```
### get\_build\_status
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@classmethod
async def get_build_status(cls,
build_info: BuildInfo,
logs_offset: Optional[int] = None,
**opts: Unpack[ApiParams])
```
Get the status of a build.
**Arguments**:
* `build_info`: Build identifiers returned from build\_in\_background
* `logs_offset`: Offset for fetching logs; when omitted, the API default applies
**Returns**:
TemplateBuild containing the build status and logs
Example
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import AsyncTemplate
build_info = await AsyncTemplate.build_in_background(template, alias='my-template')
status = await AsyncTemplate.get_build_status(build_info)
```
### exists
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@classmethod
async def exists(cls, name: str, **opts: Unpack[ApiParams]) -> bool
```
Check if a template with the given name exists.
**Arguments**:
* `name`: Template name to check
**Returns**:
True if the name exists, False otherwise
Example
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import AsyncTemplate
exists = await AsyncTemplate.exists('my-python-env')
if exists:
print('Template exists!')
```
### alias\_exists
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@classmethod
async def alias_exists(cls, alias: str, **opts: Unpack[ApiParams]) -> bool
```
Check if a template with the given alias exists.
Deprecated Use `exists` instead.
**Arguments**:
* `alias`: Template alias to check
**Returns**:
True if the alias exists, False otherwise
Example
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import AsyncTemplate
exists = await AsyncTemplate.alias_exists('my-python-env')
if exists:
print('Template exists!')
```
### assign\_tags
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@classmethod
async def assign_tags(cls, target_name: str, tags: Union[str, List[str]],
**opts: Unpack[ApiParams]) -> TemplateTagInfo
```
Assign tag(s) to an existing template build.
**Arguments**:
* `target_name`: Template name in 'name:tag' format (the source build to tag from)
* `tags`: Tag or tags to assign
**Returns**:
TemplateTagInfo with build\_id and assigned tags
Example
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import AsyncTemplate
result = await AsyncTemplate.assign_tags('my-template:v1.0', 'production')
result = await AsyncTemplate.assign_tags('my-template:v1.0', ['production', 'stable'])
```
### remove\_tags
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@classmethod
async def remove_tags(cls, name: str, tags: Union[str, List[str]],
**opts: Unpack[ApiParams]) -> None
```
Remove tag(s) from a template.
**Arguments**:
* `name`: Template name
* `tags`: Tag or tags to remove
Example
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import AsyncTemplate
await AsyncTemplate.remove_tags('my-template', 'production')
await AsyncTemplate.remove_tags('my-template', ['production', 'stable'])
```
### get\_tags
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@classmethod
async def get_tags(cls, template_id: str,
**opts: Unpack[ApiParams]) -> List[TemplateTag]
```
Get all tags for a template.
**Arguments**:
* `template_id`: Template ID or name
**Returns**:
List of TemplateTag with tag name, build\_id, and created\_at
Example
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import AsyncTemplate
tags = await AsyncTemplate.get_tags('my-template')
for tag in tags:
print(f"Tag: {tag.tag}, Build: {tag.build_id}, Created: {tag.created_at}")
```
### check\_alias\_exists
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
async def check_alias_exists(client: AuthenticatedClient, alias: str) -> bool
```
Check if a template with the given alias exists.
**Arguments**:
* `client` - Authenticated API client
* `alias` - Template alias to check
**Returns**:
True if the alias exists, False otherwise
### assign\_tags
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
async def assign_tags(client: AuthenticatedClient, target_name: str,
tags: List[str]) -> TemplateTagInfo
```
Assign tag(s) to an existing template build.
**Arguments**:
* `client` - Authenticated API client
* `target_name` - Template name in 'name:tag' format (the source build to tag from)
* `tags` - Tags to assign
**Returns**:
TemplateTagInfo with build\_id and assigned tags
### remove\_tags
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
async def remove_tags(client: AuthenticatedClient, name: str,
tags: List[str]) -> None
```
Remove tag(s) from a template.
**Arguments**:
* `client` - Authenticated API client
* `name` - Template name
* `tags` - List of tags to remove
### get\_template\_tags
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
async def get_template_tags(client: AuthenticatedClient,
template_id_or_name: str) -> List[TemplateTag]
```
Get all tags for a template.
**Arguments**:
* `client` - Authenticated API client
* `template_id_or_name` - Template ID or name (a name may be namespaced)
# Template sync
Source: https://docs.e2b.dev/sdk-reference/python-sdk/v2.56.0/template_sync
## Template
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
class Template(TemplateBase)
```
Synchronous template builder for E2B sandboxes.
### build
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@classmethod
def build(cls,
template: TemplateClass,
name: Optional[str] = None,
*,
alias: Optional[str] = None,
tags: Optional[List[str]] = None,
cpu_count: Optional[int] = None,
memory_mb: Optional[int] = None,
min_free_disk_mb: Optional[int] = None,
skip_cache: bool = False,
on_build_logs: Optional[Callable[[LogEntry], None]] = None,
**opts: Unpack[ApiParams]) -> BuildInfo
```
Build and deploy a template to E2B infrastructure.
**Arguments**:
* `template`: The template to build
* `name`: Template name in 'name' or 'name:tag' format
* `alias`: (Deprecated) Alias name for the template. Use name instead.
* `tags`: Optional additional tags to assign to the template
* `cpu_count`: Number of CPUs allocated to the sandbox.
* `memory_mb`: Amount of memory in MB allocated to the sandbox.
* `min_free_disk_mb`: Requested minimum free space after the build steps, in MiB. Growth is best effort and the filesystem is never shrunk. Omit to use the team default or set to 0 to request no growth.
* `skip_cache`: If True, forces a complete rebuild ignoring cache
* `on_build_logs`: Callback function to receive build logs during the build process
Example
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Template
template = (
Template()
.from_python_image('3')
.copy('requirements.txt', '/home/user/')
.run_cmd('pip install -r /home/user/requirements.txt')
)
Template.build(template, 'my-python-env:v1.0')
Template.build(template, 'my-python-env', tags=['v1.1.0', 'stable'])
```
### build\_in\_background
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@classmethod
def build_in_background(cls,
template: TemplateClass,
name: Optional[str] = None,
*,
alias: Optional[str] = None,
tags: Optional[List[str]] = None,
cpu_count: Optional[int] = None,
memory_mb: Optional[int] = None,
min_free_disk_mb: Optional[int] = None,
skip_cache: bool = False,
on_build_logs: Optional[Callable[[LogEntry],
None]] = None,
**opts: Unpack[ApiParams]) -> BuildInfo
```
Build and deploy a template to E2B infrastructure without waiting for completion.
**Arguments**:
* `template`: The template to build
* `name`: Template name in 'name' or 'name:tag' format
* `alias`: (Deprecated) Alias name for the template. Use name instead.
* `tags`: Optional additional tags to assign to the template
* `cpu_count`: Number of CPUs allocated to the sandbox.
* `memory_mb`: Amount of memory in MB allocated to the sandbox.
* `min_free_disk_mb`: Requested minimum free space after the build steps, in MiB. Growth is best effort and the filesystem is never shrunk. Omit to use the team default or set to 0 to request no growth.
* `skip_cache`: If True, forces a complete rebuild ignoring cache
**Returns**:
BuildInfo containing the template ID and build ID
Example
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Template
template = (
Template()
.from_python_image('3')
.run_cmd('echo "test"')
.set_start_cmd('echo "Hello"', 'sleep 1')
)
build_info = Template.build_in_background(template, 'my-python-env:v1.0')
build_info = Template.build_in_background(template, 'my-python-env', tags=['v1.1.0', 'stable'])
```
### get\_build\_status
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@classmethod
def get_build_status(cls,
build_info: BuildInfo,
logs_offset: Optional[int] = None,
**opts: Unpack[ApiParams])
```
Get the status of a build.
**Arguments**:
* `build_info`: Build identifiers returned from build\_in\_background
* `logs_offset`: Offset for fetching logs; when omitted, the API default applies
**Returns**:
TemplateBuild containing the build status and logs
Example
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Template
build_info = Template.build_in_background(template, alias='my-template')
status = Template.get_build_status(build_info)
```
### exists
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@classmethod
def exists(cls, name: str, **opts: Unpack[ApiParams]) -> bool
```
Check if a template with the given name exists.
**Arguments**:
* `name`: Template name to check
**Returns**:
True if the name exists, False otherwise
Example
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Template
exists = Template.exists('my-python-env')
if exists:
print('Template exists!')
```
### alias\_exists
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@classmethod
def alias_exists(cls, alias: str, **opts: Unpack[ApiParams]) -> bool
```
Check if a template with the given alias exists.
Deprecated Use `exists` instead.
**Arguments**:
* `alias`: Template alias to check
**Returns**:
True if the alias exists, False otherwise
Example
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Template
exists = Template.alias_exists('my-python-env')
if exists:
print('Template exists!')
```
### assign\_tags
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@classmethod
def assign_tags(cls, target_name: str, tags: Union[str, List[str]],
**opts: Unpack[ApiParams]) -> TemplateTagInfo
```
Assign tag(s) to an existing template build.
**Arguments**:
* `target_name`: Template name in 'name:tag' format (the source build to tag from)
* `tags`: Tag or tags to assign
**Returns**:
TemplateTagInfo with build\_id and assigned tags
Example
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Template
result = Template.assign_tags('my-template:v1.0', 'production')
result = Template.assign_tags('my-template:v1.0', ['production', 'stable'])
```
### remove\_tags
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@classmethod
def remove_tags(cls, name: str, tags: Union[str, List[str]],
**opts: Unpack[ApiParams]) -> None
```
Remove tag(s) from a template.
**Arguments**:
* `name`: Template name
* `tags`: Tag or tags to remove
Example
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b import Template
Template.remove_tags('my-template', 'production')
Template.remove_tags('my-template', ['production', 'stable'])
```
### get\_tags
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@classmethod
def get_tags(cls, template_id: str,
**opts: Unpack[ApiParams]) -> List[TemplateTag]
```
Get all tags for a template.
**Arguments**:
* `template_id`: Template ID or name
**Returns**:
List of TemplateTag with tag name, build\_id, and created\_at
Example
```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}")
```
### check\_alias\_exists
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def check_alias_exists(client: AuthenticatedClient, alias: str) -> bool
```
Check if a template with the given alias exists.
**Arguments**:
* `client` - Authenticated API client
* `alias` - Template alias to check
**Returns**:
True if the alias exists, False otherwise
### assign\_tags
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def assign_tags(client: AuthenticatedClient, target_name: str,
tags: List[str]) -> TemplateTagInfo
```
Assign tag(s) to an existing template build.
**Arguments**:
* `client` - Authenticated API client
* `target_name` - Template name in 'name:tag' format (the source build to tag from)
* `tags` - Tags to assign
**Returns**:
TemplateTagInfo with build\_id and assigned tags
### remove\_tags
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def remove_tags(client: AuthenticatedClient, name: str,
tags: List[str]) -> None
```
Remove tag(s) from a template.
**Arguments**:
* `client` - Authenticated API client
* `name` - Template name
* `tags` - List of tags to remove
### get\_template\_tags
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def get_template_tags(client: AuthenticatedClient,
template_id_or_name: str) -> List[TemplateTag]
```
Get all tags for a template.
**Arguments**:
* `client` - Authenticated API client
* `template_id_or_name` - Template ID or name (a name may be namespaced)
# Volume async
Source: https://docs.e2b.dev/sdk-reference/python-sdk/v2.56.0/volume_async
## AsyncVolume
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
class AsyncVolume(ClientFactory)
```
E2B Volume for persistent storage that can be mounted to sandboxes (async).
### create
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@classmethod
async def create(cls, name: str, **opts: Unpack[ApiParams]) -> Self
```
Create a new volume.
**Arguments**:
* `name`: Name of the volume
**Returns**:
An AsyncVolume instance for the new volume
### connect
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@classmethod
async def connect(cls, volume_id: str, **opts: Unpack[ApiParams]) -> Self
```
Connect to an existing volume by ID.
**Arguments**:
* `volume_id`: Volume ID
**Returns**:
An AsyncVolume instance for the existing volume
### destroy
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@classmethod
async def destroy(cls, volume_id: str, **opts: Unpack[ApiParams]) -> bool
```
Destroy a volume.
**Arguments**:
* `volume_id`: Volume ID
### make\_dir
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
async def make_dir(path: str,
uid: Optional[int] = None,
gid: Optional[int] = None,
mode: Optional[int] = None,
force: Optional[bool] = None,
**opts: Unpack[VolumeApiParams]) -> VolumeEntryStat
```
Create a directory.
**Arguments**:
* `path`: Path to the directory to create
* `uid`: User ID of the created directory
* `gid`: Group ID of the created directory
* `mode`: Mode of the created directory
* `force`: Create parent directories if they don't exist
* `opts`: Connection options
**Returns**:
Information about the created directory
### exists
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
async def exists(path: str, **opts: Unpack[VolumeApiParams]) -> bool
```
Check whether a file or directory exists.
Uses get\_info under the hood. Returns True if the path exists,
False if it does not (404). Other errors are re-raised.
**Arguments**:
* `path`: Path to the file or directory
* `opts`: Connection options
**Returns**:
True if the path exists, False otherwise
### update\_metadata
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
async def update_metadata(path: str,
uid: Optional[int] = None,
gid: Optional[int] = None,
mode: Optional[int] = None,
**opts: Unpack[VolumeApiParams]) -> VolumeEntryStat
```
Update file or directory metadata.
**Arguments**:
* `path`: Path to the file or directory
* `uid`: User ID of the file or directory
* `gid`: Group ID of the file or directory
* `mode`: Mode of the file or directory
* `opts`: Connection options
**Returns**:
Updated entry information
### read\_file
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
async def read_file(
path: str,
format: Literal["text", "bytes", "stream"] = "text",
stream_idle_timeout: Optional[float] = None,
**opts: Unpack[VolumeApiParams]
) -> Union[str, bytes, AsyncIterator[bytes]]
```
Read file content.
You can pass `text`, `bytes`, or `stream` to `format` to change the return type.
**Arguments**:
* `path`: Path to the file
* `format`: Format of the file content—`text` by default
* `stream_idle_timeout`: Idle timeout in **seconds** for a streamed
read (`format="stream"`)—abort with `httpx.ReadTimeout` if the
response head or the next chunk doesn't arrive within this
window. Resets on every chunk, so it bounds a stalled stream
without limiting total transfer time. Defaults to the
transport-wide idle read timeout (60 seconds); pass `0` to
disable.
* `opts`: Connection options
**Returns**:
File content as string, bytes, or async iterator of bytes
### write\_file
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
async def write_file(path: str,
data: Union[str, bytes, IO],
uid: Optional[int] = None,
gid: Optional[int] = None,
mode: Optional[int] = None,
force: Optional[bool] = None,
**opts: Unpack[VolumeApiParams]) -> VolumeEntryStat
```
Write content to a file.
Writing to a file that doesn't exist creates the file.
Writing to a file that already exists overwrites the file.
**Arguments**:
* `path`: Path to the file
* `data`: Data to write to the file. Data can be a string, bytes, or IO. File-like objects are streamed in chunks instead of being buffered in memory.
* `uid`: User ID of the created file
* `gid`: Group ID of the created file
* `mode`: Mode of the created file
* `force`: Force overwrite of an existing file
* `opts`: Connection options
**Returns**:
Information about the written file
### remove
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
async def remove(path: str, **opts: Unpack[VolumeApiParams]) -> None
```
Remove a file or directory.
**Arguments**:
* `path`: Path to the file or directory to remove
* `opts`: Connection options
# Volume sync
Source: https://docs.e2b.dev/sdk-reference/python-sdk/v2.56.0/volume_sync
## Volume
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
class Volume(ClientFactory)
```
E2B Volume for persistent storage that can be mounted to sandboxes.
### create
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@classmethod
def create(cls, name: str, **opts: Unpack[ApiParams]) -> Self
```
Create a new volume.
**Arguments**:
* `name`: Name of the volume
**Returns**:
A Volume instance for the new volume
### connect
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@classmethod
def connect(cls, volume_id: str, **opts: Unpack[ApiParams]) -> Self
```
Connect to an existing volume by ID.
**Arguments**:
* `volume_id`: Volume ID
**Returns**:
A Volume instance for the existing volume
### destroy
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@classmethod
def destroy(cls, volume_id: str, **opts: Unpack[ApiParams]) -> bool
```
Destroy a volume.
**Arguments**:
* `volume_id`: Volume ID
### make\_dir
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def make_dir(path: str,
uid: Optional[int] = None,
gid: Optional[int] = None,
mode: Optional[int] = None,
force: Optional[bool] = None,
**opts: Unpack[VolumeApiParams]) -> VolumeEntryStat
```
Create a directory.
**Arguments**:
* `path`: Path to the directory to create
* `uid`: User ID of the created directory
* `gid`: Group ID of the created directory
* `mode`: Mode of the created directory
* `force`: Create parent directories if they don't exist
* `opts`: Connection options
**Returns**:
Information about the created directory
### exists
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def exists(path: str, **opts: Unpack[VolumeApiParams]) -> bool
```
Check whether a file or directory exists.
Uses get\_info under the hood. Returns True if the path exists,
False if it does not (404). Other errors are re-raised.
**Arguments**:
* `path`: Path to the file or directory
* `opts`: Connection options
**Returns**:
True if the path exists, False otherwise
### update\_metadata
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def update_metadata(path: str,
uid: Optional[int] = None,
gid: Optional[int] = None,
mode: Optional[int] = None,
**opts: Unpack[VolumeApiParams]) -> VolumeEntryStat
```
Update file or directory metadata.
**Arguments**:
* `path`: Path to the file or directory
* `uid`: User ID of the file or directory
* `gid`: Group ID of the file or directory
* `mode`: Mode of the file or directory
* `opts`: Connection options
**Returns**:
Updated entry information
### read\_file
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def read_file(
path: str,
format: Literal["text", "bytes", "stream"] = "text",
stream_idle_timeout: Optional[float] = None,
**opts: Unpack[VolumeApiParams]) -> Union[str, bytes, Iterator[bytes]]
```
Read file content.
You can pass `text`, `bytes`, or `stream` to `format` to change the return type.
**Arguments**:
* `path`: Path to the file
* `format`: Format of the file content—`text` by default
* `stream_idle_timeout`: Ignored — the sync client cannot
interrupt a blocking read. A stalled streamed read is bounded by
a transport-wide idle read timeout instead (60 seconds), which
resets on every chunk. (`AsyncVolume.read_file` honors this
parameter.)
* `opts`: Connection options
**Returns**:
File content as string, bytes, or iterator of bytes
### write\_file
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def write_file(path: str,
data: Union[str, bytes, IO],
uid: Optional[int] = None,
gid: Optional[int] = None,
mode: Optional[int] = None,
force: Optional[bool] = None,
**opts: Unpack[VolumeApiParams]) -> VolumeEntryStat
```
Write content to a file.
Writing to a file that doesn't exist creates the file.
Writing to a file that already exists overwrites the file.
**Arguments**:
* `path`: Path to the file
* `data`: Data to write to the file. Data can be a string, bytes, or IO. File-like objects are streamed in chunks instead of being buffered in memory.
* `uid`: User ID of the created file
* `gid`: Group ID of the created file
* `mode`: Mode of the created file
* `force`: Force overwrite of an existing file
* `opts`: Connection options
**Returns**:
Information about the written file
### remove
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def remove(path: str, **opts: Unpack[VolumeApiParams]) -> None
```
Remove a file or directory.
**Arguments**:
* `path`: Path to the file or directory to remove
* `opts`: Connection options
# Charts
Source: https://docs.e2b.dev/sdk-reference/code-interpreter-js-sdk/v2.7.1/charts
### ChartType
Chart types
#### Enumeration Members
| Enumeration Member | Value |
| - | - |
| `BAR` | `"bar"` |
| `BOX_AND_WHISKER` | `"box_and_whisker"` |
| `LINE` | `"line"` |
| `PIE` | `"pie"` |
| `SCATTER` | `"scatter"` |
| `SUPERCHART` | `"superchart"` |
| `UNKNOWN` | `"unknown"` |
***
### ScaleType
Ax scale types
#### Enumeration Members
| Enumeration Member | Value |
| - | - |
| `ASINH` | `"asinh"` |
| `CATEGORICAL` | `"categorical"` |
| `DATETIME` | `"datetime"` |
| `FUNCTION` | `"function"` |
| `FUNCTIONLOG` | `"functionlog"` |
| `LINEAR` | `"linear"` |
| `LOG` | `"log"` |
| `LOGIT` | `"logit"` |
| `SYMLOG` | `"symlog"` |
## Type Aliases
### BarChart
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
type BarChart = Chart2D & object;
```
#### Type declaration
| Name | Type |
| - | - |
| `elements` | `BarData`\[] |
| `type` | `BAR` |
***
### BarData
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
type BarData = object;
```
#### Type declaration
| Name | Type |
| - | - |
| `group` | `string` |
| `label` | `string` |
| `value` | `string` |
***
### BoxAndWhiskerChart
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
type BoxAndWhiskerChart = Chart2D & object;
```
#### Type declaration
| Name | Type |
| - | - |
| `elements` | `BoxAndWhiskerData`\[] |
| `type` | `BOX_AND_WHISKER` |
***
### BoxAndWhiskerData
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
type BoxAndWhiskerData = object;
```
#### Type declaration
| Name | Type |
| - | - |
| `first_quartile` | `number` |
| `label` | `string` |
| `max` | `number` |
| `median` | `number` |
| `min` | `number` |
| `outliers` | `number`\[] |
| `third_quartile` | `number` |
***
### Chart
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
type Chart = object;
```
Represents a chart.
#### Type declaration
| Name | Type |
| - | - |
| `elements` | `any`\[] |
| `title` | `string` |
| `type` | `ChartType` |
***
### ChartTypes
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
type ChartTypes =
| LineChart
| ScatterChart
| BarChart
| PieChart
| BoxAndWhiskerChart
| SuperChart;
```
***
### LineChart
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
type LineChart = PointChart & object;
```
#### Type declaration
| Name | Type |
| - | - |
| `type` | `LINE` |
***
### PieChart
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
type PieChart = Chart & object;
```
#### Type declaration
| Name | Type |
| - | - |
| `elements` | `PieData`\[] |
| `type` | `PIE` |
***
### PieData
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
type PieData = object;
```
#### Type declaration
| Name | Type |
| - | - |
| `angle` | `number` |
| `label` | `string` |
| `radius` | `number` |
***
### PointData
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
type PointData = object;
```
#### Type declaration
| Name | Type |
| - | - |
| `label` | `string` |
| `points` | \[`number` \| `string`, `number` \| `string`]\[] |
***
### ScatterChart
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
type ScatterChart = PointChart & object;
```
#### Type declaration
| Name | Type |
| - | - |
| `type` | `SCATTER` |
***
### SuperChart
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
type SuperChart = Chart & object;
```
#### Type declaration
| Name | Type |
| - | - |
| `elements` | `Chart`\[] |
| `type` | `SUPERCHART` |
## Functions
### deserializeChart()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
function deserializeChart(data: any): Chart
```
#### Parameters
| Parameter | Type |
| - | - |
| `data` | `any` |
#### Returns
`Chart`
# Consts
Source: https://docs.e2b.dev/sdk-reference/code-interpreter-js-sdk/v2.7.1/consts
### DEFAULT\_TIMEOUT\_MS
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
const DEFAULT_TIMEOUT_MS: 60000 = 60_000;
```
***
### JUPYTER\_PORT
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
const JUPYTER_PORT: 49999 = 49999;
```
# Messaging
Source: https://docs.e2b.dev/sdk-reference/code-interpreter-js-sdk/v2.7.1/messaging
### Execution
Represents the result of a cell execution.
#### Constructors
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
new Execution(
results: Result[],
logs: Logs,
error?: ExecutionError,
executionCount?: number): Execution
```
###### Parameters
| Parameter | Type | Default value | Description |
| - | - | - | - |
| `results` | `Result`\[] | `[]` | List of result of the cell (interactively interpreted last line), display calls (e.g. matplotlib plots). |
| `logs` | `Logs` | `...` | Logs printed to stdout and stderr during execution. |
| `error`? | `ExecutionError` | `undefined` | An Error object if an error occurred, null otherwise. |
| `executionCount`? | `number` | `undefined` | Execution count of the cell. |
###### Returns
`Execution`
#### Properties
| Property | Modifier | Type | Default value | Description |
| - | - | - | - | - |
| `error?` | `public` | `ExecutionError` | `undefined` | An Error object if an error occurred, null otherwise. |
| `executionCount?` | `public` | `number` | `undefined` | Execution count of the cell. |
| `logs` | `public` | `Logs` | `undefined` | Logs printed to stdout and stderr during execution. |
| `results` | `public` | `Result`\[] | `[]` | List of result of the cell (interactively interpreted last line), display calls (e.g. matplotlib plots). |
#### Accessors
### text
###### Get Signature
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
get text(): undefined | string
```
Returns the text representation of the main result of the cell.
###### Returns
`undefined` | `string`
#### Methods
### toJSON()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
toJSON(): object
```
Returns the serializable representation of the execution result.
###### Returns
`object`
| Name | Type |
| - | - |
| `error` | `undefined` \| `ExecutionError` |
| `logs` | `Logs` |
| `results` | `Result`\[] |
***
### ExecutionError
Represents an error that occurred during the execution of a cell.
The error contains the name of the error, the value of the error, and the traceback.
#### Constructors
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
new ExecutionError(
name: string,
value: string,
traceback: string): ExecutionError
```
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `name` | `string` | Name of the error. |
| `value` | `string` | Value of the error. |
| `traceback` | `string` | The raw traceback of the error. |
###### Returns
`ExecutionError`
#### Properties
| Property | Modifier | Type | Description |
| - | - | - | - |
| `name` | `public` | `string` | Name of the error. |
| `traceback` | `public` | `string` | The raw traceback of the error. |
| `value` | `public` | `string` | Value of the error. |
***
### OutputMessage
Represents an output message from the sandbox code execution.
#### Constructors
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
new OutputMessage(
line: string,
timestamp: number,
error: boolean): OutputMessage
```
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `line` | `string` | The output line. |
| `timestamp` | `number` | Unix epoch in nanoseconds. |
| `error` | `boolean` | Whether the output is an error. |
###### Returns
`OutputMessage`
#### Properties
| Property | Modifier | Type | Description |
| - | - | - | - |
| `error` | `readonly` | `boolean` | Whether the output is an error. |
| `line` | `readonly` | `string` | The output line. |
| `timestamp` | `readonly` | `number` | Unix epoch in nanoseconds. |
#### Methods
### toString()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
toString(): string
```
###### Returns
`string`
***
### Result
Represents the data to be displayed as a result of executing a cell in a Jupyter notebook.
The result is similar to the structure returned by ipython kernel: [https://ipython.readthedocs.io/en/stable/development/execution.html#execution-semantics](https://ipython.readthedocs.io/en/stable/development/execution.html#execution-semantics)
The result can contain multiple types of data, such as text, images, plots, etc. Each type of data is represented
as a string, and the result can contain multiple types of data. The display calls don't have to have text representation,
for the actual result the representation is always present for the result, the other representations are always optional.
#### Constructors
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
new Result(rawData: RawData, isMainResult: boolean): Result
```
###### Parameters
| Parameter | Type |
| - | - |
| `rawData` | `RawData` |
| `isMainResult` | `boolean` |
###### Returns
`Result`
#### Properties
| Property | Modifier | Type | Description |
| - | - | - | - |
| `chart?` | `readonly` | `ChartTypes` | Contains the chart data. |
| `data?` | `readonly` | `Record`\<`string`, `unknown`> | Contains the data from DataFrame. |
| `extra?` | `readonly` | `any` | Extra data that can be included. Not part of the standard types. |
| `html?` | `readonly` | `string` | HTML representation of the data. |
| `isMainResult` | `readonly` | `boolean` | - |
| `javascript?` | `readonly` | `string` | JavaScript representation of the data. |
| `jpeg?` | `readonly` | `string` | JPEG representation of the data. |
| `json?` | `readonly` | `string` | JSON representation of the data. |
| `latex?` | `readonly` | `string` | LaTeX representation of the data. |
| `markdown?` | `readonly` | `string` | Markdown representation of the data. |
| `pdf?` | `readonly` | `string` | PDF representation of the data. |
| `png?` | `readonly` | `string` | PNG representation of the data. |
| `raw` | `readonly` | `RawData` | - |
| `svg?` | `readonly` | `string` | SVG representation of the data. |
| `text?` | `readonly` | `string` | Text representation of the result. |
#### Methods
### formats()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
formats(): string[]
```
Returns all the formats available for the result.
###### Returns
`string`\[]
Array of strings representing the formats available for the result.
### toJSON()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
toJSON(): object
```
Returns the serializable representation of the result.
###### Returns
`object`
| Name | Type |
| - | - |
| `extra`? | `any` |
| `html` | `undefined` \| `string` |
| `javascript` | `undefined` \| `string` |
| `jpeg` | `undefined` \| `string` |
| `json` | `undefined` \| `string` |
| `latex` | `undefined` \| `string` |
| `markdown` | `undefined` \| `string` |
| `pdf` | `undefined` \| `string` |
| `png` | `undefined` \| `string` |
| `svg` | `undefined` \| `string` |
| `text` | `undefined` \| `string` |
## Type Aliases
### Logs
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
type Logs = object;
```
Data printed to stdout and stderr during execution, usually by print statements, logs, warnings, subprocesses, etc.
#### Type declaration
| Name | Type | Description |
| - | - | - |
| `stderr` | `string`\[] | List of strings printed to stderr by prints, subprocesses, etc. |
| `stdout` | `string`\[] | List of strings printed to stdout by prints, subprocesses, etc. |
***
### MIMEType
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
type MIMEType = string;
```
Represents a MIME type.
***
### RawData
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
type RawData = object & E2BData;
```
Dictionary that maps MIME types to their corresponding representations of the data.
## Functions
### extractError()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
function extractError(res: Response): Promise
```
#### Parameters
| Parameter | Type |
| - | - |
| `res` | `Response` |
#### Returns
`Promise`\<`undefined` | `SandboxError`>
***
### parseOutput()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
function parseOutput(
execution: Execution,
line: string,
onStdout?: (output: OutputMessage) => any,
onStderr?: (output: OutputMessage) => any,
onResult?: (data: Result) => any,
onError?: (error: ExecutionError) => any): Promise
```
#### Parameters
| Parameter | Type |
| - | - |
| `execution` | `Execution` |
| `line` | `string` |
| `onStdout`? | (`output`: `OutputMessage`) => `any` |
| `onStderr`? | (`output`: `OutputMessage`) => `any` |
| `onResult`? | (`data`: `Result`) => `any` |
| `onError`? | (`error`: `ExecutionError`) => `any` |
#### Returns
`Promise`\<`void`>
# Sandbox
Source: https://docs.e2b.dev/sdk-reference/code-interpreter-js-sdk/v2.7.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 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/code-interpreter'
const sandbox = await Sandbox.create()
```
#### Methods
### createCodeContext()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
createCodeContext(opts?: CreateCodeContextOpts): Promise
```
Creates a new context to run code in.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `opts`? | `CreateCodeContextOpts` | options for creating the context. |
###### Returns
`Promise`\<`Context`>
context object.
### listCodeContexts()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
listCodeContexts(): Promise
```
List all contexts.
###### Returns
`Promise`\<`Context`\[]>
list of contexts.
### removeCodeContext()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
removeCodeContext(context: string | Context): Promise
```
Removes a context.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `context` | `string` \| `Context` | context to remove. |
###### Returns
`Promise`\<`void`>
void.
### restartCodeContext()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
restartCodeContext(context: string | Context): Promise
```
Restart a context.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `context` | `string` \| `Context` | context to restart. |
###### Returns
`Promise`\<`void`>
void.
### runCode()
###### Call Signature
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
runCode(code: string, opts?: RunCodeOpts & object): Promise
```
Run the code for the specified language.
Specify the `language` or `context` option to run the code as a different language or in a different `Context`.
If no language is specified, Python is used.
You can reference previously defined variables, imports, and functions in the code.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `code` | `string` | code to execute. |
| `opts`? | `RunCodeOpts` & `object` | options for executing the code. |
###### Returns
`Promise`\<`Execution`>
`Execution` result object.
###### Call Signature
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
runCode(code: string, opts?: RunCodeOpts & object): Promise
```
Runs the code in the specified context, if not specified, the default context is used.
Specify the `language` or `context` option to run the code as a different language or in a different `Context`.
You can reference previously defined variables, imports, and functions in the code.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `code` | `string` | code to execute. |
| `opts`? | `RunCodeOpts` & `object` | options for executing the code |
###### Returns
`Promise`\<`Execution`>
`Execution` result object
## Interfaces
### CreateCodeContextOpts
Options for creating a code context.
#### Properties
### cwd?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional cwd: string;
```
Working directory for the context.
###### Default
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
/home/user
```
### language?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional language: RunCodeLanguage;
```
Language for the context.
###### Default
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
python
```
### requestTimeoutMs?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional requestTimeoutMs: number;
```
Timeout for the request in **milliseconds**.
###### Default
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
30_000 // 30 seconds
```
***
### RunCodeOpts
Options for running code.
#### Properties
### envs?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional envs: Record;
```
Custom environment variables for code execution.
###### Default
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
{}
```
### onError()?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional onError: (error: ExecutionError) => any;
```
Callback for handling the `ExecutionError` object.
###### Parameters
| Parameter | Type |
| - | - |
| `error` | `ExecutionError` |
###### Returns
`any`
### onResult()?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional onResult: (data: Result) => any;
```
Callback for handling the final execution result.
###### Parameters
| Parameter | Type |
| - | - |
| `data` | `Result` |
###### Returns
`any`
### onStderr()?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional onStderr: (output: OutputMessage) => any;
```
Callback for handling stderr messages.
###### Parameters
| Parameter | Type |
| - | - |
| `output` | `OutputMessage` |
###### Returns
`any`
### onStdout()?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional onStdout: (output: OutputMessage) => any;
```
Callback for handling stdout messages.
###### Parameters
| Parameter | Type |
| - | - |
| `output` | `OutputMessage` |
###### Returns
`any`
### requestTimeoutMs?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional requestTimeoutMs: number;
```
Timeout for the request in **milliseconds**.
###### Default
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
30_000 // 30 seconds
```
### timeoutMs?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional timeoutMs: number;
```
Timeout for the code execution in **milliseconds**.
###### Default
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
60_000 // 60 seconds
```
## Type Aliases
### Context
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
type Context = object;
```
Represents a context for code execution.
#### Type declaration
| Name | Type | Description |
| - | - | - |
| `cwd` | `string` | The working directory of the context. |
| `id` | `string` | The ID of the context. |
| `language` | `string` | The language of the context. |
***
### RunCodeLanguage
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
type RunCodeLanguage =
| "python"
| "javascript"
| "typescript"
| "r"
| "java"
| "bash"
| string & object;
```
Supported language for code execution.
# Code interpreter
Source: https://docs.e2b.dev/sdk-reference/code-interpreter-python-sdk/v2.9.1/code_interpreter
## OutputMessage
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@dataclass
class OutputMessage()
```
Represents an output message from the sandbox code execution.
### line
The output line.
### timestamp
Unix epoch in nanoseconds
### error
Whether the output is an error.
## ExecutionError
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@dataclass
class ExecutionError()
```
Represents an error that occurred during the execution of a cell.
The error contains the name of the error, the value of the error, and the traceback.
### name
Name of the error.
### value
Value of the error.
### traceback
The raw traceback of the error.
### to\_json
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def to_json() -> str
```
Returns the JSON representation of the Error object.
## MIMEType
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
class MIMEType(str)
```
Represents a MIME type.
## Result
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@dataclass
class Result()
```
Represents the data to be displayed as a result of executing a cell in a Jupyter notebook.
The result is similar to the structure returned by ipython kernel: [https://ipython.readthedocs.io/en/stable/development/execution.html#execution-semantics](https://ipython.readthedocs.io/en/stable/development/execution.html#execution-semantics)
The result can contain multiple types of data, such as text, images, plots, etc. Each type of data is represented
as a string, and the result can contain multiple types of data. The display calls don't have to have text representation,
for the actual result the representation is always present for the result, the other representations are always optional.
### is\_main\_result
Whether this data is the result of the cell. Data can be produced by display calls of which can be multiple in a cell.
### extra
Extra data that can be included. Not part of the standard types.
### formats
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def formats() -> Iterable[str]
```
Returns all available formats of the result.
**Returns**:
All available formats of the result in MIME types.
### \_\_str\_\_
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def __str__() -> Optional[str]
```
Returns the text representation of the data.
**Returns**:
The text representation of the data.
### \_repr\_html\_
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def _repr_html_() -> Optional[str]
```
Returns the HTML representation of the data.
**Returns**:
The HTML representation of the data.
### \_repr\_markdown\_
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def _repr_markdown_() -> Optional[str]
```
Returns the Markdown representation of the data.
**Returns**:
The Markdown representation of the data.
### \_repr\_svg\_
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def _repr_svg_() -> Optional[str]
```
Returns the SVG representation of the data.
**Returns**:
The SVG representation of the data.
### \_repr\_png\_
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def _repr_png_() -> Optional[str]
```
Returns the base64 representation of the PNG data.
**Returns**:
The base64 representation of the PNG data.
### \_repr\_jpeg\_
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def _repr_jpeg_() -> Optional[str]
```
Returns the base64 representation of the JPEG data.
**Returns**:
The base64 representation of the JPEG data.
### \_repr\_pdf\_
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def _repr_pdf_() -> Optional[str]
```
Returns the PDF representation of the data.
**Returns**:
The PDF representation of the data.
### \_repr\_latex\_
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def _repr_latex_() -> Optional[str]
```
Returns the LaTeX representation of the data.
**Returns**:
The LaTeX representation of the data.
### \_repr\_json\_
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def _repr_json_() -> Optional[dict]
```
Returns the JSON representation of the data.
**Returns**:
The JSON representation of the data.
### \_repr\_javascript\_
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def _repr_javascript_() -> Optional[str]
```
Returns the JavaScript representation of the data.
**Returns**:
The JavaScript representation of the data.
## Logs
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@dataclass(repr=False)
class Logs()
```
Data printed to stdout and stderr during execution, usually by print statements, logs, warnings, subprocesses, etc.
### stdout
List of strings printed to stdout by prints, subprocesses, etc.
### stderr
List of strings printed to stderr by prints, subprocesses, etc.
### to\_json
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def to_json() -> str
```
Returns the JSON representation of the Logs object.
### serialize\_results
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def serialize_results(results: List[Result]) -> List[Dict[str, str]]
```
Serializes the results to JSON.
## Execution
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@dataclass(repr=False)
class Execution()
```
Represents the result of a cell execution.
### results
List of the result of the cell (interactively interpreted last line), display calls (e.g. matplotlib plots).
### logs
Logs printed to stdout and stderr during execution.
### error
Error object if an error occurred, None otherwise.
### execution\_count
Execution count of the cell.
### text
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@property
def text() -> Optional[str]
```
Returns the text representation of the result.
**Returns**:
The text representation of the result.
### to\_json
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def to_json() -> str
```
Returns the JSON representation of the Execution object.
## Context
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@dataclass
class Context()
```
Represents a context for code execution.
### id
The ID of the context.
### language
The language of the context.
### cwd
The working directory of the context.
## ChartType
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
class ChartType(str, enum.Enum)
```
Chart types
## ScaleType
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
class ScaleType(str, enum.Enum)
```
Ax scale types
## Chart
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
class Chart()
```
Extracted data from a chart. It's useful for building an interactive charts or custom visualizations.
## AsyncSandbox
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
class AsyncSandbox(BaseAsyncSandbox)
```
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 isolated code
* Access the internet
Check docs [here](https://e2b.dev/docs).
Use the `AsyncSandbox.create()` to create a new sandbox.
**Example**:
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b_code_interpreter import AsyncSandbox
sandbox = await AsyncSandbox.create()
```
### run\_code
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@overload
async def run_code(
code: str,
language: Optional[RunCodeLanguage] = None,
on_stdout: Optional[OutputHandlerWithAsync[OutputMessage]] = None,
on_stderr: Optional[OutputHandlerWithAsync[OutputMessage]] = None,
on_result: Optional[OutputHandlerWithAsync[Result]] = None,
on_error: Optional[OutputHandlerWithAsync[ExecutionError]] = None,
envs: Optional[Dict[str, str]] = None,
timeout: Optional[float] = None,
request_timeout: Optional[float] = None) -> Execution
```
Runs the code for the specified language.
Specify the `language` or `context` option to run the code as a different language or in a different `Context`.
If no language is specified, Python is used.
You can reference previously defined variables, imports, and functions in the code.
**Arguments**:
* `code`: Code to execute
* `language`: Language to use for code execution. If not defined, the default Python context is used.
* `on_stdout`: Callback for stdout messages
* `on_stderr`: Callback for stderr messages
* `on_result`: Callback for the `Result` object
* `on_error`: Callback for the `ExecutionError` object
* `envs`: Custom environment variables
* `timeout`: Timeout for the code execution in **seconds**
* `request_timeout`: Timeout for the request in **seconds**
**Returns**:
`Execution` result object
### run\_code
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@overload
async def run_code(
code: str,
context: Optional[Context] = None,
on_stdout: Optional[OutputHandlerWithAsync[OutputMessage]] = None,
on_stderr: Optional[OutputHandlerWithAsync[OutputMessage]] = None,
on_result: Optional[OutputHandlerWithAsync[Result]] = None,
on_error: Optional[OutputHandlerWithAsync[ExecutionError]] = None,
envs: Optional[Dict[str, str]] = None,
timeout: Optional[float] = None,
request_timeout: Optional[float] = None) -> Execution
```
Runs the code in the specified context, if not specified, the default context is used.
Specify the `language` or `context` option to run the code as a different language or in a different `Context`.
You can reference previously defined variables, imports, and functions in the code.
**Arguments**:
* `code`: Code to execute
* `context`: Concrete context to run the code in. If not specified, the default context for the language is used. It's mutually exclusive with the language.
* `on_stdout`: Callback for stdout messages
* `on_stderr`: Callback for stderr messages
* `on_result`: Callback for the `Result` object
* `on_error`: Callback for the `ExecutionError` object
* `envs`: Custom environment variables
* `timeout`: Timeout for the code execution in **seconds**
* `request_timeout`: Timeout for the request in **seconds**
**Returns**:
`Execution` result object
### create\_code\_context
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
async def create_code_context(
cwd: Optional[str] = None,
language: Optional[RunCodeLanguage] = None,
request_timeout: Optional[float] = None) -> Context
```
Creates a new context to run code in.
**Arguments**:
* `cwd`: Set the current working directory for the context, defaults to `/home/user`
* `language`: Language of the context. If not specified, defaults to Python
* `request_timeout`: Timeout for the request in **milliseconds**
**Returns**:
Context object
### remove\_code\_context
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
async def remove_code_context(context: Union[Context, str]) -> None
```
Removes a context.
**Arguments**:
* `context`: Context to remove. Can be a Context object or a context ID string.
**Returns**:
None
### list\_code\_contexts
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
async def list_code_contexts() -> List[Context]
```
List all contexts.
**Returns**:
List of contexts.
### restart\_code\_context
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
async def restart_code_context(context: Union[Context, str]) -> None
```
Restart a context.
**Arguments**:
* `context`: Context to restart. Can be a Context object or a context ID string.
**Returns**:
None
## Sandbox
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
class Sandbox(BaseSandbox)
```
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 isolated code
* Access the internet
Check docs [here](https://e2b.dev/docs).
Use the `Sandbox.create()` to create a new sandbox.
**Example**:
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
from e2b_code_interpreter import Sandbox
sandbox = Sandbox.create()
```
### run\_code
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@overload
def run_code(code: str,
language: Optional[RunCodeLanguage] = None,
on_stdout: Optional[OutputHandler[OutputMessage]] = None,
on_stderr: Optional[OutputHandler[OutputMessage]] = None,
on_result: Optional[OutputHandler[Result]] = None,
on_error: Optional[OutputHandler[ExecutionError]] = None,
envs: Optional[Dict[str, str]] = None,
timeout: Optional[float] = None,
request_timeout: Optional[float] = None) -> Execution
```
Runs the code for the specified language.
Specify the `language` or `context` option to run the code as a different language or in a different `Context`.
If no language is specified, Python is used.
You can reference previously defined variables, imports, and functions in the code.
**Arguments**:
* `code`: Code to execute
* `language`: Language to use for code execution. If not defined, the default Python context is used.
* `on_stdout`: Callback for stdout messages
* `on_stderr`: Callback for stderr messages
* `on_result`: Callback for the `Result` object
* `on_error`: Callback for the `ExecutionError` object
* `envs`: Custom environment variables
* `timeout`: Timeout for the code execution in **seconds**
* `request_timeout`: Timeout for the request in **seconds**
**Returns**:
`Execution` result object
### run\_code
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@overload
def run_code(code: str,
context: Optional[Context] = None,
on_stdout: Optional[OutputHandler[OutputMessage]] = None,
on_stderr: Optional[OutputHandler[OutputMessage]] = None,
on_result: Optional[OutputHandler[Result]] = None,
on_error: Optional[OutputHandler[ExecutionError]] = None,
envs: Optional[Dict[str, str]] = None,
timeout: Optional[float] = None,
request_timeout: Optional[float] = None) -> Execution
```
Runs the code in the specified context, if not specified, the default context is used.
Specify the `language` or `context` option to run the code as a different language or in a different `Context`.
You can reference previously defined variables, imports, and functions in the code.
**Arguments**:
* `code`: Code to execute
* `context`: Concrete context to run the code in. If not specified, the default context for the language is used. It's mutually exclusive with the language.
* `on_stdout`: Callback for stdout messages
* `on_stderr`: Callback for stderr messages
* `on_result`: Callback for the `Result` object
* `on_error`: Callback for the `ExecutionError` object
* `envs`: Custom environment variables
* `timeout`: Timeout for the code execution in **seconds**
* `request_timeout`: Timeout for the request in **seconds**
**Returns**:
`Execution` result object
### create\_code\_context
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def create_code_context(cwd: Optional[str] = None,
language: Optional[RunCodeLanguage] = None,
request_timeout: Optional[float] = None) -> Context
```
Creates a new context to run code in.
**Arguments**:
* `cwd`: Set the current working directory for the context, defaults to `/home/user`
* `language`: Language of the context. If not specified, defaults to Python
* `request_timeout`: Timeout for the request in **milliseconds**
**Returns**:
Context object
### remove\_code\_context
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def remove_code_context(context: Union[Context, str]) -> None
```
Removes a context.
**Arguments**:
* `context`: Context to remove. Can be a Context object or a context ID string.
**Returns**:
None
### list\_code\_contexts
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def list_code_contexts() -> List[Context]
```
List all contexts.
**Returns**:
List of contexts.
### restart\_code\_context
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def restart_code_context(context: Union[Context, str]) -> None
```
Restart a context.
**Arguments**:
* `context`: Context to restart. Can be a Context object or a context ID string.
**Returns**:
None
# Sandbox
Source: https://docs.e2b.dev/sdk-reference/desktop-js-sdk/v2.3.1/sandbox
### Sandbox
#### Properties
| Property | Modifier | Type | Default value |
| - | - | - | - |
| `display` | `public` | `string` | `':0'` |
| `stream` | `public` | `VNCServer` | `undefined` |
#### Methods
### doubleClick()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
doubleClick(x?: number, y?: number): Promise
```
Double left click on the mouse position.
###### Parameters
| Parameter | Type |
| - | - |
| `x`? | `number` |
| `y`? | `number` |
###### Returns
`Promise`\<`void`>
### drag()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
drag(from: [number, number], to: [number, number]): Promise
```
Drag the mouse from the given position to the given position.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `from` | \[`number`, `number`] | The starting position. |
| `to` | \[`number`, `number`] | The ending position. |
###### Returns
`Promise`\<`void`>
### getApplicationWindows()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
getApplicationWindows(application: string): Promise
```
Get the window ID of the window with the given title.
###### Parameters
| Parameter | Type |
| - | - |
| `application` | `string` |
###### Returns
`Promise`\<`string`\[]>
The ID of the window.
### getCurrentWindowId()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
getCurrentWindowId(): Promise
```
Get the current window ID.
###### Returns
`Promise`\<`string`>
The ID of the current window.
### getCursorPosition()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
getCursorPosition(): Promise
```
Get the current cursor position.
###### Returns
`Promise`\<`CursorPosition`>
A object with the x and y coordinates
###### Throws
Error if cursor position cannot be determined
### getScreenSize()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
getScreenSize(): Promise
```
Get the current screen size.
###### Returns
`Promise`\<`ScreenSize`>
An ScreenSize object
###### Throws
Error if screen size cannot be determined
### getWindowTitle()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
getWindowTitle(windowId: string): Promise
```
Get the title of the window with the given ID.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `windowId` | `string` | The ID of the window. |
###### Returns
`Promise`\<`string`>
The title of the window.
### launch()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
launch(application: string, uri?: string): Promise
```
Launch an application.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `application` | `string` | The application to launch. |
| `uri`? | `string` | The URI to open in the application. |
###### Returns
`Promise`\<`void`>
### leftClick()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
leftClick(x?: number, y?: number): Promise
```
Left click on the mouse position.
###### Parameters
| Parameter | Type |
| - | - |
| `x`? | `number` |
| `y`? | `number` |
###### Returns
`Promise`\<`void`>
### middleClick()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
middleClick(x?: number, y?: number): Promise
```
Middle click on the mouse position.
###### Parameters
| Parameter | Type |
| - | - |
| `x`? | `number` |
| `y`? | `number` |
###### Returns
`Promise`\<`void`>
### mousePress()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
mousePress(button: "left" | "right" | "middle"): Promise
```
Press the mouse button.
###### Parameters
| Parameter | Type | Default value |
| - | - | - |
| `button` | `"left"` \| `"right"` \| `"middle"` | `'left'` |
###### Returns
`Promise`\<`void`>
### mouseRelease()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
mouseRelease(button: "left" | "right" | "middle"): Promise
```
Release the mouse button.
###### Parameters
| Parameter | Type | Default value |
| - | - | - |
| `button` | `"left"` \| `"right"` \| `"middle"` | `'left'` |
###### Returns
`Promise`\<`void`>
### moveMouse()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
moveMouse(x: number, y: number): Promise
```
Move the mouse to the given coordinates.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `x` | `number` | The x coordinate. |
| `y` | `number` | The y coordinate. |
###### Returns
`Promise`\<`void`>
### open()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
open(fileOrUrl: string): Promise
```
Open a file or a URL in the default application.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `fileOrUrl` | `string` | The file or URL to open. |
###### Returns
`Promise`\<`void`>
### press()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
press(key: string | string[]): Promise
```
Press a key.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `key` | `string` \| `string`\[] | The key to press (e.g. "enter", "space", "backspace", etc.). Can be a single key or an array of keys. |
###### Returns
`Promise`\<`void`>
### rightClick()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
rightClick(x?: number, y?: number): Promise
```
Right click on the mouse position.
###### Parameters
| Parameter | Type |
| - | - |
| `x`? | `number` |
| `y`? | `number` |
###### Returns
`Promise`\<`void`>
### screenshot()
###### Call Signature
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
screenshot(): Promise>
```
Take a screenshot and save it to the given name.
###### Returns
`Promise`\<`Uint8Array`\<`ArrayBufferLike`>>
A Uint8Array bytes representation of the screenshot.
###### Call Signature
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
screenshot(format: "bytes"): Promise>
```
Take a screenshot and save it to the given name.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `format` | `"bytes"` | The format of the screenshot. |
###### Returns
`Promise`\<`Uint8Array`\<`ArrayBufferLike`>>
A Uint8Array bytes representation of the screenshot.
###### Call Signature
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
screenshot(format: "blob"): Promise
```
Take a screenshot and save it to the given name.
###### Parameters
| Parameter | Type |
| - | - |
| `format` | `"blob"` |
###### Returns
`Promise`\<`Blob`>
A Blob representation of the screenshot.
###### Call Signature
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
screenshot(format: "stream"): Promise>>
```
Take a screenshot and save it to the given name.
###### Parameters
| Parameter | Type |
| - | - |
| `format` | `"stream"` |
###### Returns
`Promise`\<`ReadableStream`\<`Uint8Array`\<`ArrayBufferLike`>>>
A ReadableStream of bytes representation of the screenshot.
### scroll()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
scroll(direction: "down" | "up", amount: number): Promise
```
Scroll the mouse wheel by the given amount.
###### Parameters
| Parameter | Type | Default value | Description |
| - | - | - | - |
| `direction` | `"down"` \| `"up"` | `'down'` | The direction to scroll. Can be "up" or "down". |
| `amount` | `number` | `1` | The amount to scroll. |
###### Returns
`Promise`\<`void`>
### wait()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
wait(ms: number): Promise
```
Wait for the given amount of time.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `ms` | `number` | The amount of time to wait in milliseconds. |
###### Returns
`Promise`\<`void`>
### waitAndVerify()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
waitAndVerify(
cmd: string,
onResult: (result: CommandResult) => boolean,
timeout: number,
interval: number): Promise
```
Wait for a command to return a specific result.
###### Parameters
| Parameter | Type | Default value | Description |
| - | - | - | - |
| `cmd` | `string` | `undefined` | The command to run. |
| `onResult` | (`result`: `CommandResult`) => `boolean` | `undefined` | The function to check the result of the command. |
| `timeout` | `number` | `10` | The maximum time to wait for the command to return the result. |
| `interval` | `number` | `0.5` | The interval to wait between checks. |
###### Returns
`Promise`\<`boolean`>
`true` if the command returned the result within the timeout, otherwise `false`.
### write()
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
write(text: string, options: object): Promise
```
Write the given text at the current cursor position.
###### Parameters
| Parameter | Type | Description |
| - | - | - |
| `text` | `string` | The text to write. |
| `options` | \{ `chunkSize`: `number`; `delayInMs`: `number`; } | An object containing the chunk size and delay between each chunk of text. |
| `options.chunkSize` | `number` | The size of each chunk of text to write. Default is 25 characters. |
| `options.delayInMs` | `number` | The delay between each chunk of text. Default is 75 ms. |
###### Returns
`Promise`\<`void`>
### create()
###### Call Signature
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
static create(this: S, opts?: SandboxOpts): Promise>
```
Create a new sandbox from the default `desktop` 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
````
###### Call Signature
```ts
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
````
## Interfaces
### SandboxOpts
Configuration options for the Sandbox environment.
SandboxOpts
#### Properties
### display?
```ts
optional display: string;
````
Display identifier.
### dpi?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional dpi: number;
```
Dots per inch (DPI) setting for the display.
### resolution?
```ts theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
optional resolution: [number, number];
```
The screen resolution in pixels, specified as \[width, height].
# Desktop
Source: https://docs.e2b.dev/sdk-reference/desktop-python-sdk/v2.4.2/desktop
## Sandbox
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
class Sandbox(SandboxBase)
```
### create
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@classmethod
def create(cls,
template: Optional[str] = None,
resolution: Optional[Tuple[int, int]] = None,
dpi: Optional[int] = None,
display: Optional[str] = None,
timeout: Optional[int] = None,
metadata: Optional[Dict[str, str]] = None,
envs: Optional[Dict[str, str]] = None,
secure: bool = True,
allow_internet_access: bool = True,
**opts: Unpack[ApiParams]) -> Self
```
Create a new sandbox.
By default, the sandbox is created from the default `desktop` sandbox template.
**Arguments**:
* `template`: Sandbox template name or ID
* `resolution`: Startup the desktop with custom screen resolution. Defaults to (1024, 768)
* `dpi`: Startup the desktop with custom DPI. Defaults to 96
* `display`: Startup the desktop with custom display. Defaults to ":0"
* `timeout`: Timeout for the sandbox in **seconds**, default to 300 seconds. The maximum time a sandbox can be kept alive is 24 hours (86\_400 seconds) for Pro users and 1 hour (3\_600 seconds) for Hobby users.
* `metadata`: Custom metadata for the sandbox
* `envs`: Custom environment variables for the sandbox
* `secure`: Envd is secured with access token and cannot be used without it
* `allow_internet_access`: Allow sandbox to access the internet, defaults to `True`.
**Returns**:
A Sandbox instance for the new sandbox
Use this method instead of using the constructor to create a new sandbox.
### screenshot
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@overload
def screenshot(format: Literal["stream"]) -> Iterator[bytes]
```
Take a screenshot and return it as a stream of bytes.
### screenshot
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
@overload
def screenshot(format: Literal["bytes"]) -> bytearray
```
Take a screenshot and return it as a bytearray.
### screenshot
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def screenshot(format: Literal["bytes", "stream"] = "bytes")
```
Take a screenshot and return it in the specified format.
**Arguments**:
* `format`: The format of the screenshot. Can be 'bytes', 'blob', or 'stream'.
**Returns**:
The screenshot in the specified format.
### left\_click
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def left_click(x: Optional[int] = None, y: Optional[int] = None)
```
Left click on the mouse position.
### double\_click
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def double_click(x: Optional[int] = None, y: Optional[int] = None)
```
Double left click on the mouse position.
### middle\_click
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def middle_click(x: Optional[int] = None, y: Optional[int] = None)
```
Middle click on the mouse position.
### scroll
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def scroll(direction: Literal["up", "down"] = "down", amount: int = 1)
```
Scroll the mouse wheel by the given amount.
**Arguments**:
* `direction`: The direction to scroll. Can be "up" or "down".
* `amount`: The amount to scroll.
### move\_mouse
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def move_mouse(x: int, y: int)
```
Move the mouse to the given coordinates.
**Arguments**:
* `x`: The x coordinate.
* `y`: The y coordinate.
### mouse\_press
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def mouse_press(button: Literal["left", "right", "middle"] = "left")
```
Press the mouse button.
### mouse\_release
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def mouse_release(button: Literal["left", "right", "middle"] = "left")
```
Release the mouse button.
### get\_cursor\_position
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def get_cursor_position() -> tuple[int, int]
```
Get the current cursor position.
**Raises**:
* `RuntimeError`: If the cursor position cannot be determined
**Returns**:
A tuple with the x and y coordinates
### get\_screen\_size
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def get_screen_size() -> tuple[int, int]
```
Get the current screen size.
**Raises**:
* `RuntimeError`: If the screen size cannot be determined
**Returns**:
A tuple with the width and height
### write
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def write(text: str, *, chunk_size: int = 25, delay_in_ms: int = 75) -> None
```
Write the given text at the current cursor position.
**Arguments**:
* `text`: The text to write.
* `chunk_size`: The size of each chunk of text to write.
* `delay_in_ms`: The delay between each chunk of text.
### press
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def press(key: Union[str, list[str]])
```
Press a key.
**Arguments**:
* `key`: The key to press (e.g. "enter", "space", "backspace", etc.).
### drag
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def drag(fr: tuple[int, int], to: tuple[int, int])
```
Drag the mouse from the given position to the given position.
**Arguments**:
* `from`: The starting position.
* `to`: The ending position.
### wait
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def wait(ms: int)
```
Wait for the given amount of time.
**Arguments**:
* `ms`: The amount of time to wait in milliseconds.
### open
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def open(file_or_url: str)
```
Open a file or a URL in the default application.
**Arguments**:
* `file_or_url`: The file or URL to open.
### get\_current\_window\_id
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def get_current_window_id() -> str
```
Get the current window ID.
### get\_application\_windows
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def get_application_windows(application: str) -> list[str]
```
Get the window IDs of all windows for the given application.
### get\_window\_title
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def get_window_title(window_id: str) -> str
```
Get the title of the window with the given ID.
### launch
```python theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
def launch(application: str, uri: Optional[str] = None)
```
Launch an application.
# Auth
Source: https://docs.e2b.dev/sdk-reference/cli/v2.22.0/auth
## e2b auth
authentication commands
### Usage
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
e2b auth [options] [command]
```
## e2b auth login
log in to CLI
### Usage
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
e2b auth login [options]
```
## e2b auth logout
log out of CLI
### Usage
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
e2b auth logout [options]
```
## e2b auth info
get information about the current user
### Usage
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
e2b auth info [options]
```
## e2b auth configure
configure user
### Usage
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
e2b auth configure [options]
```
# Sandbox
Source: https://docs.e2b.dev/sdk-reference/cli/v2.22.0/sandbox
## e2b sandbox
work with sandboxes
### Usage
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
e2b sandbox [options] [command]
```
## e2b sandbox connect
connect terminal to already running sandbox
### Usage
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
e2b sandbox connect [options]
```
### Options
* `-u, --user : user to start the terminal session as `
* `-c, --cwd : working directory for the terminal session `
* `-e, --env : set environment variable for the terminal session (repeatable) [default: [object Object]]`
## e2b sandbox info
show information for a sandbox
### Usage
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
e2b sandbox info [options]
```
### Options
* `-f, --format : output format, eg. json, pretty `
## e2b sandbox list
list all sandboxes, by default it list only running ones
### Usage
```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
e2b sandbox list [options]
```
### Options
* `-s, --state : filter by state, eg. running, paused. Defaults to running `
* `-m, --metadata