Scaling Laravel Reverb WebSockets in Production | Mohamed Said       [Skip to content](#main)  [ ![](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MH.png) Mohamed SaidLaravel Backend Engineer ](https://www.msaied.com/public) - [Home](https://www.msaied.com/public)
- [Projects](https://www.msaied.com/public/projects)
- [Articles](https://www.msaied.com/public/articles)
- [Certificates](https://www.msaied.com/public/certificates)
- [About](https://www.msaied.com/public#about)

           [  Contact](https://www.msaied.com/public#contact) Menu 

Menu
----

Close 

 - [HomeStart here](https://www.msaied.com/public)
- [ProjectsCase studies](https://www.msaied.com/public/projects)
- [ArticlesEngineering notes](https://www.msaied.com/public/articles)
- [CertificatesCredentials](https://www.msaied.com/public/certificates)
- [AboutHow I work](https://www.msaied.com/public#about)
- [ContactGet in touch](https://www.msaied.com/public#contact)

  [Start a conversation](https://www.msaied.com/public#contact) [WhatsApp](https://wa.me/201094619204) [Email](mailto:hello@msaied.com) 

 1. [Home](https://www.msaied.com/public)
2. /
3. [Articles](https://www.msaied.com/public/articles)
4. /
5. Laravel Reverb in Production: Scaling WebSockets Beyond a Single Server

 Laravel Reverb in Production: Scaling WebSockets Beyond a Single Server
========================================================================

 Reverb ships as a first-party WebSocket server for Laravel, but running it on a single node won't cut it under real load. Here's how to scale it horizontally with Redis pub/sub, sticky sessions, and proper health checks.

 ![](https://cdn.msaied.com/01M22N44A70A5MC2S599JP0MPH.webp) [Mohamed Said](https://www.msaied.com/public#person) Published 15 Jun 2026 · Updated 15 Jun 2026 · 4 min read

ShareCopy linkCopied

 ![Laravel Reverb in Production: Scaling WebSockets Beyond a Single Server](https://cdn.msaied.com/180/b3f362d3b1f7da03af9a87a26ffcbdf6.png) 

  On this page +1. [Why Reverb Needs Special Attention in Production](#why-reverb-needs-special-attention-in-production)
2. [The Core Problem: In-Memory Channel State](#the-core-problem-in-memory-channel-state)
3. [Solution: Redis as the Pub/Sub Backbone](#solution-redis-as-the-pubsub-backbone)
4. [Nginx: Sticky Sessions Are Non-Negotiable](#nginx-sticky-sessions-are-non-negotiable)
5. [Supervisor: Keeping Reverb Alive](#supervisor-keeping-reverb-alive)
6. [Health Checks and Graceful Deploys](#health-checks-and-graceful-deploys)
7. [Takeaways](#takeaways)

 Why Reverb Needs Special Attention in Production
------------------------------------------------

Laravel Reverb is a native PHP WebSocket server built on ReactPHP's event loop. It handles long-lived TCP connections, which means the usual stateless assumptions you make about HTTP workers no longer apply. A single Reverb process holds connection state in memory. The moment you add a second node, clients on node A can't receive events published by a job running on node B — unless you wire up a shared pub/sub backend.

This article covers the exact configuration needed to run Reverb reliably across multiple nodes.

---

The Core Problem: In-Memory Channel State
-----------------------------------------

By default, Reverb tracks which WebSocket connections are subscribed to which channels entirely in the current process's memory. When a queued job calls:

```php
broadcast(new OrderShipped($order));

```

Laravel publishes the event to the configured broadcasting driver. If that driver is `reverb`, the event hits **one** Reverb node via HTTP. Any client connected to a **different** node never sees it.

---

Solution: Redis as the Pub/Sub Backbone
---------------------------------------

Reverb ships with a Redis scaling driver. Enable it in `config/reverb.php`:

```php
'servers' => [
    'reverb' => [
        'driver' => 'reverb',
        'key'    => env('REVERB_APP_KEY'),
        // ...
        'scaling' => [
            'enabled' => true,
            'driver'  => 'redis',
            'connection' => 'default', // your redis connection name
        ],
    ],
],

```

With this enabled, every Reverb node subscribes to a shared Redis channel. When node A receives a broadcast, it publishes to Redis; node B picks it up and fans the message out to its own connected clients.

> **Important:** Use a dedicated Redis connection for Reverb scaling — not the same one your cache or queue uses. Connection contention under load will cause missed messages.

---

Nginx: Sticky Sessions Are Non-Negotiable
-----------------------------------------

WebSocket upgrades require the same TCP connection to persist for the lifetime of the session. You must configure your load balancer to route a given client to the same upstream for the duration of the connection.

With Nginx and `ip_hash`:

```nginx
upstream reverb_nodes {
    ip_hash;
    server 10.0.1.10:8080;
    server 10.0.1.11:8080;
    server 10.0.1.12:8080;
}

server {
    listen 443 ssl;
    server_name ws.example.com;

    location / {
        proxy_pass http://reverb_nodes;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "Upgrade";
        proxy_set_header Host $host;
        proxy_read_timeout 3600s;
    }
}

```

`ip_hash` is a blunt instrument — prefer cookie-based stickiness (`sticky` module) or an AWS ALB with stickiness enabled if you need finer control.

---

Supervisor: Keeping Reverb Alive
--------------------------------

Reverb is a long-running process. Supervisor is the simplest way to manage it:

```ini
[program:reverb]
command=php /var/www/artisan reverb:start --host=0.0.0.0 --port=8080
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
user=www-data
stdout_logfile=/var/log/reverb.log
stderr_logfile=/var/log/reverb-error.log

```

Set `autorestart=true` and monitor the log for `ReactPHP` loop exceptions — an unhandled exception in a coroutine can silently kill the loop without crashing the process.

---

Health Checks and Graceful Deploys
----------------------------------

Reverb exposes no built-in HTTP health endpoint, but you can add a lightweight TCP check. For zero-downtime deploys:

1. Start new Reverb processes on the new release.
2. Drain old connections by removing old nodes from the upstream pool.
3. Wait for `proxy_read_timeout` to expire naturally, or send `SIGTERM` and let existing connections close.

Avoid `SIGKILL` — it drops all open WebSocket connections immediately.

---

Takeaways
---------

- Enable Reverb's Redis scaling driver on any multi-node deployment; in-memory state does not survive across processes.
- Use a dedicated Redis connection for Reverb to avoid contention with cache and queue workloads.
- Configure sticky sessions at the load balancer — WebSocket upgrades require connection affinity.
- Run Reverb under Supervisor with `autorestart=true` and monitor for silent event-loop failures.
- Plan graceful draining during deploys; `SIGKILL` drops all live connections instantly.

- [laravel](https://www.msaied.com/public/articles?search=laravel)
- [reverb](https://www.msaied.com/public/articles?search=reverb)
- [websockets](https://www.msaied.com/public/articles?search=websockets)
- [redis](https://www.msaied.com/public/articles?search=redis)
- [broadcasting](https://www.msaied.com/public/articles?search=broadcasting)

 Frequently asked questions 
---------------------------

  Do I need Redis if I only run a single Reverb node?No. With a single node, all connection state lives in one process and broadcasts reach every connected client. Redis scaling is only required when you run two or more Reverb processes, whether on the same machine or different servers.

   Can I run Reverb under Laravel Octane for better performance?Reverb already runs its own ReactPHP event loop and is not designed to run inside an Octane worker. They are separate long-running processes. Run Reverb with its own Supervisor program alongside your Octane workers.

   How do I monitor the number of active WebSocket connections per node?Reverb does not expose Prometheus metrics out of the box. You can query the Reverb HTTP API (enabled in config) for connection counts, or push custom metrics from within a scheduled command that calls the Reverb manager to count active channels.

   ![Mohamed Said](https://cdn.msaied.com/01M22N44A70A5MC2S599JP0MPH.webp)About the author
----------------

[Mohamed Said](https://www.msaied.com/public#person)Senior Backend Engineer specializing in Laravel, scalable SaaS platforms, APIs, and cloud infrastructure. I build secure, high-performance web applications that help businesses grow.

[About](https://www.msaied.com/public#about) [GitHub ↗](https://github.com/EG-Mohamed) [LinkedIn ↗](https://www.linkedin.com/in/msaiedm/) [WhatsApp ↗](https://wa.me/201094619204) [Email Address ↗](mailto:hello@msaied.com) [My CV ↗](https://drive.google.com/file/u/0/d/1MF20IPRJyzfy32mhEutjL5EpSls0w2Q8/view)  

   [Previous articleFilament v4 Custom Field Plugins: Building Reusable Schema Components](https://www.msaied.com/public/articles/filament-v4-custom-field-plugins-building-reusable-schema-components) [Next articleLaraOwl: Self-Hosted Monitoring for Laravel Applications](https://www.msaied.com/public/articles/laraowl-self-hosted-monitoring-for-laravel-applications)  

   On this page
-------------

1. [Why Reverb Needs Special Attention in Production](#why-reverb-needs-special-attention-in-production)
2. [The Core Problem: In-Memory Channel State](#the-core-problem-in-memory-channel-state)
3. [Solution: Redis as the Pub/Sub Backbone](#solution-redis-as-the-pubsub-backbone)
4. [Nginx: Sticky Sessions Are Non-Negotiable](#nginx-sticky-sessions-are-non-negotiable)
5. [Supervisor: Keeping Reverb Alive](#supervisor-keeping-reverb-alive)
6. [Health Checks and Graceful Deploys](#health-checks-and-graceful-deploys)
7. [Takeaways](#takeaways)

 ###  Have a technical challenge?

 Tell me what you’re building. I reply within two working days.

[Start a conversation](https://www.msaied.com/public#contact) 

   Related articles
-----------------

 [ ![](https://cdn.msaied.com/740/cce86edc21eddcbdd2f2454fadaf9c70.png)  · 3 min read### The Pipeline Pattern in Laravel: Custom Pipelines Beyond Middleware

5 Oct 2026 ](https://www.msaied.com/public/articles/the-pipeline-pattern-in-laravel-custom-pipelines-beyond-middleware-1) [ ![](https://cdn.msaied.com/739/2d6897fdcdcf090613f96f72a64b8a78.png)  · 4 min read### MySQL Full-Text Search in Laravel: Indexes, Relevance Scoring, and Boolean Mode

4 Oct 2026 ](https://www.msaied.com/public/articles/mysql-full-text-search-in-laravel-indexes-relevance-scoring-and-boolean-mode) [ ![](https://cdn.msaied.com/738/073696a3fefe18bec825beec5ac658f5.png)  · 4 min read### Laravel Queue Rate-Limited Middleware: Throttling Jobs Without Losing Work

4 Oct 2026 ](https://www.msaied.com/public/articles/laravel-queue-rate-limited-middleware-throttling-jobs-without-losing-work) 

  Have a technical challenge?
----------------------------

Tell me what you’re building. I reply within two working days.

 [Discuss your project ↗](https://www.msaied.com/public#contact) 

  © 2026 Mohamed Said · Built with Laravel, meant to last.Senior Backend Engineer specializing in Laravel, scalable SaaS platforms, APIs, and cloud infrastructure. I build secure, high-performance web applications that help businesses grow.

 - [Home](https://www.msaied.com/public)
- [Articles](https://www.msaied.com/public/articles)
- [Certificates](https://www.msaied.com/public/certificates)
- [GitHub](https://github.com/EG-Mohamed)
- [LinkedIn](https://www.linkedin.com/in/msaiedm/)
- [WhatsApp](https://wa.me/201094619204)
- [Email Address](mailto:hello@msaied.com)
- [My CV](https://drive.google.com/file/u/0/d/1MF20IPRJyzfy32mhEutjL5EpSls0w2Q8/view)
- [Sitemap](https://www.msaied.com/public/sitemap.xml)
