Local E2B-compatible sandbox runtime. A single Go binary that exposes a wire-compatible subset of the E2B cloud sandbox API on a developer's laptop: point an unmodified E2B SDK at it via env vars and sandboxes run in local Docker containers instead of E2B's cloud.
- Drop-in compatible with unmodified E2B Python and TypeScript SDKs
- Single binary — no Redis, Postgres, Nomad, or Firecracker
- Cross-platform — Linux and macOS (x86_64 + arm64)
- Sandbox lifecycle — create, kill, pause, resume, snapshots
- Custom templates — programmatic
Template.build()from the SDK - Code interpreter —
@e2b/code-interpreterSDK works out of the box - Full API surface — teams, volumes, api-keys stubs so the SDK never crashes
go install github.com/contember/edvabe/cmd/edvabe@latest
edvabe doctor # preflight check
edvabe build-image # first-time: build edvabe/base:latest (~60s)
edvabe serve # listens on :3000docker run --rm \
-p 3000:3000 \
-v /var/run/docker.sock:/var/run/docker.sock \
-v edvabe-data:/data \
ghcr.io/contember/edvabe:main serveservices:
edvabe:
image: ghcr.io/contember/edvabe:main
ports:
- "3000:3000"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- edvabe-data:/data
environment:
EDVABE_STATE_DIR: /data
EDVABE_CACHE_DIR: /data/cache
volumes:
edvabe-data:The Docker socket mount is required — edvabe creates sandbox containers as siblings on the host Docker daemon.
Set these env vars in the process running your E2B SDK code:
export E2B_API_URL=http://localhost:3000
export E2B_DOMAIN=localhost:3000
export E2B_API_KEY=edvabe_local
export E2B_SANDBOX_URL=http://localhost:3000When running inside docker-compose, replace localhost:3000 with
edvabe:3000 (the service name).
Then unmodified SDK code works:
from e2b import Sandbox
sbx = Sandbox.create(timeout=60)
result = sbx.commands.run("echo hello from edvabe")
print(result.stdout) # "hello from edvabe\n"
sbx.kill()edvabe build-image --template=code-interpreter # ~10 min first timefrom e2b_code_interpreter import Sandbox
sbx = Sandbox()
execution = sbx.run_code("1 + 1")
print(execution.results[0].text) # "2"
sbx.kill()The SDK's programmatic Template.build() works against edvabe — it
translates step arrays into Dockerfiles and builds them locally:
import { Template, Sandbox } from 'e2b'
const tpl = Template()
.fromImage('oven/bun:slim')
.aptInstall(['curl', 'git'])
.runCmd('echo "built" > /etc/marker')
await Template.build(tpl, { alias: 'my-template' })
const sbx = await Sandbox.create('my-template')edvabe serve [--port 3000] # start server
edvabe doctor [--port 3000] # preflight checks
edvabe build-image [--template=base|code-interpreter|all] # build images
edvabe pull-base # pull upstream e2bdev/base
edvabe version # print version| Variable | Default | Description |
|---|---|---|
EDVABE_STATE_DIR |
~/.local/share/edvabe |
Template store (templates.json) |
EDVABE_CACHE_DIR |
~/.cache/edvabe/template-files |
File context cache |
EDVABE_BUILD_DIR |
~/.cache/edvabe/builds |
Build scratch directory |
DOCKER_HOST |
auto-detected | Docker socket path |
make test # go test ./...
make test-e2e-python # Python SDK E2E (create, commands, files, pty, watch)
make test-e2e-ts # TypeScript SDK E2E
make test-e2e-code-interpreter-python # code interpreter Python E2E
make test-e2e-code-interpreter-ts # code interpreter TypeScript E2Eedvabe implements the E2B control plane in Go — sandbox CRUD, template builds, pause/resume, and all the stub endpoints the SDK expects. The data plane (filesystem, process, PTY, watchers) is handled by upstream envd running inside each sandbox container. edvabe reverse-proxies SDK requests to envd, giving byte-exact wire compatibility without reimplementing the ~6000 LOC envd protocol.
SDK ──HTTP──> edvabe (:3000)
│
├─ control plane (Go)
│ sandboxes, templates, teams, volumes, ...
│
└─ reverse proxy ──> Docker container
│ │
│ ├─ envd (:49983)
│ │ files, process, PTY, watch
│ │
│ └─ code-interpreter (:49999)
│ Jupyter + FastAPI overlay
│
└─ routed by E2b-Sandbox-Id + E2b-Sandbox-Port headers
Key design decisions:
- Templates are Docker images.
Template.build()translates SDK step arrays into generated Dockerfiles and runsdocker build. - envd is injected into every user template via a
COPY --fromstage — user images don't need to know about envd. - Pause =
docker pause, snapshot =docker commit. No live memory snapshots. Documented trade-off for a local dev tool. - Runtime and Agent are pluggable interfaces behind
internal/runtime/andinternal/agent/. Docker is the v1 backend; Firecracker/libkrun could slot in later.
See docs/05-architecture.md for the full design and docs/03-api-surface.md for the wire protocol.
MIT — Contember Limited