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
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}"
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;
}
Critical headers explained:
-
proxy_http_version 1.1: WebSocket requires HTTP/1.1 for the upgrade handshake. -
Upgrade $http_upgradeandConnection "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
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'],
});
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;
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:
- Verify Reverb is running: check the daemon status in the Deploynix dashboard.
- Verify the port matches between your Reverb daemon command and Nginx configuration.
- 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:
- Is Reverb running? Check the daemon.
- Can Nginx reach Reverb? Test with
curl http://127.0.0.1:8080from the server. - Is Nginx proxying correctly? Check the
/applocation block exists. - Are environment variables correct? Verify
REVERB_HOST,REVERB_PORT, andREVERB_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
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(),
];
}
}
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)