With 10,000 WebSocket connections, each server instance only knows about its own clients. Broadcasting a real-time notification (order status update) to everyone requires a unified broker for horizontal scaling. We solve this with Redis Pub/Sub: a message is published to a channel, all active subscribers receive it. A fire-and-forget mechanism with <1 ms latency. The broker does not store history – suitable for short notifications, cache invalidation, state synchronization. Our setup for real-time notifications leverages Redis Pub/Sub for horizontal scaling. Below: configuration, integration with Node.js and Laravel, pitfalls.
How to Choose Between Pub/Sub and Redis Streams?
Pub/Sub solves the broadcast problem: one publisher, multiple subscribers, all receive identical messages simultaneously. If you need guaranteed delivery, history, or consumer groups – use Redis Streams. Pub/Sub is 2–3 times faster than Redis Streams for broadcast distribution, but does not guarantee delivery. Summary table:
| Characteristic | Pub/Sub | Redis Streams |
|---|---|---|
| Delivery guarantee | No (fire-and-forget) | Yes (consumer groups) |
| History storage | No | Yes (configurable TTL) |
| Latency | < 1 ms | 1–5 ms |
| Sharding support | Only sharded Pub/Sub (Redis 7+) | Yes (via consumer group) |
| Use case | Real-time broadcast notifications | Queues, guaranteed processing |
How to Avoid Message Loss During Subscriber Disconnection?
A straightforward way to protect against message loss is to duplicate recent events into a separate Redis key with TTL (e.g., last_event:user:42). When a client connects, it first retrieves missed events via REST API, then switches to Pub/Sub. For critical data (financial transactions), use Redis Streams with consumer groups. This approach reduces losses to zero and saves up to 30% resources on reprocessing.
Basic Redis Configuration
Redis supports Pub/Sub out of the box. Recommended redis.conf parameters:
# Memory limit
maxmemory 512mb
maxmemory-policy allkeys-lru
# Number of databases
databases 16
# Disable persistence for pure Pub/Sub
save ""
appendonly no
For production, use Redis Sentinel or Cluster. Regular Pub/Sub in Cluster is limited to a shard – with Redis 7+, use sharded Pub/Sub (SPUBLISH/SSUBSCRIBE).
Integration with Node.js and Socket.io
Use the ioredis library for two separate connections (publisher and subscriber). Example in TypeScript:
import Redis from 'ioredis';
const publisher = new Redis({ host: 'redis', port: 6379 });
const subscriber = new Redis({ host: 'redis', port: 6379 });
subscriber.subscribe('notifications:user:*', (err, count) => {
if (err) throw err;
console.log(`Subscribed to ${count} channels`);
});
subscriber.on('pmessage', (pattern, channel, message) => {
const userId = channel.split(':')[2];
const payload = JSON.parse(message);
broadcastToUser(userId, payload);
});
async function notifyUser(userId: string, event: object) {
const channel = `notifications:user:${userId}`;
const count = await publisher.publish(channel, JSON.stringify(event));
return count;
}
For WebSocket, use Socket.io with Redis Adapter:
import { createServer } from 'http';
import { Server } from 'socket.io';
import { createAdapter } from '@socket.io/redis-adapter';
import { createClient } from 'redis';
const httpServer = createServer();
const io = new Server(httpServer);
const pubClient = createClient({ url: 'redis://redis:6379' });
const subClient = pubClient.duplicate();
await Promise.all([pubClient.connect(), subClient.connect()]);
io.adapter(createAdapter(pubClient, subClient));
httpServer.listen(3000);
Redis Adapter uses Pub/Sub: when io.to('room').emit() is called, the command is published to a Redis channel, and all server instances broadcast it to the room's clients.
Integration with Laravel
In Laravel, sending a Pub/Sub message looks like this:
use Illuminate\Support\Facades\Redis;
Redis::publish('notifications:user:'.$userId, json_encode([
'type' => 'order.status_changed',
'orderId' => $order->id,
'status' => $order->status,
'timestamp' => now()->toISOString(),
]));
For receiving messages on the client, use Laravel Echo Server or Soketi.
Process of Setup (Step by Step)
- Design channel scheme: commonly use patterns
entity:action:user_id. - Configure Redis: set maxmemory, disable AOF for pure Pub/Sub.
- Implement event publishing in the backend (Node.js, Laravel, Django).
- Connect WebSocket server (Socket.io, Soketi) with Redis Adapter.
- Develop fallback request mechanism for missed events (via REST).
- Load test: ensure
instantaneous_ops_per_secdoes not exceed 5,000.
Monitoring
Use Redis CLI commands:
redis-cli PUBSUB CHANNELS "*"
redis-cli PUBSUB NUMSUB notifications:user:42
redis-cli PUBSUB NUMPAT
In Prometheus with Redis Exporter, monitor instantaneous_ops_per_sec. Growth above 5,000 ops/s under Pub/Sub load signals the need for optimization (reduce message size, increase number of channels).
Production Checklist
- Ensure Redis runs in Sentinel or Cluster mode.
- Set maxmemory and eviction policy.
- Use separate connections for Pub/Sub (do not mix with regular operations).
- Implement fallback requests for missed messages.
- Limit publication rate to 5,000 ops/s per instance.
Limitations and Alternatives
Message Loss on Reconnect
A subscriber that disconnects temporarily will miss messages sent during that time. For critical notifications – Redis Streams or store recent events.
No Delivery Confirmation
PUBLISH returns the number of receivers but does not guarantee processing. For at-least-once – use a queue (RabbitMQ, Redis Streams).
CPU Load with Many Patterns
PSUBSCRIBE matches every message against all patterns. With 10,000+ patterns, latency increases by 30–50%.
What's Included in the Work
Implementing Pub/Sub can reduce WebSocket infrastructure costs by up to 40%. For a typical application with 10,000 concurrent users, this setup reduces monthly WebSocket costs by approximately $1,500. A typical Redis instance for Pub/Sub costs around $30-50 per month on cloud providers like AWS or DigitalOcean. Our setup for real-time notifications leverages Redis Pub/Sub, Node.js ioredis, and Socket.io Redis adapter for seamless horizontal scaling. With over 6 years of experience and 15+ successful projects, we provide expert Redis consulting.
- Designing channel and pattern schema for business logic.
- Configuring Redis (configuration, clustering, monitoring).
- Integrating Pub/Sub with the backend (Node.js, Laravel, Django – according to your stack).
- Connecting WebSocket server (Socket.io, Soketi, Laravel Echo).
- Developing message loss prevention (fallback requests).
- Deployment and operations documentation.
- Repository and infrastructure access transfer.
- Support for 2 weeks after delivery.
| Stage | Duration |
|---|---|
| Analysis and design | 1–2 days |
| Redis and Pub/Sub setup | 1 day |
| Backend integration | 1–2 days |
| WebSocket connection | 1 day |
| Testing and debugging | 1–2 days |
| Total | 4 to 6 days |
We have been working with Redis for over 6 years and have implemented 15+ Pub/Sub projects for e-commerce and fintech clients. We use production configurations with Sentinel and Cluster, ensuring fault tolerance. Contact us to set up Redis Pub/Sub for your project – get a consultation within 1 day. Reach out to us for implementation and scaling.







