skip to content

Reverb WebSocket Server

Reverb is Laravel's own WebSocket server: a long-running Artisan process that speaks the Pusher protocol and scales out over Redis. Interviewers probe proxying, open-file limits and restarts.

on this pageshow

explore

questions

5

What is Laravel Reverb, how does it sit between a Laravel app and Echo, and why does it speak the Pusher protocol?

level: juniorimportance: must knowfreq 38%

answer

  1. a long-running Artisan process
  2. reverb:start on 0.0.0.0:8080
  3. Pusher protocol, pusher-js client
  4. the reverb driver is the Pusher driver
  5. key is public, secret signs

basics

~20 s

Reverb is Laravel's first-party WebSocket server, run as a long-lived php artisan reverb:start process. It implements the Pusher protocol, so Laravel broadcasts to it through the Pusher driver and browsers connect with Echo and pusher-js, identified by REVERB_APP_KEY.

solid answer

~40 s

Reverb is a WebSocket server written in PHP on a ReactPHP event loop and started with `php artisan reverb:start`, which binds `0.0.0.0:8080` by default and keeps running like a queue worker. It sits in the middle: the Laravel app sends each broadcast to Reverb's HTTP API at `/apps/{appId}/events`, and Reverb pushes it to the browsers connected at `/app/{appKey}`. Because it implements the **Pusher Channels protocol**, nothing new is needed on either side: the framework's `reverb` broadcast driver is the Pusher driver pointed at your own host, and Echo uses `broadcaster: 'reverb'` with `pusher-js`. Three credentials tie them together: `REVERB_APP_ID`, `REVERB_APP_KEY` (public, it is in the browser's URL) and `REVERB_APP_SECRET` (private, it signs the app's API calls and channel authorizations). `allowed_origins` in `config/reverb.php`, `['*']` by default, limits which sites' pages may connect.

code

ini · 13 lines
ini
BROADCAST_CONNECTION=reverb

REVERB_APP_ID=482113
REVERB_APP_KEY=whiteboard-key
REVERB_APP_SECRET=change-me
REVERB_HOST="localhost"
REVERB_PORT=8080
REVERB_SCHEME=http

VITE_REVERB_APP_KEY="${REVERB_APP_KEY}"
VITE_REVERB_HOST="${REVERB_HOST}"
VITE_REVERB_PORT="${REVERB_PORT}"
VITE_REVERB_SCHEME="${REVERB_SCHEME}"

go deeper

for a junior

Recall that Reverb is a WebSocket server started with reverb:start and that Echo connects to it using the Pusher protocol.

for a middle

Explain the two paths through Reverb, the HTTP API from the app and the WebSocket from the browser, and which credential each uses.

for a senior

Treat the secret as a broadcast credential, restrict allowed_origins, and plan Reverb as its own long-running service beside web and queue processes.

for a principal

Weigh running Reverb yourself against a hosted Pusher-protocol service: connection volume, operations effort, failure handling and the lock-in each avoids.

## What Reverb is **Laravel Reverb** is a first-party WebSocket server for Laravel applications. Instead of paying a hosted service to hold browser connections, you run the server yourself as part of the application: - it is a Composer package (`laravel/reverb`), installed by `php artisan install:broadcasting` with the Reverb option or by `reverb:install` - it runs as a **long-lived Artisan process**, `php artisan reverb:start`, built on a ReactPHP event loop, so one PHP process holds many connections - by default it binds `0.0.0.0:8080`; `--host`, `--port`, `--hostname` and `--path` override the config, and `--debug` prints the traffic For a collaborative whiteboard, Reverb is what keeps every participant's browser connected, so a stroke drawn by one user appears on the others' canvases immediately. ## Where it sits Broadcasting has three parties, and Reverb is the one in the middle: 1. The **Laravel app** dispatches a broadcast event. The broadcast job calls Reverb's **HTTP API** (`POST /apps/{appId}/events`), signed with the app secret. 2. **Reverb** receives it and pushes the message to every connection subscribed to the channel. 3. **Browsers** hold a WebSocket open to Reverb at **`/app/{appKey}`** and receive the message through Echo. Private and presence subscriptions still go through the Laravel app: Echo asks `/broadcasting/auth`, the app signs the subscription with the secret, and Reverb verifies the signature. ## Why the Pusher protocol Reverb implements the **Pusher Channels protocol**, the message format and HTTP API that the hosted Pusher service uses. That choice is what makes it cheap to adopt: - on the server, the framework's `reverb` broadcast driver is literally the Pusher driver pointed at your own host, using the Pusher PHP SDK - on the client, Echo's `reverb` broadcaster uses the `pusher-js` library - moving between hosted Pusher and Reverb is mostly a change of `BROADCAST_CONNECTION` and connection settings; the events, channels and Echo listeners stay the same | Piece | Talks to Reverb over | Configured by | |---|---|---| | Laravel app (broadcast driver) | HTTP API at `/apps/...` | `config/broadcasting.php`: `REVERB_APP_ID`, `REVERB_APP_KEY`, `REVERB_APP_SECRET`, `REVERB_HOST`, `REVERB_PORT`, `REVERB_SCHEME` | | Browser (Echo + pusher-js) | WebSocket at `/app/{key}` | `VITE_REVERB_APP_KEY`, `VITE_REVERB_HOST`, `VITE_REVERB_PORT`, `VITE_REVERB_SCHEME` | | Reverb server | — | `config/reverb.php`: bind address, apps, scaling | ## The credentials - **`REVERB_APP_ID`** names the application in API URLs. - **`REVERB_APP_KEY`** is **public**: it appears in the WebSocket URL every browser opens, and Reverb uses it to find the application. Treat it as an identifier, not a password. - **`REVERB_APP_SECRET`** is **private**: it signs the app's API requests and the channel authorizations. Anyone holding it can broadcast to your users or forge subscriptions. One Reverb installation can serve several applications by listing more entries under `apps` in `config/reverb.php`, each with its own credentials. ## Restricting origins Each app entry has an `allowed_origins` list. The shipped config uses `['*']`, which accepts a connection from any page. Setting it to your hosts, such as `['whiteboard.example.com']` (wildcard patterns like `*.example.com` are accepted), makes Reverb reject browser connections whose `Origin` host is not listed. It is a browser-facing control; it does not replace channel authorization. ## Hooks into the server's life Reverb dispatches its own Laravel events while it runs, such as `Laravel\Reverb\Events\ChannelCreated` when the first client subscribes to a channel, `ChannelRemoved` when the last one leaves, `ConnectionPruned` for stale connections, and `MessageReceived` / `MessageSent` for traffic. They are dispatched inside the Reverb process, so listeners for them run there and should stay fast; a slow listener blocks the event loop for every connection. ## Common misconceptions - Reverb is not a queue worker; broadcast jobs still need `queue:work` in the app - Reverb does not run inside PHP-FPM requests; it is its own process - the app key is not a secret, the app secret is

  • Can an app that used hosted Pusher Channels move to Reverb without changing its events or Echo listeners?
    Largely yes. Reverb speaks the same protocol, so events, channels and `Broadcast::channel` callbacks stay. You install Reverb, switch `BROADCAST_CONNECTION` to `reverb`, set the `REVERB_*` credentials and host, and point Echo at `broadcaster: 'reverb'` with the Reverb host and key.
  • Is it a problem that REVERB_APP_KEY appears in the browser's JavaScript?
    No. The key only identifies which application a connection belongs to, and it is in the WebSocket URL by design. What must stay private is `REVERB_APP_SECRET`, which signs broadcasts and channel authorizations; leaking it lets anyone push messages to your users.

saying these in an interview costs you the question

  • REVERB_APP_KEY must be kept secret because it lets clients broadcast.
  • Reverb runs inside the normal PHP-FPM request cycle.
  • Reverb needs its own client library instead of pusher-js.
  • With Reverb installed, broadcast events no longer need a queue worker.
  • The shipped allowed_origins setting already restricts connections to the app's domain.
open as a page

After a deploy, a Laravel Reverb server keeps running old code; why, and how do reverb:restart and a process manager fix it?

level: middleimportance: should knowfreq 28%

basics

~20 s

Reverb is a long-running process that keeps the code it booted with. reverb:restart stores a timestamp in the cache; each server notices it, disconnects clients gracefully and exits, and a process manager such as Supervisor starts it with the new code.

open as a page

When deploying Laravel Reverb behind Nginx on port 443, what is the difference between REVERB_SERVER_HOST/PORT and REVERB_HOST/PORT, and what must the proxy forward?

level: middleimportance: should knowfreq 30%

basics

~20 s

REVERB_SERVER_HOST/PORT are where the Reverb process binds, such as 0.0.0.0:8080. REVERB_HOST/PORT/SCHEME are the public address the app sends broadcasts to and Echo connects to, such as ws.example.com:443 over https. The proxy must upgrade /app WebSockets and forward /apps API calls.

open as a page

A Laravel Reverb server for a collaborative whiteboard stops accepting connections at about a thousand users; what limits cause this, and how do you raise them?

level: seniorimportance: should knowfreq 22%

basics

~20 s

Each WebSocket is an open file. Reverb's default ReactPHP loop uses stream_select, capped around 1,024 files, so install ext-uv, which Reverb uses automatically. Then raise the open-file limits everywhere: ulimit/limits.conf, Supervisor's minfds and Nginx's worker_rlimit_nofile and worker_connections.

open as a page

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%

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.

open as a page