Skip to content

Laravel Reverb in Production: Scaling WebSockets, Auth Channels, and Presence at Load

Reverb ships as Laravel's first-party WebSocket server. This guide covers production deployment, horizontal scaling with Redis, private/presence channel auth, and the subtle gotchas that only appear under real traffic.

Published · Updated · 4 min read

Laravel Reverb in Production: Scaling WebSockets, Auth Channels, and Presence at Load

Why Reverb Changes the WebSocket Story for Laravel

Before Reverb, running WebSockets in Laravel meant operating a separate Node.js process (Pusher-compatible servers like Soketi or Laravel Echo Server) alongside your PHP app. Reverb collapses that into a single PHP binary, shares your existing service container, and integrates natively with Laravel's broadcasting system. That simplicity is real — but production deployments surface a set of concerns that the getting-started docs gloss over.


Deployment Architecture

Reverb runs as a long-lived Ratchet/ReactPHP process. It is not a traditional PHP-FPM request; it is a persistent server. That changes how you deploy it.

# Start Reverb (production)
php artisan reverb:start --host=0.0.0.0 --port=8080 --env=production

Manage it with Supervisor so it restarts on failure:

[program:reverb]
command=php /var/www/html/artisan reverb:start --host=0.0.0.0 --port=8080
autostart=true
autorestart=true
stopwaitsecs=10
user=www-data
stdout_logfile=/var/log/reverb.log

Nginx proxies WebSocket upgrades to Reverb while your normal FPM traffic continues on port 80/443:

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

Horizontal Scaling with Redis

A single Reverb process is bound to one machine. When you scale to multiple app servers, a client connected to server A won't receive events broadcast from server B. The fix is the Redis scaling driver, which uses Pub/Sub to fan out events across all Reverb nodes.

// config/reverb.php
'scaling' => [
    'driver' => 'redis',
    'connection' => 'default', // your Redis connection from database.php
],

Each Reverb node subscribes to the same Redis channel. When your application fires a broadcast event, any node can pick it up and push it to the connected clients it owns. This requires sticky sessions at your load balancer (or a session-affinity cookie) so that the WebSocket handshake and subsequent frames always hit the same node.

Gotcha: If you skip sticky sessions, the HTTP-based channel auth endpoint (/broadcasting/auth) may be handled by a different server than the one holding the WebSocket connection. The auth will succeed, but Reverb on the other node won't have the subscription registered. Always pair Redis scaling with L4/L7 sticky routing.


Private and Presence Channel Auth

Reverb reuses Laravel's existing BroadcastServiceProvider and channel definitions:

// routes/channels.php
Broadcast::channel('orders.{orderId}', function (User $user, int $orderId) {
    return $user->can('view', Order::findOrFail($orderId));
});

Broadcast::channel('presence.team.{teamId}', function (User $user, int $teamId) {
    if ($user->belongsToTeam($teamId)) {
        return ['id' => $user->id, 'name' => $user->name];
    }
});

Presence channels return an array on success; that payload becomes the member data visible to all subscribers. Returning false or null denies access.

On the frontend with Echo:

window.Echo.join(`presence.team.${teamId}`)
    .here(members => console.log('Online:', members))
    .joining(member => console.log('Joined:', member.name))
    .leaving(member => console.log('Left:', member.name));

Connection Limits and Tuning

Reverb exposes connection limits per application in config/reverb.php. Under load, the bottleneck is usually open file descriptors, not CPU. Raise the OS limit:

# /etc/security/limits.conf
www-data soft nofile 65535
www-data hard nofile 65535

Also tune the max_connections and max_request_size settings:

'apps' => [[
    'id' => env('REVERB_APP_ID'),
    'key' => env('REVERB_APP_KEY'),
    'secret' => env('REVERB_APP_SECRET'),
    'options' => [
        'host' => env('REVERB_HOST', '0.0.0.0'),
        'port' => env('REVERB_PORT', 8080),
        'max_message_size' => 10_000, // bytes
    ],
]],

Key Takeaways

  • Reverb is a persistent PHP process — manage it with Supervisor, not FPM.
  • Use the Redis scaling driver for multi-node deployments; pair it with sticky sessions at the load balancer.
  • Channel auth runs through your normal Laravel HTTP stack — policies and gates work as expected.
  • Presence channel member data is the array returned from the channel callback, not a separate API call.
  • Raise OS file descriptor limits before you hit connection ceilings in production.
  • max_message_size is your first line of defense against oversized payloads crashing the event loop.

Frequently asked questions

Can Reverb replace Pusher entirely, including the client-side SDK?

Yes. Reverb implements the Pusher protocol, so Laravel Echo works unchanged. You only update the broadcaster config to point at your Reverb host instead of Pusher's servers. No client-side code changes are required.

Does Reverb support SSL termination directly?

The recommended approach is to terminate TLS at Nginx or a load balancer and proxy plain WebSocket traffic to Reverb on a local port. Reverb can handle TLS natively via its `tls` options, but offloading to a reverse proxy is simpler to certificate-manage in production.

What happens to connected clients during a Reverb restart or deployment?

Existing WebSocket connections are dropped when the process restarts. Echo's reconnection logic will re-establish them automatically within a few seconds. For zero-downtime deployments, run two Reverb processes behind the load balancer and drain one before restarting it.

Mohamed Said

About the author

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 GitHub LinkedIn WhatsApp Email Address My CV

Have a technical challenge?

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

Discuss your project