Durable Agent Runtimes: Survive Restarts Without Calling It "Memory"
Durability is resume-correct state across crashes and human waits—not memory. Choose LangGraph, Durable Objects, or Temporal by the failure you must survive.
A long-running agent that survives crashes and waits for humans needs a durable runtime: serializable control state outside the process, a resume path that continues from the last safe boundary, and tool writes that stay safe when a step re-runs. That is not “memory.” Memory is what the agent recalls; durability is whether the run still exists after the host dies.
This spoke sits under the Agentic Systems Operating Manual. Pair it with agent memory patterns (what to persist as knowledge) and state machines for agent loops (what states the loop may occupy). Durability is the substrate that keeps those designs alive across restarts.
The short answer
- Durability ≠ memory. Memory stores facts and conversation. Durability stores execution progress so a new process can resume correctly.
- In-process loops die under real ops. Deploys, OOM kills, Durable Object eviction, and overnight human approvals all outlive a single Node/Python process.
- Pick the engine by the failure mode. LangGraph checkpointing for graph-shaped agents; Cloudflare Agents / Durable Objects for addressable, hibernating entities; Temporal-style engines for multi-day workflows with activity retries and signals.
- Measure resume correctness, not “it remembered.” After a kill, does the same
thread_id/ fiber / workflow continue without double-sending email? - HITL pauses must be durable. A pause that lives only in RAM is a memory leak with a polite name.
What a durable agent runtime is (and is not)
| Concern | Durable runtime | Chat / agent “memory” |
|---|---|---|
| Question it answers | Where was this run when the process died? | What facts should the model see next turn? |
| Typical store | Checkpoints, event history, DO SQLite, workflow DB | Vector store, profile rows, transcript slices |
| Success metric | Resume correctness, no duplicate side effects | Answer quality, fewer re-asks |
| Survives human wait of 3 days? | Required | Optional |
If you only keep a longer prompt history, you have memory. If you can kill the worker mid-approval and still resume the same job tomorrow without inventing a new run id, you have durability.
Why in-process loops die under real ops
A demo agent is a while loop in one process. Production invents ways to murder that process:
- Rolling deploys mid-tool-call
- Platform eviction (Cloudflare Durable Objects typically idle-evict after ~70–140 seconds without keep-alive / alarms)
- Spot / scale-to-zero on serverless
- Operator restart after a bad release
- Human approval that arrives after the original HTTP request is gone
Failure mode we see constantly: the agent emailed the customer, the process died before writing “done,” and a retry emailed again. The model did nothing wrong. The runtime treated “in memory” as “committed.”
LangGraph checkpointing: what problem it actually solves
LangGraph checkpointers persist graph state at super-step boundaries. With a durable saver (Postgres/SQLite — not InMemorySaver), you get:
- Resume after crash on the same
thread_id - Human-in-the-loop via
interrupt()/Command(resume=...)(requires a checkpointer) - Time-travel / debug from checkpoint history
Durability modes trade safety for speed (sync / async / exit per current LangGraph docs). "exit" is faster for long graphs but does not protect mid-execution crashes the way "sync" does. Read that tradeoff before you claim “we checkpoint.”
When LangGraph checkpointing is enough: one graph owns the agent; state is typed and serializable; HITL is “pause this graph for a reviewer”; you already live in the LangGraph / LangSmith world.
When it is not: you need an addressable long-lived entity that wakes on email/WebSocket/cron; or you need Temporal-grade activity timers, sagas, and cross-service orchestration that outgrows a single graph process.
Resume note that teams miss: interrupting and resuming often re-enters the interrupted node. Side effects inside that node must be idempotent or gated. Durability without idempotent tool writes doubles the blast radius.
Cloudflare Agents / Durable Objects: what problem they actually solve
Per Cloudflare Agents long-running docs, agents are Durable Objects: globally addressable identities with SQLite-backed state that hibernate when idle and wake on events. They are not always-on processes.
Primitives that matter for durability (as of the 2026 Agents SDK docs):
| Primitive | Job |
|---|---|
setState() / this.sql | Persist entity state across activations |
schedule() / alarms | Wake later (HITL timers, polls) |
keepAlive() / keepAliveWhile() | Reduce eviction during active work |
runFiber() / stash() | Checkpoint long work; recover via onFiberRecovered |
startFiber() | Durably accept jobs with idempotency + status |
runWorkflow() | Hand heavy multi-step work to Cloudflare Workflows |
When Cloudflare Agents fit: the agent is an entity (per tenant, per ticket, per inbox); waits span hours/days; you want hibernation economics; tool work must survive DO eviction with fibers, not hope.
When they are the wrong hammer: you only need a short request/response graph with Postgres checkpoints, and you do not want to design around eviction. Then LangGraph + Postgres is less platform-specific.
Eviction is the design constraint. keepAlive lowers the chance; runFiber makes eviction survivable. Confusing the two is how “it worked in staging” becomes “lost the job overnight.”
Temporal-style engines: what problem they actually solve
Temporal (and cousins like AWS Step Functions for some shapes) own durable execution via event history: workflows that sleep for days, activities with retries/timeouts, signals for human input, and deterministic replay.
When to reach for Temporal / Step Functions:
- Multi-day business processes with many external systems
- Strict activity retry/timeout policies independent of the LLM loop
- You already run Temporal for non-AI workflows and the agent is one activity graph among many
- You need audit-grade “exactly what happened” from the history log
When not to: a five-day pilot with one agent job type and a single Postgres checkpoint table. Temporal tax is real; earn it.
Rough decision table:
| You need… | Start here |
|---|---|
| Graph agent + HITL inside one app | LangGraph + durable checkpointer |
| Addressable hibernating agent entity | Cloudflare Agents / Durable Objects |
| Multi-day cross-service orchestration | Temporal-style engine |
| “Remember the customer’s prefs” | Memory layer — not a runtime upgrade |
How human-in-the-loop pauses stay durable
A durable HITL pause is three parts:
- Persist the run at a named boundary (checkpoint / fiber stash / workflow wait)
- Return a handle the UI/ops can load (
thread_id, fiber id, workflow id) - Resume by feeding the human decision into the same handle — not by starting a new chat
Checklist:
- Pause state is in durable storage, not the request thread
- Approval payload is bound to that run id (no “approve whatever is latest”)
- Resume path is exercised in staging with a process kill mid-wait
- Tool nodes after resume are idempotent
- Timeout / escalate path exists if the human never answers
If the human waits three days, the original HTTP connection is already archaeology. Only the durable handle matters.
What state should never be stuffed into the prompt
Keep these out of the model context as your source of truth:
| State | Where it belongs |
|---|---|
| Run / thread / fiber ids | Runtime ledger |
| Tool ledger (what already ran, keys, results hashes) | Harness DB — see no-progress / idempotency spokes |
| Auth tokens and IAM scope | Secret store + policy gate |
| Approval decisions | Durable HITL record |
| Budget / kill-switch counters | Control plane |
| Full raw tool dumps | Artifact store with redaction |
The prompt may summarize some of this. The prompt must not be the only copy. Summarization is how agents re-call a tool that already succeeded.
How to measure recovery: resume correctness vs “it remembered”
Run this drill monthly:
- Start a real job that reaches a HITL pause or a slow tool
- Kill the worker / evict the DO / restart the pod
- Resume from the stored handle
- Score the outcome
| Metric | Pass | Fail |
|---|---|---|
| Same run id continues | Yes | New run invented |
| Side effects once | One email / one charge | Duplicate |
| State machine position | Same state as before kill | Rewound or skipped |
| Human sees prior context | Approval UI shows pending payload | Empty / wrong job |
“The model recalled the ticket number” is a memory win. It is not a durability win.
Worked failure: the overnight approval that double-booked
What broke: Support agent drafted a refund, paused for manager approval in an in-memory queue. Deploy restarted the API. Manager approved what looked like a stuck ticket. A new agent run also resumed from a stale Redis key. Two refunds.
Cost: Finance cleanup, customer trust hit, two days of “why agents suck” in Slack.
Instead:
- Persist pause under a single durable
run_id - Bind the approval button to that id + payload hash
- Issue refund tool with an idempotency key owned by the runtime
- Kill-test the pause path before soft-launch
Bravery is not a restore strategy.
Durability interacting with idempotent tool writes
Durability increases how often a step re-executes after a crash. That makes idempotency mandatory, not optional. On resume:
- Re-enter the node / fiber / activity
- Tool layer sees the same idempotency key
- Upstream returns the original receipt — no second charge
Treat durability and idempotency as one control loop. The operating manual frames the rest of that stack; this post only owns the resume substrate.
Pilot minimum for Spurlock Studios
A $1,500 · 5-day agentic pilot does not require Temporal on day one. It does require:
- One durable handle per job (
thread_idor equivalent) - One kill-and-resume test recorded in the handoff
- Idempotent write tools on the critical path
- HITL pause that survives process death
Framework fashion is optional. Resume correctness is not.
Anti-patterns
Calling a vector store “our durable agent.” That is memory.
In-memory checkpointer in production. Fine for unit tests; worthless for crashes.
HITL as “email the ops channel and hope.” No run handle, no resume.
Checkpointing the entire blob of secrets into Postgres. Redact; store pointers.
Assuming Cloudflare Agents are always-on processes. They hibernate; design for wake/sleep.
Choosing in one afternoon
- List the waits: seconds (tool), minutes (reviewer), days (customer).
- List the kill scenarios you accept as normal (deploy, eviction, scale-to-zero).
- Map each wait × kill to a store that survives it.
- Pick the smallest engine that covers the matrix.
- Prove resume correctness before you argue about model quality.
If the matrix is “short tools + same-day human,” LangGraph + Postgres usually wins. If the matrix is “entity sleeps for a week then wakes on email,” Cloudflare Agents fit. If the matrix is “saga across six services for a month,” Temporal earns its keep.
FAQ
When is LangGraph checkpointing enough?
When your agent is a graph you already own, state is serializable, and HITL is “interrupt this graph / resume with a decision.” Use a durable checkpointer (Postgres/SQLite), pick an explicit durability mode, and make interrupted nodes idempotent. Skip it when you need hibernating addressable entities or Temporal-scale cross-service workflows.
When do Cloudflare Agents / Durable Objects fit?
When the agent should be a long-lived identity that hibernates, wakes on events, and survives eviction with fibers/schedules. Use setState/sql for entity data, runFiber/startFiber for crash-recoverable work, and keepAlive during active LLM/tool stretches. They are a poor fit for a short-lived graph that only needs Postgres checkpoints.
When should I reach for Temporal/Step Functions instead?
When the business process outlives a single agent app: multi-day waits, activity timeouts/retries across many systems, signals from humans or other services, and audit via event history. Do not start here for a pilot with one job type — earn the orchestration tax.
How do human-in-the-loop pauses stay durable?
Persist the run at a boundary, return a stable handle, and resume that same handle with the human’s decision. The original HTTP request will be gone. Bind approvals to run id + payload hash, and kill-test the pause path in staging.
What state should never be stuffed into the prompt?
Run ids, tool ledgers, auth material, approval records, budgets, and raw secret-bearing tool dumps. Summaries may appear in context; the durable store remains authoritative. Prompt-only “state” evaporates on summarization and restart.
How does durability interact with idempotent tool writes?
Resume re-executes boundaries. Without idempotency keys outside the model, a correct resume becomes a duplicate side effect. Design durability and idempotent writes together; measure “side effects once” in the kill-and-resume drill.
CTA
Need a durable agent that survives the first real deploy — not just the demo loop? Start on /agentic or book the pilot at /contact?intent=agentic-pilot.