A Go client for the E2B sandbox platform, ported from the official Python and JavaScript SDKs.
Unofficial, community-maintained port. This module is not published by the E2B team — it tracks the upstream spec but is independent of them. Report SDK issues here; report E2B platform issues upstream.
go get github.com/eric642/e2b-go-sdkpackage main
import (
"context"
"fmt"
"log"
"time"
"github.com/eric642/e2b-go-sdk"
)
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
defer cancel()
sbx, err := e2b.Create(ctx, e2b.CreateOptions{
Template: "base",
Timeout: 5 * time.Minute,
})
if err != nil {
log.Fatal(err)
}
defer sbx.Kill(ctx)
handle, err := sbx.Commands.Run(ctx, "sh", e2b.RunOptions{
Args: []string{"-c", "echo hello"},
})
if err != nil {
log.Fatal(err)
}
result, err := handle.Wait(ctx)
if err != nil {
log.Fatal(err)
}
fmt.Println(result.Stdout) // hello
}When you create or manage more than a couple of sandboxes, hold a single
e2b.Client. It resolves the config once and shares one HTTP client and one
control-plane REST client across every call and every sandbox it creates:
c, err := e2b.NewClient(e2b.Config{}) // reads credentials from the env
if err != nil {
log.Fatal(err)
}
sbx, err := c.Create(ctx, e2b.CreateOptions{Template: "base"})
if err != nil {
log.Fatal(err)
}
defer sbx.Kill(ctx)
// List all running sandboxes (paginated under the hood).
running, err := c.ListAll(ctx, e2b.SandboxListOptions{
State: []e2b.SandboxState{e2b.SandboxStateRunning},
})
if err != nil {
log.Fatal(err)
}
for _, s := range running {
fmt.Println(s.SandboxID, s.State)
}
// Or page manually:
p := c.List(ctx, e2b.SandboxListOptions{})
for p.HasNext() {
page, err := p.NextItems(ctx)
if err != nil {
log.Fatal(err)
}
// ... use page ...
}The package-level e2b.Create / Connect / Kill / List / ListAll
functions still work; they build a throwaway Client per call and are kept for
compatibility. Prefer NewClient to reuse the REST client across calls.
The SDK reads credentials from the environment:
| Variable | Purpose |
|---|---|
E2B_API_KEY |
Team API key (X-API-Key header) |
E2B_ACCESS_TOKEN |
User access token (Authorization: Bearer) |
E2B_DOMAIN |
Override the default e2b.app domain |
E2B_API_URL |
Full override of the control-plane URL |
E2B_SANDBOX_URL |
Override the envd URL (used for tunneling/tests) |
E2B_DEBUG |
true targets http://localhost:3000 |
Pass an explicit e2b.Config to any *Options struct to override.
| Path | Description |
|---|---|
github.com/eric642/e2b-go-sdk |
Core Sandbox, Commands, Filesystem, Pty, Git |
github.com/eric642/e2b-go-sdk/template |
Template builder + ReadyCmd helpers |
github.com/eric642/e2b-go-sdk/volume |
Persistent volume client |
- Control-plane REST at
https://api.<domain>— generated fromspec/openapi.ymlusing oapi-codegen. - envd Connect-RPC at
https://49983-<sandboxID>.<domain>— generated fromspec/envd/**/*.protousing connectrpc.com/connect. - envd plain HTTP (
/files,/metrics,/envs) — generated fromspec/envd/envd.yaml. - Volume content REST — generated from
spec/openapi-volumecontent.yml.
All generated code lives under internal/ and is not part of the public API.
This SDK will track upstream e2b-dev/E2B
e2b@X.Y.Z tags 1:1 once aligned. Pre-alignment (v0.x) releases pin
an exact upstream commit instead; see spec/E2B_VERSION and
make version for what any given build is compiled against.
make tools # one-time: install buf, oapi-codegen, protoc-gen-*
make sync-spec E2B_TAG=e2b@2.19.0 # pin upstream spec (fetches tags)
make codegen # regenerate internal/ clients
# Convenience: sync + regen in one step. Defaults to the newest e2b@* tag.
make regen
make regen E2B_TAG=e2b@2.19.0
# Inspect what you're currently building against:
make versionsync-spec.sh copies the relevant spec files from the E2B/ submodule
into ./spec/ (so the submodule can stay detached at any ref without
affecting builds), writes spec/E2B_VERSION and
internal/version/upstream.go, and updates the top-level VERSION file.
Pass --skip-fetch to work offline (reuses the submodule's existing
local refs). See CHANGELOG.md for the full release
workflow.
| Path | What it shows |
|---|---|
examples/basic |
Minimal Create / Commands.Run loop |
examples/terminal |
Interactive PTY session |
examples/template |
Programmatic template.Builder (v3 build API) |
examples/lifecycle |
Full template + sandbox lifecycle on the v3 API |
examples/lifecycle_v2 |
Same lifecycle against legacy /v2/templates (self-hosted ≤ 2.1) |
examples/selfhosted |
Running against a self-hosted E2B deployment |
examples/desktop |
VNC desktop template — split noVNC URLs for terminal and browser, |
| PTY mirrored into tmux, on-demand chromium launch |
Run any example with source ./.env && go run ./examples/<name>.
examples/desktop builds the e2b.Dockerfile template and exposes two
separate noVNC URLs — one per X display — so the terminal and browser
don't share a desktop:
:0→6080— an xterm attached to the sharedtmux -L mainsession. AnyPty.Createopened through the SDK appears here in real time.:1→6081— launched on demand bylaunchBrowser, which starts chromium with--no-sandbox --disable-dev-shm-usageon the browser display and blocks until the window is mapped.
Set E2B_TEMPLATE_ID to reuse an already-built template (skips build +
teardown). Set DESKTOP_BROWSER_URL to override the page chromium opens.
The driver blocks after printing both URLs so you can open them; hit
Ctrl+C to tear the sandbox + template down.
go test ./... # unit tests (no network)
go test -tags=integration ./... # integration tests (requires E2B_API_KEY)CI is split across two workflows:
.github/workflows/test.yml— unit tests on every push & PR..github/workflows/integration.yml— integration tests on pushes tomain/masterand manualworkflow_dispatch. Fork PRs don't get access tosecrets.E2B_API_KEY, so this workflow simply isn't scheduled for them (shows as "skipped" in the check UI).
v1 implements the core sandbox surface:
-
Create,Connect,Kill,List/ListAll,Pause,SetTimeout,GetInfo,IsRunning -
GetMetrics(optionalMetricsOptions{Start, End}time window) - Snapshots:
CreateSnapshot,ListSnapshots/ListAllSnapshots,DeleteSnapshot - MCP:
GetMcpURL/GetMcpToken,GetHost,UploadURL/DownloadURL -
Commands.Run/Start/Connect/List/Kill/SendStdin/CloseStdin -
Pty.Create/Resize/SendInput/Kill -
FilesystemRead/Write/WriteFiles/List(withDepth) /Stat/Move/Remove/MakeDir/Watch -
GitClone / Add / Commit / Push / Pull / Status / Branches / … -
VolumeCreate / Connect / List / ReadFile / WriteFile / Remove / MakeDir / Delete -
template.Builderserialization, server-side build orchestration (v3Build/BuildStream/BuildInBackground), and legacy v2 counterparts (BuildV2/BuildStreamV2/BuildInBackgroundV2) -
template.Client.List/Get/Delete/SetPublic/GetBuildLogs
The SDK distinguishes interface versions on two axes:
-
Control plane. It calls the current endpoint for each operation: sandbox listing uses v2 (
GET /v2/sandboxes, cursor pagination via thex-next-tokenheader); template builds use v3 with legacy v2 counterparts;Connectresumes a paused sandbox (no separateResume). -
envd (in-sandbox). Behaviour is gated on the sandbox's
EnvdVersionto stay compatible with older templates, mirroring the JS/Python SDKs:- file uploads use
application/octet-streamon envd ≥ 0.5.7 and fall back tomultipart/form-databelow it; - the username/
Authorizationheader is omitted on envd ≥ 0.4.0 (the server infers the default user) and injected below it; - recursive
Filesystem.Watchrequires envd ≥ 0.1.4 andCommands.CloseStdinrequires ≥ 0.5.2 — both fail fast with a clear error on older builds rather than an opaque RPC failure.
Filesystem.Watchuses the streamingWatchDirRPC; the non-streaming watcher RPCs (CreateWatcher/GetWatcherEvents/RemoveWatcher) are intentionally not exposed. - file uploads use
Not yet wrapped (available in the generated client under internal/api, and
also unexposed by the reference SDKs): sandbox logs, batch / team metrics,
filesystem gzip, and Refresh.
Apache 2.0, matching upstream E2B.