When Execution Fails: Containers, Cursors, and Recovery
For Cloudflare Computer, the difficult part of full Linux execution begins before a command starts and ends after its last byte of output. This chapter follows the container-backed Workspace through boot, authentication, command supervision, transport loss, and restart. The aim is to separate failures that can be repaired by replaying a cursor from failures that must not be repaired by running a command twice, and to show where the application—not computerd or the runtime router—must supply policy and scheduling.
A container-backed Workspace has three active relationships. The Durable Object owns the authoritative SQLite tree. computerd gives a container process a FUSE-mounted view of that tree and supervises commands. A capnweb WebSocket carries filesystem changes and execution events between the two sides. Each relationship has a different lifetime, so a robust design cannot treat “the container is up” as the same fact as “the command saw current files” or “the command’s writes are durable.”
Boot is a staged boundary, not a single connection
computerd is shipped as a self-contained Node Single Executable Application. It owns the in-container VFS, the FUSE mount, the command runner, and the RPC server. The Cloudflare container backend does not simply open a socket and hope that the daemon is ready. It drives a staged bootstrap because each stage establishes a condition needed by the next one.
First, the backend starts or adopts a container with the requested environment and network policy. The daemon chooses its filesystem mode through FUSE_MOUNT: automatic selection uses real FUSE when the platform exposes the required surface and falls back to a userspace shim when it does not; an explicit none mode leaves the HTTP and RPC service available without projecting a filesystem. Real FUSE makes the container’s ordinary paths resolve into the VFS. The shim is useful for development, but its polling and reconciliation behavior is weaker and is explicitly not production-grade.
Next, the backend waits for the health endpoint. This is a readiness gate, not a filesystem operation from the agent’s point of view. Once the daemon is listening and the selected mount path is available, the backend establishes egress interception and prepares the WebSocket upgrade on the Durable Object side.
The connection direction is intentionally surprising. Rather than having the Durable Object make an ordinary inbound connection to the container, the backend arms a pending upgrade and posts /connect to computerd. The daemon polls the named health path and then dials the named API path outward. The container’s outbound request passes through the egress route controlled by the Durable Object and returns to the DO’s upgrade handler. The DO must arm the upgrade before posting /connect, because the dial-back can arrive before the HTTP response to the post.
This inversion is a platform-aware design choice. The DO controls the route through which the session arrives, and the same path can later support a fresh session after the DO is reconstructed. It also gives the host a place to enforce the daemon’s shared bearer secret. When RPC_CLIENT_SECRET is configured, health remains the unauthenticated liveness probe, while the other daemon routes and the WebSocket upgrade require the secret. The backend checks that the running daemon actually enforces the secret; launching with a token is not enough if an old image would ignore it.