# Migrate from E2B to Sandboxer

> 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.

Source: https://sandboxer.com/migrate/e2b. Updated 2026-10-27.

**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](https://e2b.dev/security)) | Sandbox (a microVM with its own kernel, root, systemd and Docker) ([Machines](/docs/machines)) |
| Template, built with the `Template()` SDK or `e2b template` ([E2B docs](https://docs.e2b.dev/template/quickstart)) | Setup script (`--setup-file`) saved as a named snapshot, started with `--from` ([Snapshots](/docs/snapshots#named-snapshots-templates)) |
| Pause and `connect()`, which keep memory ([E2B docs](https://docs.e2b.dev/sandbox/persistence)) | `stop` and `resume`, which keep memory when the resume lands on the same CPU type ([Snapshots](/docs/snapshots#resume)) |
| 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](/docs/long-running)) |
| `lifecycle.onTimeout: 'pause'` | Always: auto-stop saves memory, and idle sandboxes pause and wake on use |
| Public URL, `getHost(port)` ([E2B docs](https://docs.e2b.dev/network/public-url)) | Hosted URL, `host(port)`, private by default ([Hosting](/docs/hosting)) |
| `E2B_API_KEY` | `SANDBOXER_API_KEY` ([API keys](/docs/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](/docs/environments)) |
| Desktop SDK with VNC streaming | Desktop (XFCE and Chrome) in every sandbox ([Desktop](/docs/desktop)) |
| Code interpreter (`runCode`, a Jupyter kernel) | No equivalent; run `python3` with `exec` |

## API and SDK mapping

E2B calls come from its [docs](https://docs.e2b.dev/sandbox) and the [comparison data](/compare/e2b) we checked on 2026-10-15. Ours come from the [TypeScript SDK](/docs/sdk-typescript) and [Python SDK](/docs/sdk-python) 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](https://docs.e2b.dev/sandbox)). 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](https://docs.e2b.dev/quickstart) (TypeScript tab):

```typescript
// 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:

```bash
npm i sandboxer-sdk
export SANDBOXER_API_KEY=sbx_live_…
```

```typescript
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"));
```

```text
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:

```python
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))
```

```text
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:

```bash
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:

```bash
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"
```

```text
    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)`:

```bash
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`:

```bash
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](https://docs.e2b.dev/template/examples/docker)). 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](/docs/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](https://docs.e2b.dev/sandbox)). 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](https://docs.e2b.dev/use-cases/k3s)).
- **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](/docs/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](https://e2b.dev/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](https://docs.e2b.dev/sandbox/fork)). 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](/docs/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](https://e2b.dev/pricing), checked 2026-10-27. Ours are from [/pricing](/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.

## Related

- [Sandboxer vs E2B](/compare/e2b)
- [Quickstart](/docs/quickstart)
- [TypeScript SDK](/docs/sdk-typescript)
- [Snapshots](/docs/snapshots)
