Outcompete docs
Outcompete runs each sandbox as a Firecracker microVM with its own kernel. You drive sandboxes from code with the open-source Python and TypeScript SDKs, and watch them from the console. Every example below runs against the Mumbai install.
Quickstart
Request access, sign in, and copy the API key from Settings. Then, from your project's directory, install the SDK:
curl -fsSL https://outcompete.neev.ai/install | sh
# or choose the language: ... | sh -s -- python (or node)It adds the SDK with your project's own package manager (for Python, into the active virtualenv) and prints the settings that point it at Outcompete. To install by hand, run pip install e2b or npm i e2b.
export E2B_API_URL=https://outcompete-api.neev.ai
export E2B_SANDBOX_URL=https://outcompete-sandbox.neev.ai
export E2B_DOMAIN=outcompete.neev.ai # port URLs and desktop streams
export E2B_API_KEY=e2b_... # Settings → SDK connectionfrom e2b import Sandbox
sbx = Sandbox.create("base", timeout=600) # seconds until it's killed
result = sbx.commands.run("python3 -V")
print(result.stdout)
sbx.kill()import { Sandbox } from "e2b"
const sbx = await Sandbox.create("base", { timeoutMs: 600_000 })
const result = await sbx.commands.run("node -v")
console.log(result.stdout)
await sbx.kill()Keep the API key on your backend. It controls every sandbox in the workspace.
Sandboxes
A sandbox starts from a template in about 165 ms and runs until its timeout, when it is killed (or paused, if you ask for that).
sbx = Sandbox.create(
"base",
timeout=3600, # up to 24 hours
metadata={"job": "build-42"}, # find it later with Sandbox.list()
envs={"API_TOKEN": "..."}, # environment for every command
allow_internet_access=True, # False: no outbound internet
)| Option | What it does |
|---|---|
timeout | Seconds until the sandbox is killed; up to 24 hours. Extend later with set_timeout. |
metadata | Your own labels, returned by Sandbox.list(). Use them to tag sandboxes with job or user IDs. |
envs | Environment variables for every command in the sandbox. |
allow_internet_access | False blocks outbound internet; the sandbox still runs what it has. |
network | allow_public_traffic and outbound rules; see Port URLs. |
Commands and files
Commands run as user in /home/user. A command waits for 60 seconds by default; raise timeout or run it in the background.
# Wait for a command; stream its output as it runs.
sbx.commands.run("pip install requests", timeout=600, on_stdout=print, on_stderr=print)
# Start a long-running process and keep going.
server = sbx.commands.run("python3 -m http.server 8000", background=True)
# Files.
sbx.files.write("/home/user/task.json", '{"job": 42}')
text = sbx.files.read("/home/user/task.json")
raw = sbx.files.read("/home/user/task.json", format="bytes")
entries = sbx.files.list("/home/user")Pause, resume, fork
Pausing keeps running processes, memory and files, and frees the CPU and RAM. Resuming restores it exactly where it stopped. Forking copies a running sandbox into a new one while the original keeps going.
sbx.pause() # memory and files kept
sbx = Sandbox.connect(sbx.sandbox_id) # resume, about 200 ms
[twin] = sbx.fork() # a new sandbox from the same state
sbx.set_timeout(3600) # extend: 1 hour from now
sbx.kill()To pause instead of kill when the timeout runs out:
sbx = Sandbox.create("base", timeout=900, lifecycle={"on_timeout": "pause"})Paused sandboxes are stored in Cloud Storage. After 30 days without use they move to archive storage, and they still resume.
Port URLs
Any port a sandbox listens on is reachable at https://<port>-<sandbox-id>.outcompete.neev.ai. Set E2B_DOMAIN so get_host returns it. URLs are open to anyone with the address by default; turn that off per sandbox and send the sandbox's token instead.
sbx.commands.run("python3 -m http.server 8000", background=True)
print("https://" + sbx.get_host(8000)) # https://8000-<id>.outcompete.neev.ai
# Require the sandbox's token for its URLs:
private = Sandbox.create("base", network={"allow_public_traffic": False})
# then send header e2b-traffic-access-token: private.traffic_access_tokenTemplates
| Template | Contents | Size |
|---|---|---|
base | Ubuntu with Python and common tools | 2 vCPU, 1 GB RAM, 7.8 GB free disk |
desktop | Xfce desktop, Firefox, Chrome, VS Code, VNC | 2 vCPU, 4 GB RAM |
Build your own from a Docker image with the Template SDK. Each template gets 8 GB of free disk on top of what it installs.
import { Template } from "e2b"
const template = Template()
.fromImage("ubuntu:24.04")
.setUser("root") // install steps run as root
.aptInstall(["nodejs", "npm", "git"])
.runCmd("npm install -g pnpm")
.setUser("user") // sandboxes run as user
await Template.build(template, "my-agent-box", { cpuCount: 2, memoryMB: 4096 })Steps that copy files from your machine into a template (copy) aren't supported yet; download files in a runCmd step instead.
Desktop and computer use
The desktop template gives an agent a screen, mouse and keyboard. Use the open-source desktop SDK, and pass allow_internet_access explicitly when creating it.
from e2b_desktop import Sandbox # pip install e2b-desktop
d = Sandbox.create(template="desktop", allow_internet_access=True)
png = d.screenshot()
d.launch("xfce4-terminal")
d.write("echo hello")
d.press("enter")
d.left_click(200, 200)
d.stream.start(require_auth=True)
print(d.stream.get_url(auth_key=d.stream.get_auth_key()))In the console, a desktop sandbox has a Desktop tab that shows the screen live and lets you take over.
Console
| Page | What you can do |
|---|---|
| Sandboxes | See every running and paused sandbox, create one, pause, resume or kill it. |
| A sandbox | A terminal; a file browser with upload, download and an editor; a live desktop for desktop sandboxes. |
| Templates | Templates and their build status. |
| Settings | SDK connection values and the API key; your password. |
Accounts are by invitation. All members share one workspace and its API key.
Security
Each sandbox is its own microVM, so agent code doesn't share a kernel with the host or other sandboxes. The host accepts no traffic from the internet: everything arrives over HTTPS through an outbound-only tunnel. The console never sends the API key to a browser; it hands out a token for one sandbox at a time, and sandboxes require that token for terminal and file access.
Limits
| Limit | Value |
|---|---|
| Session length | 24 hours (pause to keep it longer) |
| Free disk per sandbox | 8 GB on top of the template |
| Memory per sandbox | Set by the template, up to 8 GB |
| Concurrent sandboxes | 20, and about 20 GB of sandbox memory in total |
| Region | Mumbai (GCP asia-south1) |
Self-hosting
Terraform sets up the sandbox host on Google Cloud: a nested-virtualization VM, a persistent data disk with daily snapshots, a Cloud Storage bucket for templates and paused sandboxes, and HTTPS through Cloudflare Tunnel. The console runs on Cloudflare Workers.
cd deploy/gcp
terraform init && terraform apply # VM, data disk, bucket, tunnel, DNS
./apply-e2b-limits.sh # 8 GiB disk per sandbox, 24 h sessions
python ../../templates/desktop/build.py
cd ../../apps/console && bun run deployBefore stopping the stack by hand, pause the instance group's auto-healing, or it replaces the VM after about five minutes of failed health checks.
Troubleshooting
| Symptom | Fix |
|---|---|
| 401 from the API | Check E2B_API_KEY against Settings. |
| 403 on plain HTTP calls | Cloudflare blocks some generic clients; send a real User-Agent. |
| 403 on a port URL | The sandbox has allow_public_traffic off; send its traffic token. |
| Invalid sandbox port | Use the SDK, or send the E2b-Sandbox-Id and E2b-Sandbox-Port headers. |
| Port URL points at e2b.app | Set E2B_DOMAIN=outcompete.neev.ai. |
| Command times out after 60 s | Pass timeout=, or run it with background=True. |
| No space left on device | Rebuild the template: older builds had 512 MB free. |
| allow_internet_access is not nullable | Pass allow_internet_access=True to the desktop SDK. |
Something else? support@neev.ai. See what changed recently in the changelog.