Migrate from E2B#

Dormo covers the same core workflow as E2B: create a sandbox from a template, run commands, read and write files, expose ports, pause and resume. Most migrations are a mechanical rename. This page maps the concepts and lists the differences in behavior.

E2B compatibility adapter: Coming soon. We plan an adapter that lets existing E2B SDK code run against Dormo after you change the import and the API key. Until it ships, use the mapping below.

E2B's SDKs change over time. The E2B names below describe its public SDK in general terms; check E2B's current documentation for exact signatures.

What changes conceptually#

E2B Dormo
Isolation Firecracker microVM Firecracker microVM with its own kernel, Ubuntu 24.04, root, systemd and Docker inside
Lifetime Session with a timeout. The sandbox is killed or paused when the timeout runs out. Persistent computer. No time limit by default. Idle sandboxes auto-pause (default after 5 min) and wake on the next request.
Disk Ephemeral unless paused Persistent disk (50 GB included with default)
Billing while idle Depends on plan and pause usage $0 compute while paused, billed per second while running
Fork — Fork a running VM with memory (target < 2 s)
Region Check E2B EU only

API and SDK mapping#

E2B (general) Dormo TypeScript Dormo REST
Sandbox.create(template) client.sandboxes.create({ template }) POST /sandboxes
Connect to an existing sandbox by ID client.sandboxes.get(id) GET /sandboxes/{sandbox_id}
sandbox.commands.run(cmd) sandbox.exec({ command }) POST /sandboxes/{sandbox_id}/exec
Run in the background sandbox.exec({ command, background: true }) same endpoint, background: true
Stream output callbacks sandbox.exec({ command, stream: true }) same endpoint, stream: true (SSE)
Interactive terminal (PTY) PTY session over WebSocket POST /sandboxes/{sandbox_id}/pty
files.read(path) / files.write(path, data) sandbox.files.read(path) / sandbox.files.write(path, data) GET / PUT /sandboxes/{sandbox_id}/files/content
files.list(dir) sandbox.files.list(dir) GET /sandboxes/{sandbox_id}/files
files.watchDir(dir) sandbox.files.watch(dir) GET /sandboxes/{sandbox_id}/files/watch (SSE)
getHost(port) sandbox.ports.expose({ port, visibility }) returns url POST /sandboxes/{sandbox_id}/ports → https://<short_id>-<port>.dormo.host
Sandbox timeout / setTimeout auto_pause.idle_timeout_seconds (pause when idle) or ttl_seconds (delete after N s) PATCH /sandboxes/{sandbox_id}
Pause / resume sandbox.pause() / sandbox.resume(), plus automatic pause and wake on request POST .../pause, POST .../resume
kill() sandbox.delete() DELETE /sandboxes/{sandbox_id}
Custom template (Dockerfile-based) Template from an OCI image POST /templates with image
Desktop SDK template: "desktop" + sandbox.desktop.* /desktop, /screenshot, /input
API key header Authorization: Bearer dormo_sk_... —

Before and after#

// E2B (illustrative)
// const sbx = await Sandbox.create("my-template");
// const r = await sbx.commands.run("python app.py");
// const url = `https://${sbx.getHost(3000)}`;

// Dormo
import { Client } from "@dormo/sdk";

const client = new Client(); // reads DORMO_API_KEY
const sandbox = await client.sandboxes.create({ template: "my-template" });
const r = await sandbox.exec({ command: "python app.py", timeout_seconds: 120 });
console.log(r.exit_code, r.stdout);

await sandbox.exec({ command: "python -m http.server 3000", background: true });
const { url } = await sandbox.ports.expose({ port: 3000, visibility: "public" });
from dormo import Client

client = Client()
sandbox = client.sandboxes.create(template="my-template")
r = sandbox.exec(command="python app.py", timeout_seconds=120)
print(r.exit_code, r.stdout)

Templates#

E2B templates are typically built from a Dockerfile. On Dormo:

  1. Build and push the image to any OCI registry: docker build -t ghcr.io/acme/env:1 . && docker push ....
  2. POST /templates with {"name": "my-template", "image": {"ref": "ghcr.io/acme/env:1"}}. Add start_command and ready_check if something must be running before the snapshot is taken.
  3. Create sandboxes with template: "my-template". The template is a RAM snapshot, so a new sandbox starts with your processes already running (target p95 < 500 ms).

A second option: set up one sandbox by hand, take a snapshot and turn it into a template (snapshot_id). See Templates.

Timeouts: think "idle" instead of "max lifetime"#

An E2B sandbox usually lives for a fixed time window. A Dormo sandbox is meant to persist, and the controls are:

  • auto_pause.idle_timeout_seconds (default 300): pause after this much idle time. Paused sandboxes cost $0 for compute, and the next exec, file, port, SSH or desktop request wakes them (target p95 < 800 ms).
  • ttl_seconds: delete the sandbox automatically after N seconds. Use it for one-off jobs.
  • always_on: true: never pause (fixed monthly price, default size only).

So a typical "give every user their own VM" setup is: create one sandbox per user, keep it forever, and let auto-pause handle cost. See Lifecycle.

Cost comparison#

Hourly price for 4 vCPU / 8 GB, according to battleships.dev as of 2026-10: E2B $0.331 per hour; Dormo default $0.030 per active hour.

Example: 1000 VMs, each active 10% of the time (73 h/month):

Monthly
E2B (73 h × $0.331) ~$24,163
Dormo (73 h × $0.030, $0 while paused) ~$2,190

Your numbers depend on your plan and usage. Check current pricing on both sides. See Billing.

Checklist#

  • [ ] Create a Dormo API key with the full-sandbox preset (POST /api-keys)
  • [ ] Rebuild custom templates from your image (POST /templates)
  • [ ] Replace SDK calls using the table above (snake_case fields, e.g. timeout_seconds, background)
  • [ ] Replace per-session timeouts with auto_pause and/or ttl_seconds
  • [ ] Update preview URLs to https://<short_id>-<port>.dormo.host
  • [ ] Subscribe to webhooks (sandbox.ready, sandbox.paused, sandbox.error, ...)
  • [ ] Handle errors by error.code and error.retryable (see API reference)