Programmable provisioning for agent sandboxes

Cloudflare Containers has been rearchitected around the way agents actually consume compute: sandboxes are created on demand per task, expected to be ready immediately, and must be able to pause and resume. Your code can now select each sandbox's image and instance type at runtime, Containers start 6x faster, and filesystem snapshots are available in public beta.

The foundation is unchanged: every Container has always been paired with its own Durable Object, a persistent, programmable controller running alongside it that manages lifecycle and outbound traffic. What is new is that more of that capability is exposed directly on the native ctx.container API, so the Durable Object can drive its Container without an intermediate wrapper class. The same model carries into Sandbox SDK 1.0.

Previously, Containers was organized around application deployments: image and compute resources were fixed at deploy time, rolled out across the application, and managed centrally. Agent environments instead derive their image, resources, tools, and starting filesystem from the task at hand, and may exist for minutes, sleep between requests, or be restored days later. Cloudflare points to Base44 on app-building workspaces and Kilo Code on cloud-agent sessions, along with integrations for Cursor Cloud Agents, Devin Outposts, the OpenAI Agents API, and Claude Managed Agents. Coding agents need repositories, compilers, test runners, and dev servers; evals need sandboxes starting from a known state; reinforcement learning systems create, grade, and reset environments in bulk; long-running tasks need produced files preserved.

The durable_object scheduling policy

Under the previous model, image and instance type were the two decisions that mattered most for a sandbox, and both were locked at deploy time. Each image/instance-type combination was its own Containers application with its own Durable Object namespace, provisioned ahead of time with wrangler deploy. A small Node.js sandbox and a large Python sandbox for builds meant two applications, two namespaces, and routing logic in the Worker; every new environment meant another deployment.

The durable_object scheduling policy turns those two decisions into arguments your code passes when a sandbox starts. Opt in by setting the scheduling policy and declaring the images the Durable Object may choose from:

// wrangler.jsonc
{
  "containers": [
    {
      "class_name": "AgentSandbox",
      "scheduling_policy": "durable_object",

      "images": {
        "node": { "dockerfile": "./images/node/Dockerfile" },
        "python": { "dockerfile": "./images/python/Dockerfile" }
      }

    }
  ],
  "durable_objects": {
    "bindings": [{ "name": "SANDBOX", "class_name": "AgentSandbox" }]
  }
}

Each declared image is addressable as this.ctx.container.images.<name> inside the Durable Object. When a task arrives, the code picks the image and instance type:

import { DurableObject } from "cloudflare:workers";

export class AgentSandbox extends DurableObject {
  async startWorkspace(workspace) {
    if (this.ctx.container.running) {
      return;
    }

    const image =
      workspace.toolchain === "python"
        ? this.ctx.container.images.python	
        : this.ctx.container.images.node;

    const instance =
      workspace.workload === "build"
        ? "standard-2"
        : "standard-1";

    this.ctx.container.start({
      image,
      instance,
      enableInternet: true,
    });
  }
}

One Durable Object class can therefore run Node.js and Python sandboxes of different sizes side by side, and adding an environment is a code change rather than a deployment.

Release strategy moves into application code

Runtime image selection also removes rollout machinery. Updating an image previously meant updating the whole application: grace periods so running instances could drain, percentage splits to shift traffic gradually, and an API call to push the new configuration, with the platform deciding which instances were replaced and when. With durable_object, there is no rollout configuration — a Container keeps running the image it started with until your code stops it, and the next start uses whatever image the code chooses. That reduces rollout strategy to a few lines:

const image =
  (await this.ctx.storage.get("pinned-image")) ??
  (isCanary(this.ctx.id)
    ? this.ctx.container.images.nodeV2
    : this.ctx.container.images.node);

Possible approaches include canarying a new toolchain on 5% of new sandboxes by hashing the Durable Object ID; pinning active projects to their current image so an environment is never swapped mid-task; migrating a workspace at a natural checkpoint such as the next session or after a snapshot; and rolling back by changing which image future starts select, with no config push and no drain wait.

Faster time to first command

Starting a Container once required the global control plane to resolve application configuration, find capacity, and coordinate placement — machinery that sat in the path of an agent's first command. Under durable_object, demand originates at the Durable Object, and the Containers infrastructure serving it looks for capacity on the same machine first, widening the search within the same location as needed. Hosts that already hold the Container's image or snapshot in local storage are favored, avoiding a download before start.

Post-placement work was cut as well. Rather than booting a virtual machine from scratch, the new runtime restores a prepared, unassigned virtual machine, reuses networking and filesystem setup, batches repeated operations, and no longer waits on services the first command doesn't need.

On ComputeSDK's independent Burst TTI Benchmark, which launches 100 sandboxes concurrently and measures time-to-interactive from the client:

Startup measurement

Previous scheduling path

New scheduling policy

Improvement

Median

4.049 seconds

648 milliseconds

6.2x faster

95th percentile

5.839 seconds

910 milliseconds

6.4x faster

99th percentile

6.717 seconds

1129 milliseconds

5.9x faster

Under burst load, a preliminary test started 100,000 Containers from a single account in 5.387 seconds across six locations. Median startup on ComputeSDK's benchmark fell from just over four seconds to 648 milliseconds.

A prebuilt system image

As the scheduling path gets faster, image preparation becomes a larger share of the remaining wait: the image must be on the host and unpacked into a filesystem, and doing that after the request arrives leaves the agent waiting. cloudflare/debian-trixie addresses this — a ready-to-use system image for agents that configure their environment at runtime, built on Debian Trixie Slim with Node.js 24.20.0 LTS:

this.ctx.container.start({
  image: "cloudflare/debian-trixie",
  instance: "standard-2",
  enableInternet: true,
  entrypoint: ["/bin/sleep", "infinity"]
});

No Dockerfile, image build, or push to Cloudflare is needed to get a Linux sandbox; once running, the agent can use exec() to clone a repository, install packages, and configure its environment. Because Cloudflare controls the image, it can be distributed and prepared across eligible Containers hosts before requests arrive, so the base image is not downloaded or unpacked while the user waits.

Filesystem snapshots in public beta

Fast startup still leaves workspace setup — cloning a repository, installing dependencies, configuring a toolchain — which can take far longer than starting the Container, plus the files the agent produces along the way. Repeating setup on every start wastes time, and losing the agent's changes makes a task hard to continue. Native filesystem snapshots, now in public beta, let an agent begin in a prepared environment, save its workspace when a task pauses, and restore those files on resume.

async saveWorkspace() {
  const snapshot = await this.ctx.container.snapshotContainer({
    name: "project-ready",
  });

  await this.ctx.storage.put("workspace-snapshot", snapshot);
}

async restoreWorkspace() {
  const snapshot = await this.ctx.storage.get("workspace-snapshot");

  if (!snapshot) {
    throw new Error("No workspace snapshot found");
  }

  this.ctx.container.start({
    containerSnapshot: snapshot,
    instance: "standard-2",
    enableInternet: true,
  });
}

Two patterns follow. One workspace can continue across many sessions: a coding agent might save the workspace when the user stops working and restore it the next day, with the repository, installed dependencies, build caches, configuration, and edits intact and no rebuild. Alternatively, a snapshot can serve as a shared checkpoint, since snapshots are immutable and reusable — multiple Containers can start independently from the same prepared environment and diverge from there. Agent evaluations are the clearest case: running one task across different system prompts, skills, models, or agent versions requires the repository, dependencies, tools, and input files to stay fixed, and a single snapshot starts many isolated environments from that baseline, cutting setup time and preventing environment drift from skewing results.

Snapshots also pair with cloudflare/debian-trixie: an agent can start from that image, configure its environment with exec(), and save the result as a snapshot, so later sandboxes begin with the repository, dependencies, and toolchain already in place. Because snapshots are available through the durable_object scheduling policy, Containers can act as persistent agent workspaces — compute stops when work pauses, and a new Container starts from the latest snapshot to continue.

Putting the Durable Object in charge

The faster scheduling path, runtime image selection, and filesystem snapshots all trace back to one design choice: leaning further into the Durable Object as the controller for its attached Container.

Agent systems need a programmable, stateful environment outside the Container to hold state, keep credentials, and manage the sandbox lifecycle. Following Anthropic's "decoupling the brain from the hands" pattern, the agent can live separately from the sandbox where it works, staying available while sandboxes and tools start, stop, fail, or get replaced on their own. Alternatively, when the agent runs inside the sandbox, that outside environment supervises it and reports progress back to the user.

Every Container is attached to a stateful Durable Object with its own compute and storage running alongside it. That means the agent can run in the Durable Object and treat the Container as its workspace, or run in the Container while the Durable Object supervises. Add Dynamic Workers for lightweight isolated execution, and an application can pick the execution environment each task needs.

The durable_object scheduling policy removes the wrapper class and lets the Durable Object control its Container directly. exec() runs in the Workers runtime, and outbound request interception, runtime image and instance selection, and filesystem snapshots are all reachable through ctx.container, combinable with Durable Object storage, alarms, WebSockets, RPC, and the rest of your code.

The Container becomes a compute extension of its Durable Object: the Container supplies the Linux environment, while the Durable Object retains the sandbox's identity, state, policy, and lifecycle.

Patterns this split enables

image2.png

Keeping the agent up while its workspace sleeps. The agent loop runs in the Durable Object, holding session state, talking to the user over WebSockets, and calling models. It wakes the Container for a shell, compiler, or dev server, then shuts that compute down while waiting on the user or model — no idle Linux compute to pay for.

Programming the security boundary. The Durable Object can track which services, repositories, and operations a user has authorized and then update the Container's Outbound Request Handler to inject newly granted credentials, enforce new policies, or log more activity — the Gatekeeper pattern from Cloudflare OS, applied per agent computer.

Supervising evals and RL attempts from outside. A coordinator snapshots a base workspace and forks it into N attempts, each with its own Durable Object and Container. Each Durable Object runs its attempt, monitors it, and preserves the result even if the Container crashes. The coordinator grades attempts, snapshots the best, and forks again from there.

No single generic lifecycle covers these patterns. Native APIs let you mix the Container with the Durable Object primitives your application needs, while keeping higher-level utilities where they help, and retaining control of lifecycle, policy, and state.

What changes for the Container class and Sandbox SDK

At launch, Containers hid the Durable Object behind the Container class so sandboxes felt familiar. The Sandbox SDK was built on that class and filled real gaps — no native command execution, outbound request interception, or snapshots existed then, so they were built in userspace.

Agent workspaces have since become a primary workload, and the abstraction now has a visible cost: hiding the Durable Object made it harder to see and combine its identity, state, and coordination with the Container it controls. Nearly every team needed something different from the generic lifecycle — their own sleep policy, credential handling, or eval-run tracking.

With these capabilities now native, the Durable Object becomes explicit:

  • New capabilities are native-only: the durable_object scheduling policy, faster startup, runtime image and instance selection, and filesystem snapshots are available only through ctx.container.
  • The Container class and legacy Sandbox class are maintained through December 31, 2026. Existing deployments keep running afterward but receive no updates. Migration to ctx.container is recommended.
  • Sandbox SDK 1.0 is a set of utilities rather than a base class; its helpers work inside your own Durable Object class alongside ctx.container.
  • For a higher-level environment, @cloudflare/computer combines Dynamic Workers and Containers with a synchronized filesystem.

Migration typically means switching extends Container to extends DurableObject and calling this.ctx.container directly. The migration guide covers the details.

Most agents today are judged by what they finish in one session. As they take on projects spanning hours, days, and weeks, their environments have to keep pace. The goal is for every agent to spin up a sandbox for the task at hand, release compute when work pauses, and return to the same workspace when the project resumes. The durable_object scheduling policy is available to all today in public beta.

Acknowledgements: This project was also made possible by the contributions of Greg Anders, Andrew Martinez, Nafeez Nazer, Kian Newman-Hazel, Sebastien Pahl, Naresh Ramesh, Cody Roseborough, Nikita Sharma, and Sarah Snell.