skip to content

How do you run Laravel Reverb on several servers behind a load balancer, and what does REVERB_SCALING_ENABLED change?

level: seniorimportance: nice to knowfreq 18%

answer

  1. one server only knows its own sockets
  2. REVERB_SCALING_ENABLED=true
  3. Redis pub/sub between servers
  4. the REDIS_* scaling server block
  5. load balancer spreads connections

basics

~20 s

Set REVERB_SCALING_ENABLED=true on every Reverb server, point them at one shared Redis server, and put them behind a load balancer. Each server then republishes messages it receives through Redis pub/sub, so every server delivers them to its own local connections.

solid answer

~40 s

A single Reverb server only knows the connections it holds. Behind a load balancer, a broadcast posted by the app lands on one server, while the whiteboard users on that board are spread across all of them. `REVERB_SCALING_ENABLED=true` makes each `reverb:start` connect to Redis: a server that receives a message **publishes** it on a pub/sub channel (`reverb` by default, `REVERB_SCALING_CHANNEL`), and every server **subscribes** and delivers it to its own subscribers. The Redis server comes from the `scaling.server` block of `config/reverb.php`, filled from the `REDIS_*` variables, and all servers must share it. Presence membership and connection metrics also travel through Redis in this mode. The load balancer must pass WebSocket upgrades and accept `/apps` API calls on any server. Run the Pulse `pulse:check` daemon on only one Reverb server.

code

ini · 6 lines
ini
REVERB_SCALING_ENABLED=true
REVERB_SCALING_CHANNEL=reverb

REDIS_HOST=10.0.0.20
REDIS_PORT=6379
REDIS_PASSWORD=change-me

go deeper

for a junior

Recall that REVERB_SCALING_ENABLED lets several Reverb servers share messages through Redis.

for a middle

Explain why a broadcast reaches only one server without scaling and how publish/subscribe through Redis fixes it.

for a senior

Configure a shared Redis, a WebSocket-capable load balancer and single-instance Pulse collection, and plan for Redis being on the delivery path.

for a principal

Decide when to scale out versus up, how much redundancy the realtime tier needs, and whether a managed Reverb or Pusher-protocol service is cheaper to operate.

## Why one server is not enough, and why two are not trivially better A single Reverb server eventually runs out of file descriptors, memory or CPU. Adding a second server behind a load balancer solves capacity but creates a new problem: each server only knows about the connections **it** holds. Consider a collaborative whiteboard with 3,000 users on one board, spread across three Reverb servers: - the Laravel app broadcasts `StrokeAdded` by posting to Reverb's HTTP API - the load balancer sends that request to server B - without scaling, server B delivers the stroke to its thousand users, and the two thousand on servers A and C never see it ## What REVERB_SCALING_ENABLED does Setting `REVERB_SCALING_ENABLED=true` turns on Reverb's Redis-based fan-out: 1. At start-up, each `reverb:start` process connects to Redis and **subscribes** to a pub/sub channel, named `reverb` unless `REVERB_SCALING_CHANNEL` says otherwise. 2. When a server receives a message, from the app's HTTP API or from a client, it **publishes** it to that channel instead of only handling it locally. 3. Every server, including the one that published, receives the message from Redis and delivers it to the matching local connections. The same channel carries other cross-server work: presence channel membership, so `here` and `joining` see members on every server; connection and channel metrics; and terminating a user's connections. ## Configuration | Setting | Where | Notes | |---|---|---| | `REVERB_SCALING_ENABLED` | `.env`, read by `config/reverb.php` | `false` by default; set on every Reverb server | | `REVERB_SCALING_CHANNEL` | `.env` | pub/sub channel name, `reverb` by default | | `REDIS_URL`, `REDIS_HOST`, `REDIS_PORT`, `REDIS_PASSWORD`, `REDIS_DB` | `.env` | fill the `scaling.server` block of `config/reverb.php` | Every Reverb server must point at the **same, central** Redis server. Two groups of Reverb servers on different Redis instances behave like two unconnected clusters. ## The load balancer - it must support WebSocket upgrades and keep each connection pinned to the server that accepted it for its lifetime, which happens naturally for a long-lived TCP connection - the app's broadcasts to `/apps/{appId}/events` can go to **any** server, because Redis spreads them - health checks can use Reverb's `/up` route - idle timeouts on the balancer must be longer than the interval at which Reverb and its clients exchange keep-alive pings (`ping_interval` and `activity_timeout` in `config/reverb.php`), or quiet connections are cut ## What stays the same - the Laravel app's broadcasting config still points at one host name, now the load balancer - Echo clients connect to the same public host - `reverb:restart` still works, as long as all servers share a cache store ## Operational notes - **Pulse**: Reverb's connection and message recorders rely on the `pulse:check` daemon; in a scaled setup run it on only one Reverb server. - **Redis is now on the delivery path**: if it is unavailable, cross-server delivery stops even though each server is up. Monitor it like any other critical dependency. - **Delivery semantics** are those of Redis pub/sub: a server disconnected from Redis misses what is published meanwhile, so clients should be able to resynchronise, for example by reloading the board's state after a reconnect. - **Capacity**: every message passes through Redis once and is received by every Reverb server, so very chatty boards add Redis and network load proportional to the number of servers. ## When to scale out Raise per-server limits first (the event loop, open files, the proxy), then add servers when one machine's CPU or connection count is the bottleneck. Scaling out also gives redundancy: losing one server drops only its share of users, who reconnect through the load balancer to the others.

  • With two Reverb servers and scaling disabled, some whiteboard users see strokes and others do not; why?
    Each broadcast reaches only the server that received the app's API call, and that server only delivers to its own connections. Users connected to the other server never get the message. Enabling `REVERB_SCALING_ENABLED` on both, with a shared Redis, makes every server receive and deliver every message.
  • Does the Laravel app need to know how many Reverb servers exist?
    No. The app keeps posting broadcasts to one host name, the load balancer, which forwards each call to any Reverb server; Redis then spreads the message to the rest. Adding or removing servers changes nothing in the app's broadcasting config.

saying these in an interview costs you the question

  • A load balancer alone makes every Reverb server see every broadcast.
  • The Laravel app must post each broadcast to every Reverb server itself.
  • Each Reverb server can use its own local Redis instance when scaling is on.
  • With scaling on, Redis stores missed messages for servers that reconnect.
  • Presence channels only list the members on the server a user is connected to, even with scaling on.