Omnigent on E2B
E2B sandboxes give you disposable cloud machines for running Omnigent hosts, two ways:
- CLI-launched:
omnigent sandbox create/connectprovisions a sandbox from your terminal, ships your local checkout into it, and registers it as a host with your server. - Server-managed: the server provisions a sandbox automatically when
a session is created with
"host_type": "managed"and terminates it when the session is deleted.
Important
E2B boots from a pre-built template, not a registry image. Unlike the Modal / Daytona / CoreWeave launchers — which pull
ghcr.io/omnigent-ai/omnigent-hostdirectly — E2B cannot start an arbitrary registry image at create time. You must first build the Omnigent host image into an E2B template (a one-time step, below); the launcher'stemplatefield then names that template, not aghcr.io/...reference. This is the one real difference from the other sandbox providers. This directory is not a server deploy target.
Prerequisites
pip install 'omnigent[e2b]' # installs the e2b SDK extra
npm i -g @e2b/cli # the E2B CLI, for building the template
Create an API key in the E2B dashboard and make it available where the launcher runs — your shell for the CLI flow, the server process for managed sandboxes:
export E2B_API_KEY=e2b_…
e2b auth login # one-time, authenticates the E2B CLI too
Note
Lifetime is capped and cannot be disabled. An E2B sandbox carries a single timeout (default 5 minutes; account maximum 24 h on Pro, 1 h on Hobby) with no "never expire" option. Omnigent requests the 24 h maximum at creation, but E2B rejects (does not clamp) a request above the account cap, so
provisionautomatically retries clamped to the account's maximum (e.g. 1 h on Hobby) — verified live. SetOMNIGENT_E2B_MAX_LIFETIME_Sto request a specific lifetime and skip the retry. A managed session outliving the cap relies on the dead-sandbox relaunch path (same posture as Modal's 24 h limit), so a Pro account is recommended for anything beyond short demos.
Build the host template (one time)
E2B builds a template from a Dockerfile whose base image must be
Debian-based and single-stage. The Omnigent host image
(python:slim, Debian) satisfies both — so the template Dockerfile is a
one-liner that layers nothing on top of the published image:
mkdir -p omnigent-e2b && cd omnigent-e2b
cat > e2b.Dockerfile <<'EOF'
# Single-stage, Debian-based — both E2B requirements. The host image
# already bakes the full omnigent install plus git / tmux / curl, so
# nothing else is needed here.
FROM ghcr.io/omnigent-ai/omnigent-host:latest
EOF
e2b template build --name omnigent-host --dockerfile e2b.Dockerfile
omnigent-host is the default template name the launcher looks for
(DEFAULT_E2B_TEMPLATE), so
a deployment that uses that name needs no further config. Use a different
name (or pin a :sha-<short> host image) and point the launcher at it
with sandbox.e2b.template / OMNIGENT_E2B_TEMPLATE.
To run your own host image, build the host target of
deploy/docker/Dockerfile
(--platform linux/amd64), push it anywhere E2B can pull from, and FROM
that ref in e2b.Dockerfile instead. Rebuild the template whenever the
host image changes (the CLI flow still overlays your local wheels on
top per-sandbox, so day-to-day code changes don't need a template
rebuild).
CLI-launched sandboxes
Provision a sandbox and ship your local checkout into it:
omnigent sandbox create --provider e2b
This starts a sandbox from the omnigent-host template, builds wheels
from your local checkout, and overlays them on top — so the sandbox runs
your code, not whatever the template was built from. Then register it
as a host with your server:
omnigent sandbox connect --provider e2b \
--sandbox-id <id-printed-by-create> \
--server https://your-host
connect runs omnigent host inside the sandbox and holds the
connection open in your terminal — Ctrl-C tears it down (and kills the
remote process; E2B exposes a real kill handle). New sessions targeting
that host now run in the sandbox.
Running multiple sandboxes against one server? Pass a unique
--host-name <label> to each connect — the server keys hosts on
(owner, name), and sandboxes that share a hostname collide.
Sandboxes are disposable. When your code changes, create a new one — and
delete the old one (via the dashboard or
e2b sandbox kill <id>), though E2B also reaps it automatically at its
timeout.
To inject LLM/git credentials into a CLI-launched sandbox, set
OMNIGENT_E2B_SANDBOX_ENV in your shell to a comma-separated list of
variable names (e.g. ANTHROPIC_API_KEY,GIT_TOKEN) before running
create — the named variables are copied from your environment into the
sandbox at provision time.
Note
E2B has no local→sandbox port forward (it exposes sandbox ports outward via public URLs only). The interactive in-sandbox
omnigent login/ App OAuth step is therefore skipped automatically (as on Modal / Daytona): use E2B with servers that don't require in-sandbox App auth, or authenticate via injected credentials (below).
Server-managed sandboxes
Add a sandbox: section to the server config (omnigent server -c config.yaml, or <data_dir>/config.yaml):
sandbox:
provider: e2b
server_url: https://your-host # public URL sandboxes dial back to
server_url must be reachable from E2B's cloud — a public HTTPS URL,
not localhost. Sessions created with host_type: "managed" (the API
call or the Web UI's New Sandbox option) then run on a fresh E2B sandbox;
the create returns immediately and provisioning happens in the
background, exactly like the Modal managed
flow — including repository
workspaces, the first-message rendezvous, and dead-sandbox relaunch.
Optional e2b: settings:
sandbox:
provider: e2b
server_url: https://your-host
e2b:
template: omnigent-host # E2B template NAME (default: omnigent-host)
env: [OPENAI_API_KEY, ANTHROPIC_API_KEY, GIT_TOKEN]
Note
sandbox.e2b.templateis an E2B template name (built above), not a registry image reference — the field that holds aghcr.io/...ref on the other providers. Omit it to use the defaultomnigent-hosttemplate.
Credentials for the sandbox (LLM keys, git tokens)
sandbox.e2b.env lists the names of variables to copy from the
server's own environment into every sandbox at provision time (passed
to Sandbox.create(envs=…)). Values never live in the config file — set
them where the server runs:
export OPENAI_API_KEY=sk-… # on the server
export GIT_TOKEN=github_pat_… # private-repo clone/fetch/push
sandbox:
provider: e2b
server_url: https://your-host
e2b:
env: [OPENAI_API_KEY, GIT_TOKEN]
A listed name that is not set in the server's environment fails the launch loudly (it would otherwise surface much later as an opaque harness auth failure inside the sandbox).
Which variables to inject — providers, gateways, subscriptions, git — is
identical to Modal; see the variable table and per-plan
recipes and
git credentials.
The in-sandbox host forwards the same standard set to its runners, and
OMNIGENT_RUNNER_ENV_PASSTHROUGH (as an injected variable) names any
extras.
The same env-injection also carries credentials for connecting to the
server itself, for a host that authenticates its dial-back with user
credentials instead of a launch token. Managed launches never need this:
the server injects a per-launch host token automatically. But a
CLI-launched host does when the server requires
authentication — name the keys (e.g. DATABRICKS_HOST +
DATABRICKS_TOKEN) in OMNIGENT_E2B_SANDBOX_ENV before create. See
Connecting to an authenticated
server in the
Modal guide.
Security considerations
- Injected credentials reach E2B's control plane.
sandbox.e2b.envvalues are sent to E2B's API as literal sandbox env vars. Prefer scoped, short-lived credentials: a fine-grained PAT limited to the repos a session needs, a gateway token over a root provider key. (Modal's launcher attaches named Modal secrets, so its values stay in Modal's secret store — a stronger posture; same trade-off as the Daytona provider.) - All managed sandboxes share one E2B account + API key. Cross-user isolation between Omnigent users rides on E2B's sandbox boundaries, and the shared key can enumerate and kill any user's sandbox. Scope the account to this workload.
- The launch token's lifetime is ~25 h, derived from the requested
lifetime. E2B sandboxes share Modal's 24 h hard cap, so the per-launch
host token outlives the sandbox by an hour to re-authenticate the tunnel
across reconnects. The TTL is computed from
OMNIGENT_E2B_MAX_LIFETIME_S(default 24 h) at server start, so it bounds the longest possible sandbox. On a capped account whereprovisionclamps the sandbox shorter (e.g. Hobby's 1 h), the token over-covers the now-shorter sandbox — safe for re-auth, but a leaked token is replayable for the full window. To tighten this, setOMNIGENT_E2B_MAX_LIFETIME_Sto your account cap so the token TTL tracks the granted lifetime (or set a shortertoken_ttl_son a directly-constructedManagedSandboxConfig). A relaunch mints a fresh token. - Sandbox URLs are public by default. E2B exposes sandbox ports via
public
*.e2b.appURLs; Omnigent never opens one (the host dials out to your server), but be aware nothing in a sandbox should bind a service expecting it to be private without E2B's access-token gating.
Troubleshooting
- "E2B sandbox creation failed: template '…' is unavailable" — the
host image was never built into an E2B template, or the name doesn't
match. Run the template build with
--name omnigent-host(or setsandbox.e2b.templateto your name). - "managed host did not come online within 120s" — the sandbox
couldn't dial back to
server_url. Confirm it's a public HTTPS URL reachable from E2B's cloud (notlocalhost), and check/tmp/omnigent-host.loginside the sandbox. - Sandbox stops after ~1 hour — you're on a Hobby account (1 h cap);
provisionauto-clamps to it (you'll see a one-line warning). Upgrade to Pro for the 24 h maximum, or expect the dead-sandbox relaunch path to re-provision on the next message.
Lifecycle notes
- Hard lifetime cap, no idle-stop disable.
provisionrequestsOMNIGENT_E2B_MAX_LIFETIME_S(default the 24 h Pro maximum); E2B rejects a request above the account cap, so creation retries clamped to it (e.g. 1 h on Hobby).keep_alivere-extends a live sandbox on reconnect, but there is no never-expire option — a managed session past the cap is replaced by the dead-sandbox relaunch path (same as Modal). - Templates, not registry images. See
Build the host template. Resources
(vCPU / memory) are fixed when the template is built — pass
--cpu-count/--memory-mbtoe2b template build— not at sandbox create time. - Custom images require rebuilding the template:
FROMyour image ine2b.Dockerfileande2b template buildit, then setsandbox.e2b.template/OMNIGENT_E2B_TEMPLATE.
Environment variable reference
| Variable | Where it's read | Purpose |
|---|---|---|
E2B_API_KEY |
CLI machine / server | E2B API credentials (required) |
OMNIGENT_E2B_TEMPLATE |
CLI machine / server | E2B template name to provision from (sandbox.e2b.template takes precedence; default omnigent-host) |
OMNIGENT_E2B_SANDBOX_ENV |
CLI machine / server | Comma-separated launcher-side env var names to inject (sandbox.e2b.env takes precedence for managed) |
OMNIGENT_E2B_MAX_LIFETIME_S |
CLI machine / server | Requested sandbox lifetime in seconds (default 24 h); creation auto-clamps to the account cap if exceeded |