Skip to content

Scaling real-time — via Redis adapter, not in-memory

Use a Redis adapter so WebSocket and SSE events reach clients on any app instance.

nestjs-realtime-transport-redis-adapter-scaling

Why it matters

Failure modes if this rule is ignored
StakeIf ignored
Can't scale
  • Without it, user A is on instance 1, user B on instance 2 — they don't see each other.
  • Room broadcasts only reach clients on the same process — multi-instance fan-out silently fails.
  • In-memory SSE buffers — resume lands on another node and the stream starts over or 404s.

How to fix

The moment you have more than one server instance, you can't keep connections in process memory. Redis (or similar) is the broker that fans events between instances. After wiring, server.to(roomId).emit(...) reaches every instance and every user in the room.

For SSE resumable streams, the same Redis (or similar) broker lets instance B serve a GET .../stream resume when instance A handled the original POST /stream. That satisfies this rule for cross-instance relay — it does not require keeping the LLM alive after disconnect. Relay + abort-on-idle is valid.

Examples

Bad
ts
const ioAdapter = new IoAdapter(app);
app.useWebSocketAdapter(ioAdapter);
Good
ts
// apps/api/src/main.ts
import { IoAdapter } from '@nestjs/platform-socket.io';
import { createAdapter } from '@socket.io/redis-adapter';

const app = await NestFactory.create(AppModule);
const pubClient = new Redis(config.REDIS_URL);
const subClient = pubClient.duplicate();

const ioAdapter = new IoAdapter(app);
(ioAdapter as any).createIOServer = (port: number, options: any) => {
  const server = new Server(port, options);
  server.adapter(createAdapter(pubClient, subClient));
  return server;
};
app.useWebSocketAdapter(ioAdapter);

Contribute

Released under the MIT License.

esc