Container lifecycle
A Container runs only when you start it. You start it, stop it, and restore it from the Durable Object that owns it.
The examples use this container:
export class Sandbox extends Cloudflare.Container<Sandbox>()("Sandbox", { schedulingPolicy: "durable_object", images: { node: { image: "node:24-slim" } },}) {}Bind the container
Section titled “Bind the container”yield* Sandbox in a Durable Object’s Construction phase gives you
the container’s handle:
export default class Workspace extends Cloudflare.DurableObject<Workspace>()( "Workspace", Effect.gen(function* () { const sandbox = yield* Sandbox;
return Effect.gen(function* () { return {}; }); }),) {}Binding does not start the container.
Start it before you use it
Section titled “Start it before you use it”Call start at the top of each operation that needs the container:
const start = sandbox.start({ entrypoint: ["sleep", "infinity"], enableInternet: false,});
return { run: (cmd: string[]) => Effect.gen(function* () { yield* start; // the container is running }),};start does nothing when the container is already running, and
concurrent calls start it once. A container that stopped or crashed
since the last request starts again on the next one.
The options only apply when start actually starts the container.
To run with different options, stop the container first.
Choose an image per start
Section titled “Choose an image per start”A container with schedulingPolicy: "durable_object" picks its image
and size when it starts:
const images = yield* sandbox.images;yield* sandbox.start({ image: images.node, instance: "standard-1", entrypoint: ["sleep", "infinity"], enableInternet: false,});images holds one reference per image you declared, typed by name.
Start it from a Layer
Section titled “Start it from a Layer”A Layer that depends on the container starts it the same way:
export const ShellLive = Layer.effect( Shell, Effect.gen(function* () { const sandbox = yield* Sandbox; const start = sandbox.start({ enableInternet: false });
return { run: (cmd: string[]) => start.pipe( Effect.andThen(sandbox.exec(cmd)), Effect.flatMap((child) => child.output()), Effect.scoped, ), }; }),);Every Layer in the Durable Object shares one container, so a Layer never stops it.
Wait for a port
Section titled “Wait for a port”getTcpPort waits until the server inside the container accepts
connections:
const { fetch } = yield* sandbox.getTcpPort(8080);
const health = Effect.gen(function* () { yield* start; const response = yield* fetch(HttpClientRequest.get("http://container/health")); return yield* response.text;});A port never starts the container. If the container is stopped, the
request fails with ContainerNotRunningError.
Run a command
Section titled “Run a command”exec runs a command in a started container:
const output = yield* Effect.scoped( Effect.gen(function* () { const child = yield* sandbox.exec(["npm", "test"]); return yield* child.output(); }),);exec runs the command without a shell. Pass ["sh", "-c", command]
when you need one. Closing the scope kills a process that is still
running.
Check whether it is running
Section titled “Check whether it is running”if (yield* sandbox.running) { // the process is up; its ports may not be ready yet}Stop it
Section titled “Stop it”destroy stops the container immediately:
yield* sandbox.destroy();signal sends a signal to its main process instead, so it can shut
down on its own:
yield* sandbox.signal(15); // SIGTERMLet it sleep
Section titled “Let it sleep”When nothing calls the Durable Object, it goes idle and its container
stops. Change how long it waits with setInactivityTimeout:
yield* sandbox.setInactivityTimeout(10 * 60 * 1000); // 10 minutesThe next operation that calls start starts the container again.
Watch for a crash
Section titled “Watch for a crash”monitor waits until the container exits:
yield* sandbox.monitor().pipe( Effect.catchTag("ContainerError", (error) => Effect.logError(`container exited: ${error.message}`), ),);A crash fails with ContainerError; a clean exit succeeds.
Save a snapshot
Section titled “Save a snapshot”snapshotContainer saves the container’s filesystem:
const snapshot = yield* sandbox.snapshotContainer({ name: "workspace" });yield* state.storage.put("snapshot", snapshot);Store the snapshot in the Durable Object’s storage so you can restore it after the container stops. A snapshot holds the writable root filesystem: files, installed packages, build caches. It does not hold memory, running processes, or mounted filesystems.
Snapshots need schedulingPolicy: "durable_object".
Restore a snapshot
Section titled “Restore a snapshot”Pass the snapshot to start to begin from that filesystem:
const snapshot = yield* state.storage.get<Cloudflare.Containers.ContainerSnapshot>("snapshot");
yield* sandbox.start({ ...(snapshot ? { containerSnapshot: snapshot } : { image: images.node }), entrypoint: ["sleep", "infinity"], enableInternet: false,});The entrypoint starts fresh. A snapshot can start any number of containers, and expires after 30 days without a restore.
Snapshot before you stop
Section titled “Snapshot before you stop”Save the filesystem, then stop the container, so the next start picks up where this one left off:
const suspend = Effect.gen(function* () { const snapshot = yield* sandbox.snapshotContainer({ name: "workspace" }); yield* state.storage.put("snapshot", snapshot); yield* sandbox.destroy();});Where next
Section titled “Where next”- Containers — declare a container and its image.
- Run a Container — build a shell-running container end to end.
- Durable Objects — the object that owns each container.
- Container API reference