# Session Persistence Differences: Cloudflare Durable Objects vs Node.js In-Memory Stores in Flue

> Compare Cloudflare Durable Objects with Node.js in-memory stores for session persistence in Flue. Discover edge SQLite vs in-memory Maps and their restart implications.

- Repository: [Astro/flue](https://github.com/withastro/flue)
- Tags: comparison
- Published: 2026-05-11

---

**Cloudflare Durable Objects persist agent sessions across requests and restarts using edge SQLite storage, while Node.js relies on per-process JavaScript Maps that clear on restart unless replaced with a custom implementation.**

Flue, the open-source agent SDK from withastro/flue, abstracts conversational state management through pluggable **session stores** that adapt to your deployment target. Understanding how session persistence differs between Cloudflare Workers and Node.js runtimes is essential for designing agents that retain conversation history, files, and sandbox changes across invocations.

## Where Session Data Lives

The fundamental difference lies in durability guarantees. Flue agents store messages, uploaded files, and sandbox state in a session object, but where that object survives depends entirely on the runtime.

**Cloudflare Durable Objects** store session data inside a per-agent Durable Object instance backed by SQLite on Cloudflare’s edge. This persists across any number of requests and worker restarts, providing a single source of truth for all concurrent invocations targeting the same `instanceId`.

**Node.js In-Memory Stores** keep session data in a plain JavaScript `Map` that exists only within the current process. When the Node process restarts or when scaling to multiple processes, the store initializes empty, isolating each process from others.

## Cloudflare Implementation: DO-Backed SessionStore

When you build with `--target cloudflare`, the SDK generates a Durable Object binding for each agent (see [`packages/sdk/src/build-plugin-cloudflare.ts`](https://github.com/withastro/flue/blob/main/packages/sdk/src/build-plugin-cloudflare.ts)). The runtime then imports the store implementation from [`packages/sdk/src/cloudflare/session-store.ts`](https://github.com/withastro/flue/blob/main/packages/sdk/src/cloudflare/session-store.ts), which uses the Durable Object’s `state` and `setState` methods:

```ts
// packages/sdk/src/cloudflare/session-store.ts
export function store(): SessionStore {
  return {
    async save(id, data) {
      const { agentInstance } = getCloudflareContext();
      const sessions = { ...(agentInstance.state?.sessions ?? {}) };
      sessions[id] = data;
      agentInstance.setState({ ...agentInstance.state, sessions });
    },
    async load(id) {
      const { agentInstance } = getCloudflareContext();
      return agentInstance.state?.sessions?.[id] ?? null;
    },
    async delete(id) {
      const { agentInstance } = getCloudflareContext();
      const sessions = { ...(agentInstance.state?.sessions ?? {}) };
      delete sessions[id];
      agentInstance.setState({ ...agentInstance.state, sessions });
    },
  };
}

```

This implementation guarantees that all requests hitting the same agent instance read from and write to the same SQLite-backed state, ensuring consistency across the edge network.

## Node.js Implementation: In-Memory SessionStore

For Node.js environments, Flue defaults to `InMemorySessionStore` defined in [`packages/sdk/src/session.ts`](https://github.com/withastro/flue/blob/main/packages/sdk/src/session.ts). This class maintains a private `Map<string, SessionData>` that lives only as long as the process:

```ts
// packages/sdk/src/session.ts
export class InMemorySessionStore implements SessionStore {
  private store = new Map<string, SessionData>();

  async save(id: string, data: SessionData) { 
    this.store.set(id, data); 
  }
  
  async load(id: string) { 
    return this.store.get(id) ?? null; 
  }
  
  async delete(id: string) { 
    this.store.delete(id); 
  }
}

```

While this provides the fastest possible read/write performance (pure in-process memory access), it offers no durability. A server restart, container redeployment, or load-balanced request to a different process results in lost session state.

## Runtime Selection Logic

Flue selects the appropriate store automatically based on the build target. In [`packages/sdk/src/runtime/flue-app.ts`](https://github.com/withastro/flue/blob/main/packages/sdk/src/runtime/flue-app.ts), the SDK forwards incoming HTTP requests to the per-agent Durable Object when running on Cloudflare, whereas Node.js runs invoke the handler directly, utilizing the in-memory store. You can verify the active store by checking the runtime context; no manual configuration is required for the defaults.

## Replacing the Default Store

Both runtimes allow you to override the default store by passing a custom implementation to `init()`. For example, replacing the Node.js Map with Redis for multi-process durability:

```ts
import { init, type SessionStore, type SessionData } from '@flue/sdk';
import { createClient } from 'redis';

class RedisSessionStore implements SessionStore {
  private client = createClient();
  
  async save(id: string, data: SessionData) {
    await this.client.set(id, JSON.stringify(data));
  }
  
  async load(id: string) {
    const raw = await this.client.get(id);
    return raw ? JSON.parse(raw) : null;
  }
  
  async delete(id: string) {
    await this.client.del(id);
  }
}

const harness = await init({
  model: 'anthropic/claude-haiku-4-5',
  store: new RedisSessionStore(), // overrides default in-memory store
});

```

Similarly, you can provide a custom store when targeting Cloudflare by passing the `store` option to `init()`, though this replaces the Durable Object backing entirely.

## Performance and Durability Trade-offs

**Cloudflare Durable Objects** introduce a small network hop to Cloudflare’s edge storage but guarantee durability and cross-request consistency. This suits production agents requiring conversation history spanning days or running across multiple edge nodes.

**Node.js In-Memory** offers minimal latency and zero external dependencies, making it ideal for local development, testing, or short-lived scripts where persistence is unnecessary. However, it requires external storage (like Redis or PostgreSQL) via a custom `SessionStore` implementation for production multi-process deployments.

## Summary

- **Cloudflare**: Uses Durable Objects ([`packages/sdk/src/cloudflare/session-store.ts`](https://github.com/withastro/flue/blob/main/packages/sdk/src/cloudflare/session-store.ts)) with SQLite backing for durable, cross-request session persistence.
- **Node.js**: Uses `InMemorySessionStore` ([`packages/sdk/src/session.ts`](https://github.com/withastro/flue/blob/main/packages/sdk/src/session.ts)) with a JavaScript Map that clears on process restart.
- **Customization**: Both platforms accept custom `SessionStore` implementations via the `init({ store })` option.
- **Build step**: The `--target cloudflare` flag triggers Durable Object binding injection in [`packages/sdk/src/build-plugin-cloudflare.ts`](https://github.com/withastro/flue/blob/main/packages/sdk/src/build-plugin-cloudflare.ts).

## Frequently Asked Questions

### Does Flue automatically persist sessions in Node.js?

No. By default, Node.js uses `InMemorySessionStore`, which stores data in a process-bound JavaScript Map. Sessions are lost when the process restarts. To persist sessions in Node.js, you must provide a custom store implementation (such as Redis or PostgreSQL) that satisfies the `SessionStore` interface.

### Can I use Durable Objects in local development?

Yes. When running locally with the Cloudflare target, the Wrangler CLI simulates Durable Objects, allowing you to test persistence behavior. However, the default Node.js target uses in-memory storage unless explicitly configured otherwise.

### How do I share sessions across multiple Node.js processes?

You cannot share the default in-memory store across processes. Each Node.js process maintains its own isolated `Map`. To share sessions across processes or containers, implement a custom `SessionStore` backed by a shared database or cache (like Redis) and pass it to `init({ store })`.

### What happens to session data when a Cloudflare Worker restarts?

Session data survives worker restarts because it is stored in the Durable Object’s SQLite-backed state, not in the Worker’s memory. The Durable Object persists its state to Cloudflare’s edge storage, reloading it when the Worker instance reinitializes.