A deep dive into Cloudflare Computer

By

How Cloudflare Computer combines a Durable Object filesystem, Dynamic Worker runtimes, a container backend, FUSE mount, and sync protocol.

~~~

Cloudflare just introduced @cloudflare/computer.

The name made me think of a virtual machine.

But Cloudflare Computer is not one machine running forever.

It is a durable filesystem inside a Durable Object, connected to several places where an AI agent can run code.

Small jobs can run in a fast isolate. JavaScript modules can run in a fresh Dynamic Worker. A command that needs Linux can run in a container.

All three see the same files.

This is the important idea.

Cloudflare is not trying to make containers start a little faster. It is trying to make the container optional for most of an agent’s work.

The project is moving quickly, so APIs and implementation details will change.

Cloudflare marks the package as an early preview. It is for experiments and prototypes, not production.

The repository uses the MIT license.

If the Cloudflare platform is new to you, start with my free Cloudflare course. It covers Workers, storage, Durable Objects, Queues, Workflows, and the other building blocks behind this project.

My free Cloudflare Workers course takes the next step and builds a complete Worker application.

The architecture in one picture

Here is the complete system:

flowchart TD
  A["AI agent"] --> T["File and execution tools"]
  T --> W["Workspace in a Durable Object"]
  W --> D["SQLite virtual filesystem"]
  W --> R{"Runtime router"}
  R --> S["Dynamic Worker with just-bash"]
  R --> J["Fresh Dynamic Worker with JavaScript"]
  R --> C["Linux container"]
  S --> D
  J --> D
  D --> P["Push changed chunks"]
  P --> F["computerd FUSE mount"]
  F --> C
  C --> L["Pull changed chunks"]
  L --> D

The Durable Object gives one agent a stable identity and private storage.

The SQLite database inside that object is the authoritative filesystem.

The runtime router chooses where each execution should happen.

The two Worker backends call the Durable Object filesystem directly. The container keeps a second copy behind a FUSE mount, so Cloudflare Computer synchronizes changes before and after each command.

The package currently ships three execution backends:

The announcement describes the Worker shell and container model. The repository already contains the third backend for running ECMAScript modules in a fresh Dynamic Worker.

This is a preview of a preview. We should look at the direction, not treat every API as final.

The computer starts with a filesystem

You can create a Workspace without any execution backend.

That gives the agent a durable working directory:

const workspace = new Workspace({
  storage: this.ctx.storage,
})

await workspace.fs.mkdir('/workspace', { recursive: true })
await workspace.fs.writeFile('/workspace/notes.md', '# Notes\n')

The API looks like node:fs/promises.

It includes operations such as readFile(), writeFile(), mkdir(), readdir(), rm(), and grep().

But these calls do not touch a normal disk.

They update SQLite tables inside the Durable Object.

How a filesystem fits inside SQLite

The filesystem implementation lives in the private @cloudflare/dofs package.

Its schema separates the filesystem into a few parts:

A file is not stored as one large SQLite value.

Cloudflare Computer splits it into fixed 512 KiB chunks. Each chunk gets a SHA-256 hash.

The file points to an ordered list of hashes:

/workspace/report.pdf
  chunk 0 → 9f2a...
  chunk 1 → 27bc...
  chunk 2 → c841...

This gives the sync protocol two useful properties.

First, identical chunks only need to be stored and transferred once.

Second, changing one part of a large file does not require sending the entire file again.

The same idea appears in content-addressed storage systems and Git. Names point to content, while hashes identify the bytes.

Every filesystem mutation also receives a monotonically increasing revision number.

Those revisions let the Workspace ask a backend for everything that changed after its last cursor.

A Durable Object is the owner

The choice of Durable Objects matters.

Cloudflare Computer needs more than storage. It needs one place that owns the agent’s state and coordinates execution.

The Durable Object provides both.

One object can represent one agent, user, project, or session. Requests for that identity reach the same logical object. The Workspace database survives when the object leaves memory and later starts again.

The package says a workspace can use roughly 10 GB, shared with the Durable Object’s other storage.

This is an agent workspace, not a replacement for a huge development disk.

One execution API, several backends

Every backend sits behind the same method:

const run = await workspace.runtime.exec('npm test', {
  backend: 'container',
  encoding: 'utf8',
})

const result = await run.result()

The execution handle is also a ReadableStream.

You can stream standard output and standard error while the command runs. You can also reconnect to supported executions by ID, stop them, or dispose of their stored records.

Backends connect lazily. Creating a Workspace does not start every runtime.

The first configured backend becomes the default, but callers can select one by ID.

The common method hides an important difference: source does not mean the same thing everywhere.

For the shell and container backends, it is a shell command.

For the JavaScript backend, it is an ECMAScript module.

The Worker shell runs without Linux

The lightest command backend uses just-bash inside a Dynamic Worker.

Dynamic Workers use the same isolate model as regular Cloudflare Workers. My workerd deep dive explains the open-source runtime that executes Worker code.

just-bash interprets shell commands in JavaScript. It provides common text and filesystem operations without starting a Linux machine.

The Dynamic Worker receives a narrow RPC connection back to the Workspace. A command such as this reads the authoritative SQLite filesystem directly:

grep -R "TODO" /workspace

There is no second filesystem and no synchronization step.

The Worker shell can include optional command groups such as curl, jq, python, sqlite, yq, and xan.

Unused groups can stay out of the bundle.

This backend is useful for file inspection, text processing, Git operations, and other jobs that do not need a real operating system.

It is not a Linux emulator.

Commands that need native binaries, package installation, a compiler, or a full process environment still belong in the container.

The JavaScript backend runs a module

The Worker JavaScript backend takes a different approach.

Each execution gets a fresh Dynamic Worker. Cloudflare Computer builds a module graph, validates imports, supplies structured input, and collects a structured result.

The module can use an asynchronous node:fs/promises subset:

import fs from 'node:fs/promises'

export default async function () {
  const input = await fs.readFile('/workspace/input.txt', 'utf8')
  await fs.writeFile('/workspace/output.txt', input.toUpperCase())

  return { bytes: input.length }
}

Those filesystem calls cross a capability bridge back to the Durable Object.

The Dynamic Worker does not receive the Durable Object storage binding, loader binding, credentials, or the complete Workspace object.

It gets only the capabilities the host installed.

Network access is disabled by default. Environment variables contain only values supplied for that execution. Source size, input, output, standard streams, CPU time, wall-clock time, and capability traffic all have configurable limits.

The runtime also reserves ws: modules for trusted host operations. The current implementation includes ws:git and ws:artifacts.

This is a strong design idea: code enters the isolate, but authority stays with the host.

The container is the heavy backend

Some work needs a real Linux environment.

For that, Cloudflare Computer starts a Cloudflare Container running a daemon named computerd.

computerd is packaged as a self-contained Node single executable application. The container image does not need its own Node.js installation.

The daemon has four main jobs:

FUSE lets a userspace program implement a filesystem.

To a compiler or package manager inside the container, /workspace/app.ts looks like a normal file. Behind the mount, computerd reads and writes the SQLite-backed virtual filesystem.

The container can contain anything the task needs: Node.js, Python, pandoc, FFmpeg, compilers, or native libraries.

This is where Cloudflare Computer pays the cost of a full Linux environment.

It pays that cost only when the task needs it.

For comparison, my Vercel Sandbox tutorial shows the more traditional approach: give untrusted code an isolated Linux microVM from the beginning.

The container has a second copy of the filesystem

The Worker backends call the Durable Object filesystem directly.

The container cannot do that for every kernel filesystem operation. It keeps its own virtual filesystem behind the FUSE mount.

This creates a synchronization problem.

Before a container command starts, Cloudflare Computer pushes Durable Object changes into the container.

After the command finishes, it pulls container changes back into the Durable Object.

The complete round trip looks like this:

Durable Object SQLite
        ↓ push changed paths and missing chunks
container VFS behind /workspace
        ↓ run command
container records new revisions
        ↓ pull changed paths and missing chunks
Durable Object SQLite

The protocol sends metadata first.

File entries contain chunk hashes, not inline bytes. The receiver checks which hashes it already owns. The sender transfers only the missing chunks.

Repeated writes to one path are coalesced. If the host rewrites a file five times between container commands, the container receives the latest state once.

Pulls run in batches of 256 entries. The Durable Object saves a cursor after each committed batch, so a crash does not restart a large pull from the beginning.

Deletes travel as tombstones.

Renames do not have a special wire operation. The protocol sends the final state: new paths plus tombstones for old paths.

This costs more for a large directory move, but it keeps replay and recovery idempotent.

What happens when both sides change a path?

The sync protocol converges with last-writer-wins behavior.

If an incoming path has a different node type, the receiver removes the local tree at that path and applies the incoming entry.

This is practical, but it is not collaborative editing or a distributed transaction.

The normal execution bracket avoids most conflicts:

  1. push host changes
  2. run one command
  3. pull container changes

Applications that write the same paths concurrently need to understand the boundary.

How the Durable Object reaches the container

The connection has an interesting shape.

computerd exposes a Cap’n Web RPC interface over WebSockets.

But the Durable Object does not keep a normal inbound connection to the container.

It asks computerd to open an outbound WebSocket. Cloudflare intercepts that container request and routes it back to the owning Durable Object.

The connection is inverted:

Durable Object → POST /connect → container
Durable Object ← intercepted WebSocket ← computerd

This gives both sides one bidirectional RPC session for filesystem synchronization and command execution.

If the WebSocket dies in the current implementation, the backend does not transparently splice in a replacement. The caller reconstructs the Workspace.

The agent tools are smaller than the computer

Cloudflare Computer includes tools for the Vercel AI SDK.

The default writable set is:

You can add exec and file publishing.

The file tools operate on the Workspace directly. They do not start a container.

For exec, you provide backend names and descriptions:

const tools = createAITools({
  workspace,
  shell: {
    defaultBackend: 'worker',
    backends: {
      worker: {
        description: 'Fast text and file commands',
      },
      container: {
        description: 'Full Linux with native binaries',
      },
    },
  },
})

The model can use those descriptions to choose a backend.

But routing is not authorization.

The repository documentation says a public gateway must validate which backend a capability may select. An application should not trust a model-supplied backend name at a security boundary.

Git does not need a shell

The Workspace also has an optional Git client built with isomorphic-git.

It works directly against the SQLite filesystem.

An agent can clone, inspect status, stage files, create commits, and work with branches without starting a container.

Networked Git operations are a separate capability. The JavaScript backend denies them by default, even though local Git operations remain available.

Again, the design separates code execution from authority.

How I would use Cloudflare Computer

I build many small products that are meant to be changed by coding agents.

Most of the work is not complicated. The agent reads the repository, searches for the relevant files, edits some code, runs a few checks, and gives me the result.

Cloudflare Computer fits this workflow well.

I would create one Workspace for each project or task. The Git client would clone the repository directly into the Durable Object filesystem. The Worker shell would handle searches, diffs, JSON, Markdown, and other small operations. The JavaScript backend could run project-specific scripts.

Only the final build or test suite would need a container.

Take Waiting Lists, for example. It is a self-hosted Cloudflare application distributed with source code and instructions for coding agents.

An agent could clone it, inspect the configuration, add a webhook, change the confirmation flow, or connect a new frontend form without keeping a Linux container alive for the entire session. It could start the container only when it needs to install packages or run the full build.

Events Logger is another example. It is an Astro application with Drizzle and SQLite.

An agent could use the Worker runtimes to update a dashboard, add an event type, change a report, or generate documentation. The container would be useful for dependency installation, database migrations, and the complete Astro test and build process.

There is an important boundary here. Events Logger keeps its production data in a local SQLite database. Cloudflare Computer would not magically gain access to that database. I would have to provide a narrow API or explicitly upload an export.

I would keep that separation.

Livestream Recorder shows another boundary. It works with yt-dlp, FFmpeg, and large video files.

The container is the right place to run the native tools. But I would not copy multi-gigabyte recordings into the Workspace filesystem. I would keep the media in object storage or on a normal disk and use the Workspace for source code, recording manifests, logs, metadata, and repair instructions.

The same applies to Port Pilot and Local Hoster. Both exist to inspect and control my local Mac. A remote Cloudflare Computer cannot replace that local access.

It could work on their source code. It could not see my local processes, ports, certificates, or Keychain unless I deliberately built a bridge and granted that access.

That is how I would use Cloudflare Computer: as a durable workspace for an agent, not as a pretend replacement for every machine and every kind of storage.

The performance tradeoff

The filesystem is optimized for an agent workspace, not raw disk throughput.

Cloudflare’s published repository benchmark compares the computerd FUSE mount with memory and the container’s ext4 disk.

The FUSE workspace performs well on some metadata-heavy tasks. In the published run, directory traversal, git init, and a small Git clone beat the ext4 baseline.

Large sequential files are much slower.

Reading or copying a 64 MiB file through the mount was roughly 30 to 40 times slower than the disk baseline. Installing the 854-package cloudflare/sandbox-sdk dependency tree took 124.7 seconds through computerd, compared with 63.9 seconds on ext4.

The reason is the same mechanism that makes sync efficient. Every 512 KiB chunk is hashed and stored by content.

This is a good trade for source files, Git operations, generated documents, and incremental changes.

It is a poor fit for large media files or enormous dependency trees.

Cloudflare explicitly recommends small, agent-scale workspaces rather than full monorepos.

What survives a restart?

The Durable Object filesystem is authoritative and durable.

The current container-side filesystem lives in memory.

If the container process restarts, its local copy disappears. The next push from the Durable Object rebuilds the container state.

This means the container is replaceable.

The durable identity belongs to the Workspace, not to one Linux process.

That distinction is the whole architecture in one sentence.

Why this design matters

The normal way to give an agent a computer is to create a container and leave it running.

That gives the agent a familiar shell, filesystem, processes, packages, and network.

It also makes the most expensive execution environment the default.

Cloudflare Computer reverses the model.

The filesystem and agent state live in a horizontally scalable Durable Object. Fast isolate runtimes handle file and code work. A container becomes one tool the agent can call when it needs Linux.

This follows the same rule I use in my Production at the Edge mini-course: start with the smallest architecture, then add infrastructure only when a real requirement demands it.

Cloudflare says its goal is to make containers necessary for less than 10% of an agent’s work.

The current preview does not prove that target yet.

But the architecture points in an interesting direction:

Give the agent one durable computer abstraction, then choose the cheapest execution environment for each action.

The agent sees files and tools.

The platform decides whether the hands are a Worker or a container.

That is much more interesting than starting another container.

~~~

Related posts about cloudflare: