Skip to content

Commit 96d405a

Browse files
svallerue2b-bot[bot]
authored andcommitted
docs(embed): make the hub README a landing page and move the reference to docs/
GitOrigin-RevId: 4c4a65fee64a63b750d07ea5e236e25fa7389239
1 parent aabde69 commit 96d405a

4 files changed

Lines changed: 316 additions & 240 deletions

File tree

embed/README.md

Lines changed: 76 additions & 233 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,14 @@
55

66
# E2B Embed
77

8-
E2B Embed is a complete E2B, sandboxes included, on one machine you own. Three
9-
ways to get that machine running, all of them the same stack.
8+
**A complete E2B, sandboxes included, on one machine you own.** Three ways to
9+
get that machine running, all of them the same stack.
10+
11+
[Docker Compose](compose/README.md)
12+
| [Terraform on GCP](terraform/gcp/README.md)
13+
| [Kubernetes](kubernetes/README.md)
14+
| [Reference](docs/REFERENCE.md)
15+
| [Releasing](../docs/RELEASING.md)
1016

1117
![Overview: the running stack on one machine](docs/overview.svg)
1218

@@ -19,25 +25,46 @@ ways to get that machine running, all of them the same stack.
1925
| Kubernetes | a cluster with one KVM node | `kubectl apply -k` | [`kubernetes/README.md`](kubernetes/README.md) |
2026

2127
Compose and Terraform run [`compose/compose.yaml`](compose/compose.yaml) and
22-
[`compose/.env`](compose/.env) as shipped, differing only in who prepares the
23-
machine (you, or the instance template) and where the two files come from
24-
(your download, or instance metadata). Kubernetes runs a StatefulSet
25-
translated from them, repeating the same pins in
26-
[`kubernetes/kustomization.yaml`](kubernetes/kustomization.yaml), which
27-
[`tests/kubernetes.bats`](tests/kubernetes.bats) keeps in step. These are not
28-
three architectures, and all three are single-machine evaluation packages
29-
rather than deployment patterns; for a production deployment see
30-
https://e2b.dev/enterprise.
31-
32-
## What every shape gives you
33-
34-
Sandboxes are real Firecracker microVMs on the machine, and everything they
35-
need stays there: the databases, the templates you build and the logs all
36-
live on its disk. Each install gets its own team API key, generated on the
37-
first start. Eleven ports listen on every interface of the machine. The SDK
38-
needs the first two. The other nine must not be reachable on any address the
39-
machine holds: give it no public address of its own, or firewall those nine
40-
ports for that address as well, not only at the network edge.
28+
[`compose/.env`](compose/.env) as shipped. Kubernetes runs a StatefulSet
29+
translated from them, with the same pins in
30+
[`kubernetes/kustomization.yaml`](kubernetes/kustomization.yaml). All 3 are
31+
single-machine evaluation packages, not deployment patterns. For a production
32+
deployment see [e2b.dev/enterprise](https://e2b.dev/enterprise).
33+
34+
## What you get
35+
36+
- **Real Firecracker sandboxes on the machine.** Everything they need stays
37+
there: the databases, the templates you build and the logs all live on its
38+
disk.
39+
- **A team API key per install.** The first start generates it and prints it
40+
with the 2 SDK URLs. Each guide's Secrets section says where its copy lives
41+
and how to rotate it.
42+
- **Your own templates.** `Template.build` builds through the same API on any
43+
shape. The build runs inside a Firecracker VM on the machine; no Docker
44+
daemon is involved.
45+
- **Any port inside a sandbox.** `sandbox.get_host(port)` returns an `e2b.app`
46+
name that does not resolve here. Reach the port through client-proxy's
47+
header routing instead:
48+
49+
```bash
50+
curl -H "E2b-Sandbox-Id: $SANDBOX_ID" -H "E2b-Sandbox-Port: 8080" http://localhost:3002/
51+
```
52+
53+
- **One version everywhere.** Embed is released with the platform at one
54+
version, and that release moves every platform pin in
55+
[`compose/.env`](compose/.env) and the kustomization. To pin an install, pin
56+
the commit: put it in place of `main` in the raw URLs, or add
57+
`?ref=<commit>` to the git URLs.
58+
- **Public images, pulled anonymously.** The released E2B images, the 3 stack
59+
images Embed builds itself and the Firecracker binaries are all public. The
60+
stores come from Docker Hub.
61+
62+
## Ports
63+
64+
11 ports listen on every interface of the machine. The SDK needs 3000 and
65+
3002. The other 9 must not be reachable on any address the machine holds: give
66+
it no public address of its own, or firewall those 9 ports for that address as
67+
well, not only at the network edge.
4168

4269
| Port | Service | Reachable from | Purpose |
4370
|------|---------|----------------|---------|
@@ -53,225 +80,41 @@ ports for that address as well, not only at the network edge.
5380
| 5018 | orchestrator | the machine only | sandbox egress: TCP firewall proxy (other) |
5481
| 5109 | api | the machine only | edge gRPC |
5582

56-
Port 5008 is the one to be most careful about: it creates and kills sandboxes
57-
and starts template builds, nothing authenticates it (the api dials it
58-
directly as `LOCAL_ORCHESTRATOR_ADDRESS`), and anyone who reaches it has the
59-
whole orchestrator. The four egress-proxy ports (5010 and 5016 to 5018) take
60-
the sandbox traffic the orchestrator redirects inside each sandbox's network
61-
namespace; they expect no client from outside the machine and have no
62-
authentication of their own.
63-
64-
Everything else stays on loopback: Postgres on 5432, Redis on 6379,
65-
ClickHouse on 8123 and 9000 (on Kubernetes also on 9004, 9005 and 9009, its
66-
MySQL and PostgreSQL wire protocols and its interserver port, since the pod
67-
shares the node's network), Vector's log listener on 30006 (20006 on
68-
Kubernetes, for the reason that guide gives) and its API on 44313 on
69-
Kubernetes only, and the two pprof endpoints, 6060 for api and 6061 for the
70-
orchestrator. `sudo ss -ltnp` on the machine confirms the
71-
split: the eleven ports above show a `*:` address, everything in this
72-
paragraph shows `127.0.0.1:`.
73-
74-
## Reference
75-
76-
### What runs where
77-
78-
The four stores run in containers on a bridge network with their ports on
79-
`127.0.0.1`; api, client-proxy and the released orchestrator run on the
80-
machine's own network. The orchestrator is a host process, launched through
81-
`nsenter` by a privileged container, the same pattern E2B's own Kubernetes
82-
deployment uses; its launcher ends every sandbox when it stops.
83-
84-
Eight one-shots run before the stack is usable, in this order: `preflight`
85-
fails fast with a `FIX:` line when the machine is unsuitable, then
86-
`api-secrets` generates the api's admin token and sandbox-token hash seed
87-
(it needs only the volume, so it finishes first), `host-setup` prepares the
88-
machine on every start, `fetch-artifacts` downloads and verifies the five
89-
Firecracker binaries, `db-migrator` and `clickhouse-migrator` bring the two
90-
databases to the schema their pinned images expect, `seed` writes the default
91-
team and generates this install's API key, and `base-template` builds the
92-
default template through the API. Kubernetes runs seven of them: there the
93-
api's two secrets come from a Secret instead. `ready` is the marker that
94-
everything above worked: not a one-shot but a long-running container, whose
95-
readiness is the whole stack's.
96-
97-
Sandbox and template-build logs live in ClickHouse. The orchestrator and the
98-
api ship their lines (envd's output from inside the VM, the orchestrator's
99-
per-sandbox events, the template-manager's build output, the api's own) to
100-
Vector, which turns each into a row of the `sandbox_logs` table; the api reads
101-
that table for the SDK's `getLogs` and `e2b template build`
102-
(`LOGS_READ_CONFIG=true`, which fixes the api's `logs-read-config` flag where
103-
there is no LaunchDarkly to set it). Retention is the table's seven days. There
104-
is no Loki in this stack, and the api needs no `LOKI_URL`.
105-
106-
### Secrets
107-
108-
The team API key is per install. The seed generates it on the first start and
109-
keeps it beside the databases, so a shape never has a key without its database
110-
or a database without its key, and every shape prints it with the two SDK
111-
URLs. Each guide's Secrets section says where its copy lives and how to pin or
112-
rotate it. A rotation revokes the old key, which keeps working for up to five
113-
minutes, because the api caches team lookups in Redis for that long.
114-
115-
`ADMIN_TOKEN` and `SANDBOX_ACCESS_TOKEN_HASH_SEED` are the api's own two, and
116-
every shape generates them per install too. Compose writes them on the first
117-
start into the volume that holds the team key (`/run/e2b/api.env` in
118-
`seed-state`), Terraform writes generated ones into the instance's `.env` at
119-
first boot, and Kubernetes reads the Secret the install creates. On Compose,
120-
setting either one in `.env` pins it and leaves the other generated; each
121-
guide's Secrets section says how to rotate what its shape holds. Both are
122-
worth guarding: the admin token is admin over the seeded team on port 3000
123-
without the team API key, including minting and revoking API keys (the team's
124-
id is a public constant), and the hash seed makes the
125-
traffic and envd tokens of `secure` sandboxes computable from a sandbox ID
126-
by anyone who reaches 3002.
127-
128-
### Images and pins
129-
130-
[`compose/.env`](compose/.env) is the source of truth for every version the
131-
stack uses: the four released E2B service images (api, db-migrator,
132-
client-proxy, clickhouse-migrator), the three small stack images Embed builds
133-
itself, which carry everything that is not a released E2B service (the host
134-
scripts, the SDK scripts and the database seeder), and the five
135-
Firecracker binaries. Terraform ships that file to the instance, and
136-
Kubernetes repeats its pins in
137-
[`kubernetes/kustomization.yaml`](kubernetes/kustomization.yaml). The three
138-
stack images live in the `embed` repository of the `e2b-artifacts` registry.
139-
140-
Embed is released together with the platform at one version, and that
141-
release moves every platform pin in both files to it: api, db-migrator,
142-
client-proxy, clickhouse-migrator, the orchestrator and the three stack
143-
images (the lines carrying a release marker). A checkout at a release
144-
therefore names one version everywhere, and pulls exactly what that release
145-
published (see [RELEASING.md](../docs/RELEASING.md)). envd has its own
146-
release line, and the kernel, Firecracker and BusyBox are not released here,
147-
so those four are pinned by hand; a bump adds the binary's checksum to
148-
[`compose/scripts/fetch-artifacts.sh`](compose/scripts/fetch-artifacts.sh)
149-
first. The orchestrator needs no row: each release writes a `.sha256`
150-
beside the binary it publishes, and `fetch-artifacts` verifies against it.
151-
All of it is public and pulled anonymously; the stores come from Docker Hub.
152-
153-
To pin an install, pin the commit. The Compose files come from raw URLs, so
154-
put the commit in place of `main` in their path; the Terraform `source` and the
155-
`kubectl apply -k` URL are git URLs and take `?ref=<commit>`. The `main` URLs
156-
the guides use give the newest. `tests/pins.bats` keeps the three pins, their
157-
Kubernetes counterparts and the registry the images are built into in step
158-
with each other.
83+
Port 5008 is the one to guard most: nothing authenticates it, and anyone who
84+
reaches it has the whole orchestrator. The stores, Vector and the pprof
85+
endpoints stay on loopback; [What runs where](docs/REFERENCE.md#what-runs-where)
86+
lists them.
15987

160-
![Layer map: source files, build definitions, images, services](docs/layer-map.svg)
88+
## Not supported
16189

162-
Top to bottom: the source files, the Dockerfiles that copy them, the images
163-
(three built here, eight pulled ready-made) and the compose services. Arrows
164-
in the last band are `depends_on` gates, in start order; the stripe on each
165-
service says which image it runs. Two things the picture leaves out: the
166-
ClickHouse and Vector configs are inlined into `compose/compose.yaml` rather
167-
than shipped in an image, and the five Firecracker artifacts never enter an
168-
image at all, since `fetch-artifacts` writes them onto the machine and the
169-
orchestrator reads them there.
170-
171-
### Beyond the first sandbox
172-
173-
`Template.build` builds your own template through the same API, on any shape:
174-
175-
```python
176-
from e2b import Template, Sandbox
177-
tpl = Template().from_python_image("3.12").run_cmd("pip install requests")
178-
Template.build(tpl, alias="py-requests", cpu_count=2, memory_mb=1024, on_build_logs=lambda e: print(str(e)))
179-
sbx = Sandbox.create("py-requests")
180-
print(sbx.commands.run("python3 -c 'import requests; print(requests.__version__)'").stdout)
181-
sbx.kill()
182-
```
183-
184-
That template, one pip layer on Python 3.12, built in 53 to 79 s across runs
185-
on an 8-vCPU / 32 GiB VM, the spread depending on how much of the base image
186-
was already cached. The build runs inside a Firecracker VM on the machine and
187-
pulls the image from Docker Hub; no Docker daemon is involved.
188-
189-
`sandbox.get_host(port)` returns `{port}-{id}.e2b.app`, which does not resolve
190-
here. Reach a port inside a sandbox through client-proxy's header routing
191-
instead:
192-
193-
```bash
194-
curl -H "E2b-Sandbox-Id: $SANDBOX_ID" -H "E2b-Sandbox-Port: 8080" http://localhost:3002/
195-
```
90+
- **macOS and Windows as the host.** The stack needs a Linux machine with KVM
91+
and a 4 KiB-page kernel, x86-64 or arm64. On Apple silicon that is a Linux
92+
VM with nested virtualization (M3 or newer, macOS 15 or newer). arm64 is
93+
verified end to end on bare metal and needs kernel 6.10 or newer; the
94+
Compose guide's Requirements say why.
95+
- **Container-Optimized OS.** The machine needs apt, a writable `/etc` and
96+
glibc 2.34 or newer.
97+
- **No dashboard.** Only the SDK and API paths are covered.
98+
- **No wildcard DNS and no TLS.**
19699

197-
### Developing
100+
## Developing
198101

199102
`make` is a developer convenience; the operator path is only `docker compose`,
200103
`terraform` or `kubectl`.
201104

202-
| Target | What it does |
203-
|--------|--------------|
204-
| `make images` | build the three stack images locally under their pinned tags |
205-
| `make lint` | render the compose file and the kustomization, validate the Vector config and the Terraform module, shellcheck the scripts and the tests |
206-
| `make test` | run the bats suite in `tests/` |
207-
| `make stores-check` | the store-level integration check |
208-
| `make sync-configs` | re-inline the two configs into the compose file |
105+
- `make images` builds the 3 stack images locally under their pinned tags.
106+
- `make lint` renders the compose file and the kustomization, validates the
107+
Vector config and the Terraform module, and shellchecks the scripts and the
108+
tests.
109+
- `make test` runs the bats suite in `tests/`.
110+
- `make stores-check` runs the store-level integration check.
111+
- `make sync-configs` re-inlines the 2 configs into the compose file.
209112

210-
`make lint` wants `shellcheck`, `terraform` (1.7.5 or newer; `terraform init`
211-
downloads the google and random providers, so network) and `kubectl` (for
212-
`kubectl kustomize`); `make test` wants `bats` plus `kubectl`, which
213-
`tests/kubernetes.bats` renders the manifest with. `tests/terraform.bats`
214-
reads the module's files as text and needs no terraform. Both also need a
215-
working Docker daemon with the compose plugin and `jq` on `PATH`, because
216-
`make lint` renders `compose/compose.yaml` and validates the Vector config
217-
with the Vector image (`make vector-validate`), and
218-
`tests/inline-configs.bats` diffs the rendered inline configs against the
219-
copies under `compose/config/`.
220-
`tests/vector-rows.bats` replays the fixture log lines in
221-
`tests/fixtures/vector/` through the shipped Vector config with that image,
222-
`compose/scripts/dev/vector-testconfig.py` swapping only the source for stdin
223-
and the sink for a JSON console, and asserts the `sandbox_logs` row each
224-
becomes or that it is dropped; it needs Docker, python3 and `jq` as well.
113+
What each target needs, and when the stack images have to be rebuilt, is in
114+
[Developing](docs/REFERENCE.md#developing).
225115

226-
After editing `compose/config/vector/vector.toml` or
227-
`compose/config/clickhouse/config.xml`, run `make sync-configs` (python3): it
228-
rewrites the inline copies in `compose/compose.yaml` and the
229-
`VECTOR_CONFIG_SHA256` and `CLICKHOUSE_CONFIG_SHA256` stamps that make Compose
230-
recreate the container on a config change, which it does not do for inline
231-
`configs:` content on its own. `tests/config-hashes.bats` fails until the
232-
stamps match.
233-
234-
`make stores-check` runs what CI's stores job runs: both stores, their
235-
migrators, the seed, and the assertions that the seed wrote the team API key
236-
file, that the seeded row hashes that key, and that both migration ledgers
237-
landed. It wants Docker, network access and the images from `make images`,
238-
takes about a minute and needs no KVM, and it deliberately leaves the
239-
containers up so a failure can be inspected, so finish with
240-
`docker compose --project-directory compose down -v`.
241-
242-
The scripts under `compose/scripts/` and `compose/scripts/node/` travel inside
243-
the two stack images that carry them, and the compose file mounts nothing from
244-
the repository, so after editing either directory run `make images` before the
245-
next start, or the stack silently keeps running the old code. Without `make`,
246-
this is the `images` target (its `$$` becomes a single `$` outside of make):
247-
248-
```bash
249-
set -a; . ./compose/.env; set +a
250-
docker buildx bake --load \
251-
--set "*.platform=linux/$(docker version -f '{{.Server.Arch}}')" \
252-
--set "seed.context=https://github.com/e2b-dev/runtime.git#${RUNTIME_COMMIT}" \
253-
--set seed.args.SRC=packages \
254-
--set "tools.tags=${E2B_TOOLS_IMAGE}" \
255-
--set "node-e2b.tags=${E2B_NODE_E2B_IMAGE}" \
256-
--set "seed.tags=${E2B_SEED_IMAGE}"
257-
```
258-
259-
Binary checksums live in the tools image's `compose/scripts/fetch-artifacts.sh`,
260-
so bumping a Firecracker artifact means new stack images, not an edit on a
261-
machine.
262-
263-
### Not supported
116+
## Reference
264117

265-
- macOS and Windows as the host. The stack needs a Linux machine with KVM
266-
and a 4 KiB-page kernel, x86-64 or arm64; on Apple silicon that is a Linux
267-
VM with nested virtualization (M3 or newer, macOS 15 or newer). arm64 is
268-
verified end to end on a bare-metal arm64 host (2026-09-14, kernel 7.0);
269-
it needs kernel 6.10 or newer, which the Compose guide's Requirements
270-
explain. The seven pinned images are published for both architectures,
271-
and `fetch-artifacts` verifies the arm64 orchestrator and envd against the
272-
`.sha256` their release writes beside the object. A pin with no such
273-
object still stops with a `FIX:` line naming it.
274-
- Container-Optimized OS: the machine needs apt, a writable `/etc` and
275-
glibc 2.34 or newer.
276-
- No dashboard. Only the SDK and API paths are covered.
277-
- No wildcard DNS and no TLS.
118+
[`docs/REFERENCE.md`](docs/REFERENCE.md) has the rest: what runs where and in
119+
which order, how logs reach ClickHouse, the secrets each shape holds, how
120+
images and pins are released, building templates, and the developer tooling.

embed/compose/README.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ install does not ship one. It cannot override a key already set in the api
178178

179179
### What runs where
180180

181-
The hub's [What runs where](../README.md#what-runs-where) has the services.
181+
The reference's [What runs where](../docs/REFERENCE.md#what-runs-where) has the services.
182182
This is what they do to the host, which is why it should be a dedicated host
183183
or VM: on every `up`, `host-setup`
184184

@@ -282,8 +282,8 @@ The api's own two secrets, `ADMIN_TOKEN` and
282282
`seed-state` volume as `/run/e2b/api.env`, a file only root can read, as
283283
`GEN_ADMIN_TOKEN` and `GEN_SANDBOX_ACCESS_TOKEN_HASH_SEED`; a later run finds
284284
them again and the api's entrypoint reads them; nothing prints them, and
285-
`down -v` drops them with the databases. The hub's
286-
[Secrets](../README.md#secrets) says what the two are for.
285+
`down -v` drops them with the databases. The reference's
286+
[Secrets](../docs/REFERENCE.md#secrets) says what the two are for.
287287

288288
To choose your own instead, put them in `.env` before the first `up`. A value
289289
there wins over the generated one, one variable at a time, and the install is
@@ -429,7 +429,7 @@ Changing an E2B component pin means editing `.env` and, for a binary, the
429429
checksums in [`scripts/fetch-artifacts.sh`](scripts/fetch-artifacts.sh); then
430430
`make images` builds the three stack images locally under their pinned tags,
431431
and `docker compose up -d --wait` **without** `--pull always` uses the images
432-
just built rather than the published ones. The hub's
433-
[Developing](../README.md#developing) covers `make lint`, `make test`,
432+
just built rather than the published ones. The reference's
433+
[Developing](../docs/REFERENCE.md#developing) covers `make lint`, `make test`,
434434
`make sync-configs` and `make stores-check`, all of which run from the package
435435
root.

0 commit comments

Comments
 (0)