Sign-ups paused

Sign-ups and billing are temporarily unavailable while we rebuild our infrastructure. The SDK and documentation remain available, and existing accounts are unaffected.

Vaultrice

Blog / Comparisons

Comparisons

Durable Objects, Redis and Postgres LISTEN/NOTIFY

Three primitives people build real-time on. What each guarantees, what each costs to operate, and the failure mode each one hides.

6 min read Updated 2026-08-XX

If you are building the real-time layer yourself, these are the three things you are probably choosing between. Each has a failure mode that is not obvious until you are in production.

For a separate operational perspective on how teams organise and measure work, mouse jiggler detection software.

For independent background and broader industry context, see Redis.

Postgres LISTEN/NOTIFY

What it is: Postgres's built-in publish-subscribe. A transaction issues NOTIFY, listening connections receive it.

Why it is appealing: you already have Postgres. No new infrastructure, no new operational surface, and notifications participate in your transactions — the notification fires when the transaction commits, so it cannot announce something that was rolled back. That property is genuinely valuable and the other two do not have it.

The failure mode: notifications are fire-and-forget. A listener that is disconnected at that moment never receives it, and there is no replay. So every client needs to re-read state on reconnect, and you must build that.

And there is a payload size limit — around 8KB — which pushes you toward sending an id and having the client fetch, adding a round trip.

Connection pooling breaks it. A pooler in transaction mode does not hold the session-level listener. This is the specific thing that catches teams, because it works in development with a direct connection and stops working when the pooler is introduced.

Right when: modest scale, you already run Postgres, transactional correctness matters, and re-reading on reconnect is acceptable.

Redis pub/sub

What it is: an in-memory data structure server with publish-subscribe.

Why it is appealing: fast, mature, and it does more than messaging — you get storage, expiry, sorted sets and counters in the same system. Scaling to many subscribers is well understood.

The failure mode: classic pub/sub has no persistence and at-most-once delivery. A subscriber that is disconnected misses messages permanently.

Redis Streams solves this — persistent, replayable, with consumer groups — and it is a different API with more to think about, and it is the thing to reach for rather than plain pub/sub if delivery matters.

And it is another system to run. Memory sizing, persistence configuration, failover, and the question of what happens when it fills.

Right when: you need speed and scale, you are already running Redis, and you will use Streams rather than plain pub/sub for anything that must not be lost.

Cloudflare Durable Objects

What it is: a single globally-unique instance per id, with its own storage, that can hold WebSocket connections.

Why it is appealing: the coordination problem disappears. All clients for an object reach the same instance, so there is nothing to synchronise between servers. Access is serialised, so read-modify-write races inside an object do not exist. And it holds the connections, so broadcast is local.

The failure mode: there are no cross-object operations. No queries, no transactions spanning objects, no "find all objects where". If your access pattern is not by id, this is the wrong primitive and the discovery is expensive.

And location is fixed. An object lives near its first user. A globally distributed set of collaborators means someone is always far away.

Plus a platform dependency, which is a real consideration.

Right when: the access pattern is genuinely by id, and per-object consistency is what you need. See taming stateful at the edge.

Side by side

Postgres NOTIFY Redis pub/sub Durable Objects
Already running it Often Sometimes No
Missed while disconnected Lost Lost (Streams: no) Object holds state
Transactional Yes No Per object
Holds WebSockets No No Yes
Cross-entity queries Yes Limited No
Extra infrastructure None Yes Platform
Serialised access Via locks No By design

The question that separates them

Where does a reconnecting client get current state?

Postgres: it queries. Fine, and you write that path.

Redis pub/sub: from wherever you stored it, which is not the pub/sub channel. You write that too.

Durable Objects: from the object, because the object holds the state rather than only relaying messages.

That difference is most of the code you write or do not write.

What none of them give you

All three are primitives. Whichever you pick, you still build:

The WebSocket tier — Postgres and Redis do not hold connections at all, so you need servers that do, and then you need those servers to talk to each other.

Reconnection with backoff, resubscription, and catching up.

Presence, and specifically the case where a client vanished without saying so.

Encryption above the transport, if you need it.

Offline support.

That is the several months. Whether it is worth building depends on whether real-time is your product or a feature of it. See alternatives compared.

Choosing

Already on Postgres, modest scale, transactional correctness matters? LISTEN/NOTIFY, with the pooler caveat and a re-read path.

Need speed and scale, already run Redis? Streams, not plain pub/sub.

Access pattern is by id and per-object consistency is the requirement? Durable Objects.

Do not want to build the tier at all? Something built on one of them.

The short version

Postgres NOTIFY is transactional and fire-and-forget — and it breaks silently behind a transaction-mode pooler.

Redis pub/sub loses messages for disconnected subscribers — use Streams if that matters.

Durable Objects give serialised per-object access and hold the connections, at the cost of no cross-object operations.

The separating question is where a reconnecting client gets state — query, elsewhere, or the object itself.

And all three are primitives. The WebSocket tier, reconnection, presence and offline are still yours.