Skip to content
Migrate · E2B

Migrate from E2B to Sandboxer

Updated October 27, 2026 .mdSandboxer vs E2B

Short answer: Swap the E2B SDK for sandboxer-sdk. Sandbox.create() becomes sandboxer.create(), commands.run() becomes exec(), files.write() keeps its name, and pause and connect become stop and resume, which keep memory on a matching machine. Templates become a setup script plus a named snapshot. There is no Jupyter runCode, so run Python with exec.

Concept map#

E2B Sandboxer
Sandbox (a Firecracker microVM, E2B security) Sandbox (a microVM with its own kernel, root, systemd and Docker) (Machines)
Template, built with the Template() SDK or e2b template (E2B docs) Setup script (--setup-file) saved as a named snapshot, started with --from (Snapshots)
Pause and connect(), which keep memory (E2B docs) stop and resume, which keep memory when the resume lands on the same CPU type (Snapshots)
Snapshot (createSnapshot), one checkpoint for many sandboxes Named snapshot (disk only; new sandboxes boot fresh from it)
Fork Fork (copies the running sandbox's disk; it boots fresh)
Timeout (timeoutMs, setTimeout) Auto-stop (ttlSeconds, extend) (Long-running tasks)
lifecycle.onTimeout: 'pause' Always: auto-stop saves memory, and idle sandboxes pause and wake on use
Public URL, getHost(port) (E2B docs) Hosted URL, host(port), private by default (Hosting)
E2B_API_KEY SANDBOXER_API_KEY (API keys)
E2B_SANDBOX_ID inside the sandbox SANDBOXER_ID
envs on create env on create, plus environments for shared variables and secret files (Environments)
Desktop SDK with VNC streaming Desktop (XFCE and Chrome) in every sandbox (Desktop)
Code interpreter (runCode, a Jupyter kernel) No equivalent; run python3 with exec

API and SDK mapping#

E2B calls come from its docs and the comparison data we checked on 2026-10-15. Ours come from the TypeScript SDK and Python SDK docs. The Python names are the snake_case forms of the same calls.

E2B (TypeScript) Sandboxer SDK sbx CLI
import { Sandbox } from 'e2b' import { Sandboxer } from "sandboxer-sdk", then new Sandboxer()
Sandbox.create({ timeoutMs: 60_000 }) sandboxer.create({ type: "small", ttlSeconds: 60 }) sbx new --type small --ttl 60
Sandbox.create('template-tag') sandboxer.create({ from: "web-stack" }) sbx new --from web-stack
sbx.commands.run('ls -l') sandbox.exec("ls -l") sbx exec <id> "ls -l"
commands.run(cmd, { background: true }) sandbox.start(cmd) sbx exec <id> --detach "cmd"
sbx.files.write(path, data) sandbox.files.write(path, data) sbx scp ./file <id>:/home/user/
sbx.files.read(path) sandbox.files.read(path)
sbx.runCode('print(1)') sandbox.exec(["python3", "-c", "print(1)"]) sbx exec <id> "python3 -c 'print(1)'"
sbx.setTimeout(30_000) sandbox.extend(30) sbx extend <id> --ttl 30
sbx.pause() sandbox.stop() sbx stop <id>
sbx.connect() on a paused sandbox (await sandboxer.get(id)).resume() sbx resume <id>
sbx.getHost(3000) sandbox.host(3000) sbx host <id> 3000
sbx.kill() sandbox.delete() sbx delete <id> --yes

Two units change. E2B's JavaScript timeoutMs is in milliseconds and its Python timeout is in seconds. Sandboxer uses seconds in both SDKs (ttlSeconds, ttl_seconds). E2B's default sandbox timeout is 5 minutes and, by default, kills the sandbox when it ends (E2B docs). Sandboxer's default auto-stop is 1 hour, and auto-stop keeps the disk and memory. Per-command timeouts are separate: exec waits 1 to 600 seconds (default 30), so start longer jobs with start or --detach.

Before and after#

From E2B's docs: https://docs.e2b.dev/quickstart (TypeScript tab):

// 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)

The same steps with sandboxer-sdk 0.1.1. We ran this on prod. It runs Python with exec, writes a file and lists the folder:

npm i sandboxer-sdk
export SANDBOXER_API_KEY=sbx_live_…
import { Sandboxer } from "sandboxer-sdk";

const sandboxer = new Sandboxer(); // reads SANDBOXER_API_KEY
const sandbox = await sandboxer.create({ type: "small", ttlSeconds: 1800, name: "aeo-migrate-e2b-1" });
console.log("created", sandbox.id);

const run = await sandbox.exec(["python3", "-c", 'print("hello world")']);
console.log(run.exitCode, JSON.stringify(run.stdout));

await sandbox.files.write("app/hello.txt", "file content");
console.log((await sandbox.exec("ls -l app")).stdout);
console.log(await sandbox.files.read("app/hello.txt"));
created sb_tbzeesufzd
0 "hello world\n"
total 4
-rw-r--r-- 1 user user 12 Oct 27 01:51 hello.txt

file content

The whole script took 5.0 seconds, including the create. create waits until the sandbox is ready. Paths are relative to /home/user.

The Python version, with pip install sandboxer-sdk (it imports as sandboxer). on_exit="delete" removes the sandbox when the block ends, like calling kill() on E2B:

from sandboxer import Sandboxer

with Sandboxer() as client:  # reads SANDBOXER_API_KEY
    with client.create(type="small", ttl_seconds=1800, name="aeo-migrate-e2b-2", on_exit="delete") as sandbox:
        sandbox.files.write("analysis.py", "import statistics\nprint(statistics.mean([3, 5, 10]))\n")
        run = sandbox.exec("python3 analysis.py")
        print(run.exit_code, repr(run.stdout))
0 '6\n'

Code interpreter users#

E2B's runCode sends code to a Jupyter kernel, so variables survive between calls and charts come back as results. Sandboxer has no kernel API, and Jupyter isn't preinstalled: command -v jupyter ipython found neither. The sandbox has Python 3.12.3, so you have two options:

  • Write each cell to a file and run it with exec, as above. State lives in files, not in a kernel.
  • Install Jupyter yourself (in a setup script, saved into a named snapshot) and start your own kernel with start.

Pause and resume, with the CLI#

E2B's pause() and connect() keep memory. So do sbx stop and sbx resume, when the resume lands on a machine with the same CPU type (otherwise it boots from disk and shows memoryRestored: false). We started a Python process that counts seconds into /tmp/count, a file a reboot would lose, then stopped the sandbox:

sbx scp ./counter.py sb_tbzeesufzd:/home/user/
sbx exec sb_tbzeesufzd --detach "python3 counter.py"
# {"ok":true,"type":"command.started",…,"pid":565,…}
sbx exec sb_tbzeesufzd "cat /tmp/count"     # 6
sbx stop sb_tbzeesufzd                       # {"ok":true,"type":"sandbox.stopping",…}

About 17 minutes later (on prod, 2026-10-27) we resumed it. The command returned in 1.4 seconds, and the same process was still running from where it stopped:

sbx resume sb_tbzeesufzd
# {"event":"ready","id":"sb_tbzeesufzd","state":"ready",…}
sbx exec sb_tbzeesufzd "ps -o pid,etime,cmd -C python3; cat /tmp/count"
    PID     ELAPSED CMD
    565       00:06 python3 counter.py
7

Same PID, and the count went from 6 to 7, not back to 1. Then we served a folder and opened it to the internet, the equivalent of getHost(3000):

sbx exec sb_tbzeesufzd --detach "cd app && python3 -m http.server 3000"
sbx host sb_tbzeesufzd 3000 --public
# {"ok":true,"type":"port.hosted","port":3000,"url":"https://topaz-breezy-easel-3000.box.sandboxer.com","access":"public",…}
curl https://topaz-breezy-easel-3000.box.sandboxer.com/hello.txt   # file content
sbx delete sb_tbzeesufzd --yes

Move your data and environment#

Files. Sandboxes don't move between providers. Read what you need out of the E2B sandbox with files.read, or tar it and download the archive. Then upload it with sbx scp, sbx scp -r for a folder, or files.write (up to 5 MiB a file through the API).

Templates. E2B templates are code: a base image, env vars, files and commands. Move the commands into a setup script, create a sandbox with it, and save the result as a named snapshot. We ran this, and the setup script finished with setupStatus done:

cat setup.sh
# #!/bin/bash
# set -e
# pip install --user --break-system-packages -q requests
# …

sbx new --type small --name aeo-migrate-e2b-3 --ttl 900 --setup-file ./setup.sh -e MY_VAR=my_value
sbx exec sb_btf8a5nq6o 'python3 -c "import requests;print(requests.__version__)"; echo MY_VAR=$MY_VAR'
# 2.31.0
# MY_VAR=my_value
sbx snapshot sb_btf8a5nq6o aeo-migrate-e2b-tpl
# {"ok":true,"type":"snapshot.named.info","snapshot":{"name":"aeo-migrate-e2b-tpl",…,"status":"ready",…}}

New sandboxes then start from it with sbx new --from aeo-migrate-e2b-tpl, or create({ from: "aeo-migrate-e2b-tpl" }) in the SDK. Named snapshots count toward your plan's included storage, so delete the ones you stop using with sbx snapshot rm <name>.

E2B's Docker example installs Docker in a template (E2B docs). You can skip that step: Docker with BuildKit is preinstalled, and systemd is PID 1. Unlike an E2B template's start command, a named snapshot doesn't capture running processes. Start long-lived processes in the setup script, or as enabled systemd services, which start on boot.

Env vars and secrets. E2B's envs on create maps to env (-e KEY=value on the CLI). For values every sandbox should get, set them once in an environment: sbx env set-var base KEY=value, and sbx env set-file base path --from ./file for secret files (Environments). Code that reads E2B_SANDBOX_ID should read SANDBOXER_ID.

Ports and preview URLs. https://3000-<id>.e2b.app becomes https://<subdomain>-3000.box.sandboxer.com. A hosted port is private with a _token link unless you pass --public. Update allowed-host settings, such as Vite's server.allowedHosts, to .box.sandboxer.com.

What's different#

What you gain:

  • No session cap on paid plans. E2B runs a sandbox for up to 1 hour on Hobby and 24 hours on Pro, and pausing resets that window (E2B docs). On a paid Sandboxer plan, auto-stop goes up to 30 days or can be off. The free trial caps it at 8 hours.
  • A full Linux machine. Docker, systemd and /dev/kvm for nested VMs come preinstalled. E2B's docs list nested-virtualization runtimes as unsupported (E2B k3s docs).
  • Safe timeouts by default. Auto-stop and idle pause both keep the disk and save memory. On E2B, the default onTimeout is kill.
  • Coding agents. Claude Code, Codex and others are installed in every sandbox and driven through sandbox.prompt() (Agents).

What you lose or must change:

  • Self-hosting. E2B is open source under Apache-2.0 and can run in your own cloud (E2B open source). Sandboxer is hosted only.
  • Code interpreter. There is no runCode, kernel state or chart results. See above.
  • Memory in snapshots and forks. E2B's snapshots and forks capture memory and running processes (E2B fork docs). On Sandboxer only stop and resume keep memory. Forks and named snapshots copy the disk and boot fresh.
  • Sizes. E2B lets you set CPU and memory per template. Sandboxer has four fixed sizes, from small (2 vCPU, 4 GB) to xlarge (16 vCPU, 32 GB).
  • Region. Sandboxer has two regions, us-east (Ashburn, Virginia) and eu-central (Helsinki, Finland); see regions. New sandboxes go to the nearest one unless you set region.
  • Free tier. E2B's Hobby plan is free with a one-time $100 credit. Sandboxer's trial needs a card.

Pricing#

E2B's prices are from its pricing page, checked 2026-10-27. Ours are from /pricing. Both bill per second while a sandbox runs.

E2B Sandboxer
Compute $0.000014 per vCPU-second plus $0.0000045 per GiB-second Fixed sizes, from $0.018/hour for small
4 vCPU and 8 GB $0.3312 per hour (4 × $0.000014 × 3600 + 8 × $0.0000045 × 3600) default size: $0.036/hour
Plans Hobby $0 with a one-time $100 credit; Pro $150 a month plus usage From $20 a month, all returned as machine time
Running at once 20 on Hobby, 100 on Pro 2 on the trial, 100 on the $20 plan
Storage 10 GiB on Hobby, 20 GiB on Pro, free Not billed; 50 GB included on the $20 plan
Trial No card needed 7 days and 25 hours; needs a card

Example: one 4 vCPU, 8 GB sandbox running 8 hours a day for 22 days is 176 hours. On E2B that is about $58.29 of usage, and runs that long need Pro, since Hobby caps sessions at 1 hour. On Sandboxer, the default size costs 176 × $0.036 = $6.34, covered by the $20 plan's included time.

FAQ#

Do I have to rewrite my E2B code?#

You change the import, the constructor and some method names. The flow stays the same: create, run commands, read and write files, pause, resume and kill. The tables above map each call.

Is there a replacement for @e2b/code-interpreter?#

Not as an API. Run Python with sandbox.exec, and keep state in files. If you need a live kernel, install Jupyter in a setup script and start it yourself with sandbox.start.

Does stop keep running processes like E2B's pause?#

Yes. In our test a Python process kept its PID and its in-memory count across a stop and resume. If no machine with the same CPU type is free within 2 minutes, a resume boots from disk and says so. Pass --require-memory to wait instead.

What happens to my E2B templates?#

Rebuild them once. Put the build commands in a setup script, run sbx new --setup-file, then save the result with sbx snapshot <id> <name>. Start new sandboxes with --from <name>.

Why does my sandbox not die when the timeout ends?#

On Sandboxer, auto-stop stops the sandbox and keeps its disk and memory, so you can resume it later. Call delete when you want it gone, as you called kill on E2B.

All migration guides