Redis is best known as a cache, but it also includes a lightweight message broker. With Pub/Sub (publish/subscribe), one part of your system publishes messages to a channel, and every client subscribed to that channel receives them instantly. It’s a simple way to push live notifications, broadcast events between app servers, and scale WebSocket servers horizontally.

The basics with redis-cli

Open two terminals. In the first, subscribe:

redis-cli SUBSCRIBE notifications

In the second, publish:

redis-cli PUBLISH notifications '{"user":42,"text":"Your order shipped"}'
# (integer) 1   ← number of subscribers that received it

The first terminal prints the message immediately. See redis-cli commands.

Pattern subscriptions match many channels:

redis-cli PSUBSCRIBE 'orders.*'
redis-cli PUBLISH orders.created '{"id":1001}'
redis-cli PUBLISH orders.shipped '{"id":1001}'

Inspect what’s going on:

redis-cli PUBSUB CHANNELS '*'
redis-cli PUBSUB NUMSUB notifications

Node.js example (node-redis)

A subscriber needs its own connection, because a connection in subscribe mode can’t run other commands:

import { createClient } from 'redis';

const pub = createClient({ url: process.env.REDIS_URL });
const sub = pub.duplicate();
await Promise.all([pub.connect(), sub.connect()]);

await sub.subscribe('notifications', (message) => {
  const event = JSON.parse(message);
  console.log('received', event);
});

await pub.publish('notifications', JSON.stringify({ user: 42, text: 'Hello' }));

Python example (redis-py)

import json, redis

r = redis.Redis.from_url("redis://localhost:6379/0")

# publisher
r.publish("notifications", json.dumps({"user": 42, "text": "Hello"}))

# subscriber (blocking loop, usually in its own process or thread)
p = r.pubsub()
p.subscribe("notifications")
for msg in p.listen():
    if msg["type"] == "message":
        print(json.loads(msg["data"]))

Typical use: scaling WebSockets across servers

With several app servers behind a load balancer, a user connected to server A should still receive an event triggered on server B. Pub/Sub solves it:

  1. Every app server subscribes to a channel (e.g. ws-broadcast).
  2. When something happens, the server that handles it publishes to Redis.
  3. All servers receive the message, and push it to their connected WebSocket clients.

Socket.IO’s Redis adapter, Django Channels’ Redis layer and Laravel broadcasting all work this way. See WebSockets explained and Nginx WebSocket proxy.

Other uses: cache invalidation broadcasts (“clear key X on every server”), live dashboards, and chat.

Delivery semantics: know the limits

Redis Pub/Sub is fire-and-forget:

  • No persistence: messages aren’t stored. A subscriber that’s offline, or reconnecting, misses them.
  • No acknowledgements or retries.
  • At-most-once delivery to currently connected subscribers.
  • Slow subscribers accumulate output buffers on the Redis server. Redis disconnects clients that exceed client-output-buffer-limit pubsub (default 32 MB hard / 8 MB soft over 60 s).

That’s fine for live notifications, where a missed message is harmless or recoverable (the client can reload state on reconnect). It’s not fine for jobs, payments or anything that must be processed.

When to use Redis Streams instead

Streams (XADD, XREAD, XREADGROUP) are Redis’s persistent log:

Pub/Sub Streams
Stored No Yes (with optional trimming)
Offline consumers Miss messages Catch up from their last ID
Consumer groups No Yes (work sharing, acks, pending lists)
Use for Live broadcast Event logs, job queues, reliable processing
redis-cli XADD orders '*' id 1001 status created
redis-cli XGROUP CREATE orders workers '$' MKSTREAM
redis-cli XREADGROUP GROUP workers w1 COUNT 10 STREAMS orders '>'

For background jobs, also consider queue libraries built on Redis: BullMQ, Sidekiq, Celery, RQ.

Sharded Pub/Sub in Redis Cluster

In Redis Cluster, classic Pub/Sub messages are broadcast to all nodes, which doesn’t scale. Redis 7 adds sharded Pub/Sub (SSUBSCRIBE, SPUBLISH), where channels are assigned to slots like keys.

Security

Pub/Sub uses the same Redis connection as everything else, so the usual rules apply: bind to localhost or a private network, require a password or ACLs, and never expose port 6379 publicly. ACLs can restrict channels (&notifications). See install and secure Redis.

Quick reference

Task Command
Subscribe SUBSCRIBE channel
Pattern subscribe PSUBSCRIBE 'orders.*'
Publish PUBLISH channel message
List channels PUBSUB CHANNELS
Subscriber count PUBSUB NUMSUB channel
Persistent alternative Streams: XADD / XREADGROUP

Related: Redis vs Memcached and Redis Sentinel.

  • Redis
  • Databases
  • Real-Time