There is a moment in every LangGraph tutorial where you type MemorySaver(), the example resumes across turns, and you conclude that persistence is handled. It is not. You have wired in the one checkpointer that is guaranteed to forget everything the instant your process restarts. The gap between that line and a production-ready agent is the whole subject here — and the choice you actually have to make, Postgres versus Redis, is not the choice most people think it is.

A checkpointer is a program counter, not a cache#

First, the thing being persisted. A LangGraph checkpointer writes an immutable StateSnapshot after every super-step. That snapshot holds the state values and the tuple of nodes to run next, all addressed by a thread_id. Re-invoke the graph with the same thread_id and it loads the last snapshot and continues from the next node — the already-executed nodes do not re-run.

Read that again with a systems eye: the checkpointer is persisting the agent's program counter and stack. It is not a cache of model outputs, and it is not your application's business database. It is the serialized execution state of a paused program — the machinery underneath durable execution for agents, where a run survives a crash by reloading exactly this. Once you see it that way, the "which backend" question stops being about read latency and starts being about what lifecycle you want that execution state to have.

The storage primitive each saver creates is the honest signal. Tables mean ledger. Indices with a TTL mean cache.

The backends disagree by construction#

Watch what each saver does when you call .setup() — the one-time migration step every persistent backend requires.

PostgresSaver.setup() creates tables. That is a ledger. The checkpoints are rows, permanent until you delete them, and you can walk the entire history of a thread with get_state_history and time-travel — pick any prior checkpoint and re-run from it, forking the trajectory. When your agent's state history is itself valuable — for audit, for human-in-the-loop review, for "why did it decide that," for replaying a bad run — Postgres treats it as a first-class, queryable record. This is also the first-party production default: the Postgres and SQLite savers live in the langchain-ai monorepo, and v0.2 split them into standalone libraries with Postgres positioned as the production backend. (If you'd rather not run the checkpointer yourself at all, LangGraph Platform ships a managed Postgres-backed one.)

RedisSaver.setup() creates RediSearch indices — three of them, for checkpoints, channel values, and writes — and the Redis saver supports TTL-based expiry with a configurable default_ttl and refresh_on_read. That is a cache with search bolted on. It is fast, it resumes threads quickly, and it will let old checkpoints age out on their own, which is exactly what you want for high-churn, short-lived conversations you have no reason to keep forever. The tradeoffs come with the territory: it is a separate package maintained by Redis rather than core LangChain, and it depends on the RedisJSON and RediSearch modules — bundled in Redis 8.0+, otherwise you are installing Redis Stack.

Neither primitive is "better." A ledger and a cache are answers to different questions. The mistake is choosing one without noticing you were being asked.

The trap is neither of them#

The failure mode in production is not picking the wrong persistent backend. It is picking InMemorySaver — the MemorySaver alias from the base langgraph-checkpoint package — because it is the default in nearly every example and it works on your laptop. It stores checkpoints in RAM. It loses all of them on process restart. In a real deployment that means every rolling deploy, every crash, every autoscale-down evicts the program counter of every in-flight thread. The agent that was three tool calls into a task wakes up with no memory that the task existed. The official reference is explicit that it is for debugging and testing; production wants Postgres.

So the decision reduces to something you can hold in one sentence. If the state history is an asset — you want to inspect it, replay it, prove what happened — reach for Postgres: a durable ledger. If resume speed and automatic expiry matter more than permanence and you already run the Redis modules, reach for Redis: a fast, self-cleaning cache. And whatever you do, treat MemorySaver the way you treat console.log — indispensable while you build, never the thing you ship.