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

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). The runtime then imports the store implementation from packages/sdk/src/cloudflare/session-store.ts, which uses the Durable Object’s state and setState methods:

// 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. This class maintains a private Map<string, SessionData> that lives only as long as the process:

// 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, 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:

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

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.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →