DEV Community

Cover image for Laravel Reverb + Deploynix: Real-Time WebSockets in Production
Deploynix
Deploynix

Posted on • Originally published at deploynix.io

Laravel Reverb + Deploynix: Real-Time WebSockets in Production

Real-time features transform a web application. Live notifications, chat, collaborative editing, real-time dashboards, live comments. Users expect instant updates without refreshing the page. Laravel Reverb is the official first-party WebSocket server for Laravel, and it makes building real-time features straightforward. Getting it to work locally is easy. Getting it to work reliably in production behind Nginx with SSL is where most developers hit friction.

This guide walks you through deploying Laravel Reverb on Deploynix, from server provisioning through production scaling, with special attention to the Nginx configuration challenges that trip up most teams.

What Is Laravel Reverb?

Laravel Reverb is a high-performance WebSocket server written in PHP that integrates natively with Laravel's broadcasting system. Unlike third-party services like Pusher or Ably, Reverb runs on your own infrastructure. You control your data, your costs, and your scaling.

Reverb handles persistent WebSocket connections, authenticates them through your Laravel application, and broadcasts events to connected clients using Laravel Echo on the frontend. It supports horizontal scaling through Redis pub/sub, making it suitable for applications with thousands of concurrent connections.

Server Architecture Decisions

Before provisioning, decide how Reverb fits into your infrastructure.

Option 1: Reverb on your app server.

The simplest approach. Reverb runs as a daemon on the same server as your Laravel application. This works well for small to medium applications with up to a few hundred concurrent WebSocket connections.

Option 2: Dedicated Reverb server.

For applications with heavy real-time traffic, run Reverb on a dedicated server. This isolates WebSocket connection handling from your HTTP request processing, preventing WebSocket load from affecting page load times.

Option 3: Multiple Reverb servers behind a load balancer.

For high-scale applications, run multiple Reverb instances behind a Deploynix load balancer with IP hash balancing (to ensure WebSocket connections stick to the same server). Use Redis pub/sub so events broadcast on one Reverb server are delivered to clients connected to any server.

For most Laravel applications starting with real-time features, Option 1 is the right choice. You can migrate to Options 2 or 3 as your concurrent connection count grows.

Setting Up Reverb on Deploynix

Step 1: Install Reverb

If you haven't already, install Reverb in your Laravel application:

composer require laravel/reverb
php artisan reverb:install
Enter fullscreen mode Exit fullscreen mode

This publishes the Reverb configuration file and adds the necessary environment variables to your .env.

Step 2: Configure Environment Variables

In your Deploynix dashboard, add the following environment variables to your site:

BROADCAST_CONNECTION=reverb

REVERB_APP_ID=your-app-id
REVERB_APP_KEY=your-app-key
REVERB_APP_SECRET=your-app-secret
REVERB_HOST=your-domain.com
REVERB_PORT=443
REVERB_SCHEME=https

VITE_REVERB_APP_KEY="${REVERB_APP_KEY}"
VITE_REVERB_HOST="${REVERB_HOST}"
VITE_REVERB_PORT="${REVERB_PORT}"
VITE_REVERB_SCHEME="${REVERB_SCHEME}"
Enter fullscreen mode Exit fullscreen mode

Generate secure values for REVERB_APP_ID, REVERB_APP_KEY, and REVERB_APP_SECRET. These don't need to match any external service; they're internal credentials used between your Laravel application and the Reverb server.

Step 3: Configure Reverb as a Daemon

Reverb needs to run continuously as a background process. On Deploynix, set up a daemon through the dashboard:

  • Command: php artisan reverb:start --host=0.0.0.0 --port=8080
  • Directory: Your site's root directory
  • User: deploynix

Deploynix manages the daemon with Supervisor, ensuring Reverb restarts automatically if it crashes.

The --host=0.0.0.0 flag makes Reverb listen on all interfaces (necessary for Nginx to proxy to it). The --port=8080 keeps it on a non-privileged port. Nginx will handle the public-facing port (443) and proxy WebSocket connections to Reverb.

Step 4: Configure Nginx for WebSocket Proxying

This is where most production Reverb deployments go wrong. WebSocket connections require specific Nginx configuration that differs from standard HTTP proxying.

Add the following to your Nginx site configuration. You'll need to SSH into your Deploynix server and edit the Nginx config:

# WebSocket proxy for Reverb
location /app {
    proxy_http_version 1.1;
    proxy_set_header Host $http_host;
    proxy_set_header Scheme $scheme;
    proxy_set_header SERVER_PORT $server_port;
    proxy_set_header REMOTE_ADDR $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "Upgrade";

    proxy_pass http://127.0.0.1:8080;

    # WebSocket timeout settings
    proxy_read_timeout 60s;
    proxy_send_timeout 60s;
}

# Reverb API endpoint for broadcasting
location /apps {
    proxy_http_version 1.1;
    proxy_set_header Host $http_host;
    proxy_set_header Scheme $scheme;
    proxy_set_header SERVER_PORT $server_port;
    proxy_set_header REMOTE_ADDR $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

    proxy_pass http://127.0.0.1:8080;
}
Enter fullscreen mode Exit fullscreen mode

Critical headers explained:

  • proxy_http_version 1.1: WebSocket requires HTTP/1.1 for the upgrade handshake.
  • Upgrade $http_upgrade and Connection "Upgrade": These headers tell Nginx to perform the HTTP-to-WebSocket protocol upgrade.
  • proxy_read_timeout 60s: How long Nginx waits for data from Reverb. Without this, Nginx closes idle WebSocket connections after its default timeout (60s by default, but explicitly setting it makes the behavior clear).

Step 5: Configure Laravel Echo on the Frontend

Install Laravel Echo and the Pusher JS library (Reverb uses the Pusher protocol):

npm install laravel-echo pusher-js
Enter fullscreen mode Exit fullscreen mode

Configure Echo in your JavaScript:

import Echo from 'laravel-echo';
import Pusher from 'pusher-js';

window.Pusher = Pusher;

window.Echo = new Echo({
    broadcaster: 'reverb',
    key: import.meta.env.VITE_REVERB_APP_KEY,
    wsHost: import.meta.env.VITE_REVERB_HOST,
    wsPort: import.meta.env.VITE_REVERB_PORT,
    wssPort: import.meta.env.VITE_REVERB_PORT,
    forceTLS: (import.meta.env.VITE_REVERB_SCHEME ?? 'https') === 'https',
    enabledTransports: ['ws', 'wss'],
});
Enter fullscreen mode Exit fullscreen mode

After making frontend changes, rebuild your assets. On Deploynix, you can add npm run build to your deployment hooks to automate this.

Common WebSocket Issues Behind Nginx

Connection Drops After 60 Seconds

Symptom: WebSocket connections establish successfully but disconnect after exactly 60 seconds of inactivity.

Cause: Nginx's proxy_read_timeout closes connections that haven't received data within the timeout period. WebSocket connections are long-lived and may be idle for extended periods.

Solution: Increase the timeout and implement heartbeats:

proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
Enter fullscreen mode Exit fullscreen mode

Reverb sends periodic ping frames to keep connections alive. Verify your client is responding to pings. Laravel Echo handles this automatically.

502 Bad Gateway Errors

Symptom: WebSocket connections fail with a 502 error.

Cause: Nginx can't connect to the Reverb process. Either Reverb isn't running, or it's listening on a different port than Nginx expects.

Solution:

  1. Verify Reverb is running: check the daemon status in the Deploynix dashboard.
  2. Verify the port matches between your Reverb daemon command and Nginx configuration.
  3. Check Reverb logs for startup errors.

Mixed Content Errors

Symptom: The browser console shows mixed content warnings and WebSocket connections fail.

Cause: Your page is loaded over HTTPS but the WebSocket connection is attempting to use ws:// instead of wss://.

Solution: Ensure your Reverb environment variables use https scheme and port 443. The forceTLS: true option in Echo configuration should also be set.

Connections Work Locally But Not in Production

Symptom: Everything works with php artisan reverb:start locally, but fails in production.

Cause: Usually a combination of missing Nginx proxy configuration and incorrect environment variables.

Solution: Verify each layer:

  1. Is Reverb running? Check the daemon.
  2. Can Nginx reach Reverb? Test with curl http://127.0.0.1:8080 from the server.
  3. Is Nginx proxying correctly? Check the /app location block exists.
  4. Are environment variables correct? Verify REVERB_HOST, REVERB_PORT, and REVERB_SCHEME.

Scaling Reverb

As your application grows, you may need to scale Reverb beyond a single server.

Horizontal Scaling with Redis

Configure Reverb to use Redis for pub/sub communication between multiple Reverb instances:

In your config/reverb.php, ensure the Redis pub/sub connection is configured. Then run multiple Reverb instances across different servers.

When a Laravel event is broadcast, it's published to Redis. Every Reverb instance subscribed to Redis receives the event and delivers it to their connected clients. This means a client connected to Reverb Server A receives events broadcast by a request handled by App Server B, as long as both Reverb servers share the same Redis instance.

Load Balancer Configuration

If you run multiple Reverb servers behind a Deploynix load balancer, use the IP Hash load balancing method. This ensures that a client's WebSocket connection always routes to the same Reverb server, which is necessary for the persistent connection to function correctly.

Deploynix supports Round Robin, Least Connections, and IP Hash load balancing methods. For WebSocket traffic, IP Hash is the correct choice.

Connection Limits

Each Reverb instance can handle thousands of concurrent connections, limited primarily by the server's memory and file descriptor limits. Monitor your server's resource usage through Deploynix's real-time monitoring dashboard and scale horizontally when you approach limits.

Increase the file descriptor limit if needed:

# In /etc/security/limits.conf
deploynix soft nofile 65536
deploynix hard nofile 65536
Enter fullscreen mode Exit fullscreen mode

Monitoring Connection Health

Monitor your Reverb deployment by tracking:

  • Number of active WebSocket connections.
  • Memory usage of the Reverb process.
  • Redis pub/sub lag (if using horizontal scaling).
  • Connection error rates in your Nginx error log.

Deploynix's health alerts can notify you when CPU or memory exceeds thresholds, helping you proactively scale before users experience degraded performance.

Broadcasting Events from Your Laravel Application

With Reverb configured, broadcasting events from your Laravel application follows the standard Laravel broadcasting pattern:

class OrderShipped implements ShouldBroadcastNow
{
    public function __construct(
        public Order $order,
    ) {}

    public function broadcastOn(): array
    {
        return [
            new PrivateChannel('orders.' . $this->order->user_id),
        ];
    }

    public function broadcastWith(): array
    {
        return [
            'order_id' => $this->order->id,
            'status' => $this->order->status,
            'shipped_at' => $this->order->shipped_at->toISOString(),
        ];
    }
}
Enter fullscreen mode Exit fullscreen mode

Use ShouldBroadcastNow for immediate delivery or ShouldBroadcast to dispatch through your queue. For time-sensitive events like chat messages or real-time notifications, ShouldBroadcastNow is usually the right choice.

Conclusion

Laravel Reverb brings WebSocket capabilities to Laravel without third-party dependencies or monthly fees. The challenge lies not in the code but in the production infrastructure: Nginx proxy configuration, daemon management, SSL termination, and scaling.

Deploynix simplifies most of the infrastructure work. Daemon management through the dashboard keeps Reverb running reliably. SSL certificates are provisioned automatically. Firewall rules protect the internal Reverb port. And when you outgrow a single server, Deploynix's load balancer with IP hash routing provides a clear scaling path.

Start with Reverb on your app server, get your real-time features working, and scale the infrastructure as your concurrent connection count demands it. The configuration details in this guide should get you from zero to production WebSockets without the days of debugging that typically accompany the journey.

Top comments (0)