SUBSCRIBE, PUBLISH, PSUBSCRIBE for pattern channels, the no-persistence guarantee, keyspace notifications, horizontal scaling across app servers, and when to use Streams instead of Pub/Sub.
F-7 — Pub/Sub and the Message Fanout Model
Who this module is for: You have heard of Redis Pub/Sub and may have used it for real-time notifications or chat. But you have not understood its most important limitation — messages are not persisted and any subscriber that is offline loses them permanently. This module covers the full Pub/Sub model, pattern-based subscriptions, its correct use cases, and when to use Streams instead.
What Pub/Sub Is
Redis Pub/Sub is a message fanout system. Publishers send messages to named channels. Subscribers listening on those channels receive every message in real time. Redis acts as the broker — it receives the message from the publisher and immediately delivers it to all connected subscribers.
The key characteristic: delivery is best-effort and immediate. If no subscriber is connected when a message is published, the message is gone. If a subscriber disconnects and reconnects, it misses all messages published during the disconnection window.
Commands
Subscribing
SUBSCRIBE channel [channel ...]
Once a client issues SUBSCRIBE, it enters a special "subscriber mode." In this mode, the only commands it can issue are SUBSCRIBE, UNSUBSCRIBE, PSUBSCRIBE, PUNSUBSCRIBE, PING, and RESET. The connection is dedicated to receiving messages.
Publishing
PUBLISH channel message
Returns the number of subscribers that received the message (0 if nobody is subscribed).
Unsubscribing
UNSUBSCRIBE [channel ...] → unsubscribe from channels; no args = unsubscribe from all
Pattern Subscriptions
PSUBSCRIBE subscribes to channels matching a glob pattern:
Pattern messages include the matched channel name in the received message:
Inspecting Active Pub/Sub State
Message Delivery Guarantees (and the Lack Thereof)
This is the most important thing to understand about Redis Pub/Sub:
There are no delivery guarantees.
- If a subscriber is offline when a message is published → message lost
- If a subscriber's connection drops mid-delivery → message lost (TCP retransmission handles byte-level loss, but Redis does not retry at the application level)
- If the Redis server goes down → all pending messages lost
- Messages are not stored anywhere — there is no way to replay past messages
- A slow subscriber receiving messages faster than it can process them will have messages buffered in Redis's output buffer. If the buffer exceeds
client-output-buffer-limit, Redis disconnects the client and the remaining buffered messages are lost.
PUBLISH returns immediately regardless of whether subscribers received the message.
This is fundamentally different from a message queue (BullMQ, RabbitMQ, Kafka). A queue persists messages until a consumer acknowledges them. Pub/Sub does not.
Building a Real-Time Notification System
Here is a typical Pub/Sub pattern in a Node.js backend:
Why two separate connections? A subscribed connection is in subscriber mode — you cannot issue normal commands on it. You need a separate connection for everything else (including PUBLISH).
Pattern Subscription in Node.js
Horizontal Scaling: One Subscriber Per Server
When you run multiple application servers behind a load balancer, each server has its own WebSocket clients. A message published on server A's Redis connection needs to reach WebSocket clients on servers B and C.
The solution is to have every application server subscribe to the relevant channels. When a message is published (by any server), Redis delivers it to all subscribers — including the subscribers on every application server:
This is the correct pattern for scaling real-time features across application servers. Each server maintains its own subscriber connection per channel family.
Pub/Sub vs Streams
The question comes up constantly: "should I use Pub/Sub or Streams for this?" Here is the honest answer:
| Concern | Pub/Sub | Streams |
|---|---|---|
| Message persistence | No — fire and forget | Yes — messages stored until explicitly deleted |
| Replay missed messages | No | Yes — consumers can read from any position |
| Consumer groups (competing consumers) | No | Yes — multiple consumers sharing a stream |
| Message acknowledgement | No | Yes — XACK |
| Offline consumers | Messages lost | Messages waiting when consumer reconnects |
| Delivery guarantee | At-most-once | At-least-once (with ACK) |
| Latency | Lowest possible | Slightly higher (storage + ACK overhead) |
| Complexity | Simple | Moderate |
Use Pub/Sub when:
- You need real-time fanout to many subscribers simultaneously
- Missing messages during disconnection is acceptable (or impossible, e.g., the subscriber is always connected)
- You are broadcasting to WebSocket/SSE clients where reconnecting clients will reload state anyway
- Cache invalidation signals (you do not need to replay past invalidations)
- Live activity feeds where new users see only new events, not history
Use Streams when:
- Messages must not be lost
- Consumers may go offline and must catch up on missed messages
- You need multiple independent consumers processing the same stream (consumer groups)
- You need durable event sourcing or audit logs
- Job queues where every job must be processed exactly once
Streams are covered in depth in P-4.
Client Output Buffer Limits
Redis has a protection mechanism for slow Pub/Sub subscribers. If a subscriber cannot read messages fast enough, they accumulate in Redis's output buffer for that client connection. If the buffer exceeds the configured limit, Redis forcibly disconnects the slow subscriber.
Default configuration:
client-output-buffer-limit pubsub 32mb 8mb 60
This means: disconnect a Pub/Sub subscriber if their output buffer exceeds 32MB at any point, or if it stays above 8MB for more than 60 consecutive seconds.
This is a safety valve — a slow subscriber should not cause Redis to run out of memory buffering messages for them. In practice, if your subscriber is consistently hitting this limit, it means your publisher is producing faster than the subscriber can consume, and you should consider Streams with consumer groups instead.
Keyspace Notifications: Pub/Sub for Redis Events
Redis can publish internal events to Pub/Sub channels when keys are modified — when a key expires, is deleted, or is written. This feature is called keyspace notifications.
Enable it in redis.conf or at runtime:
CONFIG SET notify-keyspace-events "KEA"
The value is a combination of flags:
K— keyspace events (channel:__keyspace@{db}__:{key})E— keyevent events (channel:__keyevent@{db}__:{event})g— generic commands (DEL, EXPIRE, RENAME)$— String commandsl— List commandsz— Sorted Set commandsx— expired eventse— evicted eventsA— alias forg$lszxet(all events)
Keyspace notifications are useful for:
- Cache warming triggers (when a key expires, recompute and re-cache it)
- Session expiry hooks (notify your app when a session token expires)
- Monitoring and alerting (react when specific keys are deleted or modified)
Caution: Keyspace notifications add overhead to every Redis write operation. Enable them only for the event types you actually need, and disable them if you are not using them.
Summary
- Pub/Sub is fire-and-forget fanout: messages are delivered to all connected subscribers instantly and not persisted
SUBSCRIBEputs a connection in subscriber-only mode; you need a separate connection forPUBLISHand other commandsPSUBSCRIBEsubscribes to channels matching a glob pattern- There are no delivery guarantees — offline subscribers miss messages permanently
- For real-time WebSocket/SSE fanout across app servers, have every server subscribe — Redis delivers to all
- Use Streams when you need durability, replay, or consumer groups
- Client output buffer limits (
client-output-buffer-limit pubsub) protect Redis from slow subscribers - Keyspace notifications let you subscribe to Redis's internal events (key expiry, deletion, etc.)
Next: F-8 — Streams: Append-Only Logs and Consumer Groups — the durable alternative to Pub/Sub that gives you persistent message storage, consumer groups, and at-least-once delivery guarantees.
A developer is building a real-time chat application using Redis Pub/Sub. When a user sends a message, it is published to the chat:global channel. A user complains that when they momentarily lose their internet connection and reconnect a minute later, they do not see the messages that were sent while they were offline. Why does this happen, and what is the proper architectural fix?
In a Node.js microservice, you need to listen for global configuration updates via Pub/Sub while occasionally writing new configurations back to Redis. Why do you need to instantiate *two* separate Redis client connections in your code?
An application relies on Redis Keyspace Notifications to trigger a background job whenever a caching key expires. The team enabled this by running CONFIG SET notify-keyspace-events "Ex". Under heavy load, they notice some background jobs are never triggering, even though the keys are definitely expiring. Given the mechanics of Pub/Sub, what is the fundamental flaw in this architecture?
Test your knowledge with more question sets
Sign in to access a wider variety of questions and get notified when new practice sets are added to this module.
Sign in & RegisterDiscussion
0Join the discussion