# How to run OpenCode 2.0 on Cloudflare and recover after Durable Object eviction

> Use Cloudflare’s experimental OpenCodeHarness to persist agent sessions in SQLite and resume interrupted work after Durable Object eviction.
> By Dave · 2026-10-13
> Source: https://otf-kit.dev/blog/cloudflare-opencode-durable-sessions

A multi-turn agent is useful only if a short interruption does not erase its work. For a production builder, that interruption might be an idle host closing, a Durable Object being evicted, or a run that stops while the user is waiting. If your recovery plan is “rebuild the agent and hope the transcript is somewhere else,” you have left session continuity to chance.

Cloudflare’s experimental OpenCodeHarness gives you a concrete recovery path: OpenCode runs inside a Durable Object, its session data is persisted in the object’s SQLite storage, and Lifecycle wakes interrupted work after an eviction. The important design choice is to treat the object as the durable session boundary, while treating the host process as disposable.

This guide walks through the deployment shape, how the pieces fit, and what to test before you put a real user’s multi-turn work behind it. Cloudflare labels the harness experimental, so treat the integration as a prototype to validate against your own reliability needs—not a production stability guarantee.

## The recovery model: durable state, disposable host

A Durable Object instance can be evicted. The JavaScript host process in that instance is not the place to keep the only copy of a conversation or an in-progress task. Instead, the OpenCodeHarness integration connects OpenCode’s session state to Durable Object SQLite and installs Lifecycle support for interrupted work.

Cloudflare describes three parts of the flow:

1. OpenCode stores sessions, messages, and durable events in the Durable Object’s SQLite database. Its tables and indexes are prefixed with `opencode_`.
2. A Lifecycle job tracks an active session with a heartbeat.
3. If the object crashes or is evicted during that work, an alarm starts the object again and OpenCode resumes the interrupted work.

That is the recovery boundary. The object can be recreated; the persisted session state is what allows the agent to continue. Lifecycle is responsible for waking the object, while OpenCode owns the agent loop, transcript, tools, and recovery behavior.

The host may also close after a period with no work or event subscriptions. That is expected lifecycle behavior, not proof that the session disappeared. Keep your application design centered on the durable object identity and stored session, not on a process remaining warm.

![Dex and Nova examine persistent record tiles and a Lifecycle alarm waking an object.](https://cdn.otf-kit.dev/blog/cloudflare-opencode-durable-sessions/inbody1-20261013a.png)

## Install the integration and configure SQLite-backed storage

Cloudflare’s announcement lists Agents SDK 0.28.0 or later, plus the OpenCode SDK and plugin packages. Install the integration packages with your package manager:

```bash
npm install agents@latest @opencode/sdk@2 @opencode/plugin@2
```

The Workers configuration needs a Workers AI binding, a Durable Object binding, a SQLite-backed class migration, and the `nodejs_compat` compatibility flag. The shape is:

```toml
name = "opencode-agent"
main = "src/index.ts"
compatibility_date = "2026-10-13"
compatibility_flags = [ "nodejs_compat" ]

[ai]
binding = "AI"
remote = true

[[durable_objects.bindings]]
name = "Assistant"
class_name = "Assistant"

[[migrations]]
tag = "v1"
new_sqlite_classes = [ "Assistant" ]
```

Use a current compatibility date when you deploy. The migration declares the class as SQLite-backed, which is the storage the harness passes to OpenCode. If you already have Durable Objects, treat this as a schema and rollout change: review the migration against your existing bindings before applying it.

## Create the harness and attach Lifecycle

The core wiring is intentionally small. The harness factory receives a storage object. Pass that storage into `OpenCodeWorkerd.create()`; this connects OpenCode to the Durable Object’s SQLite database. Then install Lifecycle on the object and register the harness as a capability.

```ts
import { DurableObject } from "cloudflare:workers";
import { Lifecycle } from "agents/lifecycle";
import { OpenCodeWorkerd } from "@opencode/sdk/workerd";
import { OpenCodeHarness } from "agents/harness/opencode";
import { createAI } from "agents/models/opencode";
import workerTools from "./worker-tools";

export class Assistant extends DurableObject {
  ai = createAI({ binding: this.env.AI });

  harness = new OpenCodeHarness({
    opencode: ({ storage }) =>
      OpenCodeWorkerd.create({
        storage,
        models: { fetch: false },
        plugins: [this.ai.plugin, workerTools],
      }),
    defaults: {
      model: this.ai("@cf/moonshotai/kimi-k2.7-code", {
        reasoningEffort: "low",
      }),
      agent: "build",
    },
  });

  lifecycle = Lifecycle.install(this).use(this.harness);

  async ask(prompt: string) {
    const { status, text, reason } = await this.harness.prompt(prompt);
    return { status, text, reason };
  }
}
```

The code mirrors Cloudflare’s example. The `AI` binding supplies the model provider; the custom plugin is where you register tools that make sense inside your Worker. OpenCode’s local filesystem and shell tools do not automatically become useful or safe in a Worker environment. Adapt the tool set to the runtime and your own security boundary rather than assuming that a desktop configuration can be copied unchanged.

The object name in an example is not a multi-tenant identity strategy. In your application, resolve the object from an authenticated user or conversation key, and use that same routing rule on reconnect. That gives a later request a path back to the object holding the stored session. Do not let a client choose another user’s object name without authorization.

## Submit work so reconnects can find it

For a request that can finish during a single call, the example uses `harness.prompt()` and returns its status, text, and reason. If your product needs long-running tasks, make that a separate application-level workflow: persist a task ID and its session association before returning an accepted response, then let later requests resolve the same object and read the latest session state.

This distinction matters for a user interface. A browser connection can drop while work continues. The client should reconnect through the same authenticated user or conversation route and ask for current session state rather than submitting the same task blindly. Keep an application-level idempotency key for each logical task and enforce it at your own API boundary. The durable transcript is not itself a promise that a retried HTTP request will not start duplicate work.

If your UI streams progress, treat the stream as a view of work, not the source of truth. When the connection drops, reconnect to the same object, reload the current session state, and reattach the progress view. Distinguish “connection lost” from “task failed” so the user sees a recoverable state while the client checks whether work resumed.

![Byte and Nova reconnect clients to a restored session-event timeline.](https://cdn.otf-kit.dev/blog/cloudflare-opencode-durable-sessions/inbody2-20261013a.png)

## Test eviction recovery before exposing it to users

A local happy-path request proves that the harness can answer once. It does not prove that the system recovers. Before you rely on this for multi-turn production work, exercise the interruption path deliberately in a non-production environment.

Start a session, submit a task that takes long enough to remain active, then force the Durable Object to restart or become unavailable using the supported test setup for your environment. Reconnect through the normal user route. Check that the same object is selected, the stored transcript is present, and interrupted work resumes instead of starting from a blank session. Repeat with the client dropping its WebSocket while the object continues running; the UI should reattach without duplicating the task.

Also test the negative paths. A task may have been completed before the client disconnected, so a retry must not produce a second side effect. A run may have stopped in a way that requires the application to surface an error rather than claim success. Keep any external side effects idempotent at your tool boundary; durable transcript recovery alone cannot undo a payment, send a message, or reverse a database mutation that ran before the interruption.

Record the state transitions you expect: submitted, running, interrupted, resumed, and completed. Verify how your client learns the difference between an interrupted operation and an unanswered result. Alert on work that stays pending beyond your own service objective, and provide a user-visible retry or resume action. Those are application reliability decisions around the harness, not features that an experimental integration can decide for you.

## Know what the experimental label means

The Oct. 12 announcement calls OpenCodeHarness experimental. That label should shape your rollout. Start with a bounded internal workload, validate recovery under the same failure modes your users will hit, and keep a fallback plan if an SDK update changes the integration surface. Pin the dependencies you deploy, review changelog changes before upgrading, and test a sample session from submission through eviction and resume against the exact versions you intend to run.

The harness provides a useful foundation: SQLite-backed session data, Lifecycle wake-up, an OpenCode host, and a Workers AI provider. It does not make every tool compatible with Workers, select your tenant model, define your authorization rules, or guarantee that every business action can safely repeat. Own those application boundaries explicitly.

If your agent also needs an operator dashboard, the [SaaS Dashboard template](/templates/saas-dashboard) can be an optional UI starting point. It does not include Cloudflare hosting, OpenCode, or this session-recovery integration. For a separate look at isolating preview Durable Objects during CI, see [our Cloudflare Worker previews guide](/blog/cloudflare-workers-previews-adlc).

## Sources

- [Cloudflare: Run OpenCode 2.0 on Cloudflare with the Agents SDK (Oct. 12, 2026)](https://developers.cloudflare.com/changelog/post/2026-10-12-opencode-harness/)
