Durable Object Container API

Last updated Sep 30, 2026 Each container is managed and proxied by a Durable Object. The Durable Object manages routing and persistent state. The container process runs your image inside a Linux VM.

The API documented on this page is available on this.ctx.container inside any Durable Object class that has a container binding. Use it for direct control over the container process.

Note

We recommend starting new applications with the Durable Object Container API. It lets you run container workloads alongside the Durable Object's persistent storage, alarms, and request handling. Use ctx.container to control when the container starts, receives traffic, and stops, while the Durable Object manages application state and coordination. For existing applications that use the Container class, refer to Migrate to the Durable Object Container API.

Your Durable Object also has access to SQLite storage through this.ctx.storage, alarms, and all other Durable Object APIs. Use storage to preserve configuration and state across container restarts. Use alarms to schedule work without keeping the container running.

index.jsjs

import { DurableObject } from "cloudflare:workers";

export class MyDurableObject extends DurableObject {
    async start() {
        const container = this.ctx.container;
        if (!container) {
            throw new Error("No container is configured for this Durable Object");
        }
        if (!container.running) {
            // With the `default` scheduling policy, call `container.start()` without options.
            container.start({
                image: container.images.base,
                enableInternet: false,
            });
            await this.ctx.storage.put("lastStartedAt", Date.now());
        }
    }

async scheduleStart(timestamp) {
        await this.ctx.storage.setAlarm(timestamp);
    }

async alarm() {
        await this.start();
    }
}

index.tsts

import { DurableObject } from "cloudflare:workers";

interface Env {}

export class MyDurableObject extends DurableObject<Env> {
    async start(): Promise<void> {
        const container = this.ctx.container;
        if (!container) {
            throw new Error("No container is configured for this Durable Object");
        }
        if (!container.running) {
            // With the `default` scheduling policy, call `container.start()` without options.
            container.start({
                image: container.images.base,
                enableInternet: false,
            });
            await this.ctx.storage.put("lastStartedAt", Date.now());
        }
    }

async scheduleStart(timestamp: number): Promise<void> {
        await this.ctx.storage.setAlarm(timestamp);
    }

async alarm(): Promise<void> {
        await this.start();
    }
}

Attributes

images durable_object policy only

images is a read-only map generated from the images field in the application's Wrangler configuration. Wrangler builds or resolves each named image and exposes its digest-pinned reference under the same key. This attribute is available only for applications that use the durable_object scheduling policy.

const image = this.ctx.container.images.base;

Pass a value from this map as the image option to start().

running

running is true when the container is running. It does not confirm that the container is ready to accept requests.

this.ctx.container.running;

Methods

start

start() boots a container. It returns before the container is ready to accept requests. Confirm readiness before sending traffic.

start() returns once it validates its options. To catch later errors, including a container that fails to start, use monitor().

The required options depend on the application's scheduling policy.

// `default` scheduling policy:
// The image and instance size come from Wrangler configuration.
this.ctx.container.start();

// `durable_object` scheduling policy:
// Pass an image and choose whether to allow outbound Internet access.
// The instance size is optional and defaults to "lite".
this.ctx.container.start({
    image: this.ctx.container.images.base,
    enableInternet: true,
});

Parameters

  • options ( object, conditionally required): Startup options. Required for applications that use the durable_object scheduling policy and optional for applications that use the default scheduling policy.
  • env ( Record<string, string>, optional): Environment variables to pass to the container. Processes started with exec() do not receive these variables, except PATH.
  • entrypoint ( string[], optional): Command and arguments to run in the container.
  • enableInternet ( boolean, required): Whether to allow outbound Internet access. Required when you pass options.
  • image ( string, conditionally required) durable_object policy only: Image reference to start. Required unless you pass containerSnapshot. Pass the cloudflare/debian-trixie managed image or a value from ctx.container.images.
  • instance ( string | object, optional) durable_object policy only: Instance size. Pass "lite", "standard-1", "standard-2", "standard-3", "standard-4", or a custom object with vcpu, memoryMib, and diskMb properties. memoryMib is in mebibytes and diskMb is in megabytes. Both must be positive integers. Defaults to "lite".
  • containerSnapshot ( ContainerSnapshotRestoreParams, optional) durable_object policy only: Snapshot handle to restore before startup. Pass the ContainerSnapshot returned by snapshotContainer(), or an object containing its id. You cannot pass both containerSnapshot and image.
  • labels ( Record<string, string>, optional): Up to 10 labels that inspect() returns. Label names must contain 1 to 16 bytes. Label values can contain up to 64 bytes. Names and values cannot contain control characters.

monitor() rejects when a container that uses the durable_object scheduling policy starts without image or containerSnapshot.

Return values

  • void: No return value.

Exceptions

  • start() throws an Error when the container is already running.
  • start() throws a TypeError when both image and containerSnapshot are set, when either is empty, or when instance names an unknown type.
  • start() throws a RangeError when a custom instance value is missing or invalid.
  • start() throws an Error when labels exceed the label limits, when an environment variable name contains =, or when an environment variable name or value contains a null character.

inspect

inspect() returns the image and labels for a running container. It returns null when no container is running.

const containerInfo = await this.ctx.container.inspect();

Parameters

  • None.

Return values

  • Promise<ContainerInfo | null>: Resolves with null when no container is running, including after a failed start or after destroy(). Otherwise, it resolves with a ContainerInfo object containing:
  • image ( string): Image reference passed to start(). The value is an empty string while the container is starting and for a container restored from containerSnapshot.
  • labels ( Record<string, string>): Labels passed to start().

exec

exec() starts another process inside an already-running container. It does not start a stopped container. It waits for a container that is still starting.

exec(
  cmd: string[],
  options?: ContainerExecOptions,
): Promise<ExecProcess>

exec() starts the executable directly with the provided arguments. It does not start a shell or interpret pipes, redirects, expansion, or other shell syntax. Invoke Bash explicitly with ["bash", "-lc", "<COMMAND>"] when Bash exists in the image. Use ["sh", "-c", "<COMMAND>"] for images with only a Portable Operating System Interface (POSIX) shell.

The following RPC method checks that the container is running before executing a command:

import { DurableObject } from "cloudflare:workers";

export class MyDurableObject extends DurableObject {
    async runCommand() {
        const container = this.ctx.container;
        if (!container) {
            throw new Error("No container is configured for this Durable Object");
        }
        if (!container.running) {
            throw new Error("Container is not running");
        }

const process = await container.exec(["node", "--version"]);
        const output = await process.output();

return {
            pid: process.pid,
            exitCode: output.exitCode,
            stdout: new TextDecoder().decode(output.stdout),
        };
    }
}
import { DurableObject } from "cloudflare:workers";

interface Env {}

export class MyDurableObject extends DurableObject<Env> {
    async runCommand() {
        const container = this.ctx.container;
        if (!container) {
            throw new Error("No container is configured for this Durable Object");
        }
        if (!container.running) {
            throw new Error("Container is not running");
        }

const process = await container.exec(["node", "--version"]);
        const output = await process.output();

return {
            pid: process.pid,
            exitCode: output.exitCode,
            stdout: new TextDecoder().decode(output.stdout),
        };
    }
}

Parameters

  • cmd ( string[]): Executable followed by its arguments.
  • options ( ContainerExecOptions, optional): Process configuration:
  • stdin ( ReadableStream | "pipe", optional): Source for standard input. Use "pipe" to write through the returned stdin stream. When omitted, standard input closes and sends end-of-file (EOF).
  • stdout ( "pipe" | "ignore", optional, default "pipe"): Captures or discards standard output.
  • stderr ( "pipe" | "ignore" | "combined", optional, default "pipe"): Captures, discards, or merges standard error into standard output. The "combined" value requires stdout: "pipe". Combined output does not guarantee ordering between its source streams.
  • cwd ( string, optional): Working directory for the process.
  • env ( Record<string, string>, optional): Environment variables for the process. Other variables from start() or the image are not inherited, except for PATH. Unless overridden here, PATH uses the container's startup value, including any override passed to start().
  • user ( string, optional): Numeric Linux user and group IDs for the process, as uid:gid. When omitted, the process runs as the image user.
  • signal ( AbortSignal, optional): Aborting the signal sends SIGKILL to the process. An already-aborted signal makes exec() throw an AbortError.
  • pty ( boolean | { cols?: number; rows?: number }, optional): Runs the process attached to a pseudo-terminal. true uses 80 columns and 24 rows.

The following call runs a command as user 1000 and group 1000. The file it creates belongs to 1000:1000:

const process = await this.ctx.container.exec(["touch", "/tmp/notes.txt"], {
    user: "1000:1000",
});

Caution

Pass both IDs in user. A user ID without a group ID runs the process as root, and a user or group name makes exec() reject with an internal error.

user does not restrict a process in a deployed container with the durable_object scheduling policy. Every process has the same Linux capabilities as root, regardless of user. File permissions do not stop the process, and it can switch back to root. The IDs set the owner of files that the process creates. In containers with the default scheduling policy, other users have no capabilities, and file permissions apply.

With pty, stdin defaults to "pipe", and stderr defaults to "combined" and cannot take another value. Standard output and standard error arrive together on stdout. The terminal converts line endings to \r\n.

Return values