Skip to content

E2B.Template reference

Source: packages/e2b/src/CreateTemplate.ts

Build E2B templates at runtime from E2B’s own build inputs: a template per repository, per customer image, per agent configuration. E2B builds remotely, so this works from a Worker.

Templates built at runtime carry no metadata, so they are not deleted when the host is destroyed. Delete them with delete when their owner goes away.

const templates = yield* E2B.CreateTemplate();
const sandboxes = yield* E2B.CreateSandbox();
// in fetch:
const template = yield* templates.getOrBuild({
name: `repo-${slug}`,
image: "node:22-bookworm",
files: [{ path: "/home/user/app/package.json", contents: packageJson }],
run: ["cd /home/user/app && npm install"],
});
const sandbox = yield* sandboxes.create({ templateID: template.templateId });

Source: packages/e2b/src/CreateTemplateHttp.ts Kind: Layer · Provides: E2B.CreateTemplate

HTTP implementation of CreateTemplate. Works on any host.

Effect.gen(function* () {
const templates = yield* E2B.CreateTemplate();
// ...
}).pipe(Effect.provide(E2B.CreateTemplateHttp))

Source: packages/e2b/src/Template.ts

An E2B template: the image every sandbox boots from, built by E2B into a Firecracker snapshot.

Build from a registry image, a dockerfile (a path or inline content), or another template (fromTemplate). E2B builds remotely, so a deploy needs no Docker daemon: Alchemy plans the Dockerfile with @alchemy.run/docker’s build plan, lowers it to E2B’s build steps, and uploads only the COPY sources E2B has not seen, by content hash. A deploy starts a new build only when the inputs change.

E2B’s builder runs one stage for linux/amd64. A Dockerfile with several FROMs, COPY --from, ADD <url>, heredocs, RUN --mount or SHELL fails at plan time with UnsupportedDockerfile, listing the lines. EXPOSE, LABEL, VOLUME, HEALTHCHECK and STOPSIGNAL are ignored, as E2B ignores them. An ARG becomes an environment variable. CMD / ENTRYPOINT become the template’s start command: it is captured running in the snapshot, so every sandbox starts with it already up.

E2B templates carry no labels, so Alchemy recognises its templates by their generated name.

From a registry image

export const Python = E2B.Template("Python", { image: "python:3.12-slim" });

From an inline Dockerfile

import * as Dockerfile from "@alchemy.run/docker/Dockerfile";
export const Agent = E2B.Template("Agent", {
dockerfile: Dockerfile.inline`
FROM node:22-bookworm
RUN apt-get update && apt-get install -y git ripgrep
WORKDIR /work
`,
cpuCount: 2,
memoryMB: 4096,
});

From a Dockerfile and its build context

export const Repo = E2B.Template("Repo", {
context: "./sandbox",
dockerfile: "./sandbox/Dockerfile",
build: { args: { NODE_VERSION: "22" } },
});

Template: Starting a process in every sandbox

Section titled “Template: Starting a process in every sandbox”
export const Preview = E2B.Template("Preview", {
image: "node:22-bookworm",
run: ["npm create vite@latest app -- --template react", "cd app && npm install"],
startCmd: "cd /home/user/app && npm run dev -- --host 0.0.0.0 --port 3000",
readyCmd: "curl -sf http://localhost:3000",
});
export const Private = E2B.Template("Private", {
image: "ghcr.io/my-org/base:1",
registry: { username: "my-org", password: Alchemy.Secret("GHCR_TOKEN") },
});

Give the template a main and an Effect, and every sandbox from it boots with the program already serving HTTP on port. Alchemy bundles main, copies it into the template, and E2B snapshots the template once the program answers. The program runs with Bun; the image defaults to oven/bun:1.

A program in every sandbox

export default class Program extends E2B.Template<Program>()(
"Program",
{ main: import.meta.url, port: 3000 },
Effect.gen(function* () {
return {
fetch: Effect.succeed(HttpServerResponse.text("hello from a sandbox")),
};
}),
) {}

Call the program in a sandbox

const sandboxes = yield* E2B.CreateSandbox(Program);
const sandbox = yield* sandboxes.create({ timeout: 300 });
const port = yield* sandbox.getTcpPort(3000);
const res = yield* port.fetch(HttpClientRequest.get("/"));

A main without an Effect is bundled and run with Bun as-is. It must listen on PORT. E2B.InferEnv types its env.

export const Server = E2B.Template("Server", {
main: "./src/server.mjs",
env: { GREETING: "hi" },
});
export type ServerEnv = E2B.InferEnv<typeof Server>;

Bindings work inside a template program like in any other host.

export default class Program extends E2B.Template<Program>()(
"Program",
{ main: import.meta.url },
Effect.gen(function* () {
const reports = yield* E2B.ReadVolume(Reports);
return {
fetch: Effect.gen(function* () {
return HttpServerResponse.text(yield* reports.readText("/latest.json"));
}).pipe(Effect.orDie),
};
}).pipe(Effect.provide(E2B.ReadVolumeHttp)),
) {}

Size, image and port

export default class Program extends E2B.Template<Program>()(
"Program",
{ main: import.meta.url, image: "oven/bun:1-debian", cpuCount: 4, memoryMB: 8192, port: 8080 },
Effect.gen(function* () {
return { fetch: Effect.succeed(HttpServerResponse.text("ok")) };
}),
) {}

Sandboxes from the template, in a Worker

const sandboxes = yield* E2B.CreateSandbox(Agent);
const sandbox = yield* sandboxes.create({ timeout: 600 });

Template: Referencing a template the stack doesn’t manage

Section titled “Template: Referencing a template the stack doesn’t manage”

E2B.Template.lookup finds an existing template by name. Nothing is created, adopted or deleted.

const base = E2B.Template.lookup({ name: "my-team/base-image" });
const sandboxes = yield* E2B.CreateSandbox(base);