Production WebSocket deployments require careful planning for reliability and scale. Laravel Reverb simplifies real-time features but needs proper infrastructure. At ZIRA Software, our Reverb deployments handle thousands of concurrent connections.
Production Architecture
┌─────────────────┐
│ Load Balancer │
│ (nginx) │
└────────┬────────┘
│
┌────────────────┼────────────────┐
│ │ │
▼ ▼ ▼
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
│ Reverb #1 │ │ Reverb #2 │ │ Reverb #3 │
└───────┬───────┘ └───────┬───────┘ └───────┬───────┘
│ │ │
└────────────────┼────────────────┘
│
┌────────▼────────┐
│ Redis │
│ (pub/sub) │
└─────────────────┘
Environment Configuration
# .env (Production)
BROADCAST_CONNECTION=reverb
REVERB_APP_ID=your-app-id
REVERB_APP_KEY=your-secure-key
REVERB_APP_SECRET=your-secure-secret
# Server settings
REVERB_HOST=0.0.0.0
REVERB_PORT=8080
REVERB_HOSTNAME=ws.yourdomain.com
REVERB_SCHEME=https
# Scaling with Redis
REVERB_SCALING_ENABLED=true
REVERB_SCALING_CHANNEL=reverb
# Connection limits
REVERB_MAX_REQUEST_SIZE=10000
REVERB_PING_INTERVAL=60
# Frontend
VITE_REVERB_APP_KEY="${REVERB_APP_KEY}"
VITE_REVERB_HOST="ws.yourdomain.com"
VITE_REVERB_PORT=443
VITE_REVERB_SCHEME=https
Nginx Configuration
# /etc/nginx/sites-available/reverb
upstream reverb_servers {
# Sticky sessions for WebSocket connections
ip_hash;
server 127.0.0.1:8080;
server 127.0.0.1:8081;
server 127.0.0.1:8082;
}
server {
listen 443 ssl http2;
server_name ws.yourdomain.com;
ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
location / {
proxy_pass http://reverb_servers;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# Timeouts for long-lived connections
proxy_read_timeout 86400s;
proxy_send_timeout 86400s;
proxy_connect_timeout 60s;
# Buffer settings
proxy_buffering off;
proxy_buffer_size 4k;
}
# Health check endpoint
location /health {
proxy_pass http://reverb_servers/health;
proxy_http_version 1.1;
}
}
Supervisor Configuration
# /etc/supervisor/conf.d/reverb.conf
[program:reverb-1]
command=php /var/www/app/artisan reverb:start --host=127.0.0.1 --port=8080
directory=/var/www/app
autostart=true
autorestart=true
user=www-data
redirect_stderr=true
stdout_logfile=/var/www/app/storage/logs/reverb-1.log
stopwaitsecs=3600
stopsignal=SIGTERM
[program:reverb-2]
command=php /var/www/app/artisan reverb:start --host=127.0.0.1 --port=8081
directory=/var/www/app
autostart=true
autorestart=true
user=www-data
redirect_stderr=true
stdout_logfile=/var/www/app/storage/logs/reverb-2.log
stopwaitsecs=3600
stopsignal=SIGTERM
[group:reverb]
programs=reverb-1,reverb-2
priority=999
Redis Scaling Configuration
// config/reverb.php
'servers' => [
'reverb' => [
'host' => env('REVERB_HOST', '0.0.0.0'),
'port' => env('REVERB_PORT', 8080),
'scaling' => [
'enabled' => env('REVERB_SCALING_ENABLED', true),
'channel' => env('REVERB_SCALING_CHANNEL', 'reverb'),
'server' => [
'url' => env('REDIS_URL'),
'host' => env('REDIS_HOST', '127.0.0.1'),
'port' => env('REDIS_PORT', 6379),
'database' => env('REDIS_REVERB_DB', 0),
],
],
],
],
Monitoring with Pulse
// config/reverb.php
'pulse_ingest_interval' => env('REVERB_PULSE_INGEST_INTERVAL', 15),
// Dashboard shows:
// - Connected clients
// - Messages per second
// - Memory usage
// - Connection errors
Health Checks
// routes/api.php
Route::get('/reverb/health', function () {
$redis = Redis::connection('reverb');
try {
$redis->ping();
$redisStatus = 'connected';
} catch (Exception $e) {
$redisStatus = 'disconnected';
}
return response()->json([
'status' => 'healthy',
'redis' => $redisStatus,
'timestamp' => now()->toISOString(),
]);
});
Graceful Deployment
#!/bin/bash
# deploy-reverb.sh
# Graceful restart - one at a time
supervisorctl stop reverb-1
sleep 5
git pull
composer install --no-dev
php artisan config:cache
supervisorctl start reverb-1
# Wait for connections to migrate
sleep 30
supervisorctl stop reverb-2
supervisorctl start reverb-2
Client Reconnection Strategy
// resources/js/echo.js
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: true,
enabledTransports: ['ws', 'wss'],
// Reconnection settings
enableLogging: false,
reconnectAttempts: 10,
reconnectDelay: 3000,
});
// Handle disconnection
window.Echo.connector.pusher.connection.bind('disconnected', () => {
console.log('WebSocket disconnected, attempting reconnect...');
});
window.Echo.connector.pusher.connection.bind('connected', () => {
console.log('WebSocket connected');
// Re-subscribe to channels if needed
});
Rate Limiting Connections
// app/Providers/AppServiceProvider.php
use Laravel\Reverb\Contracts\ConnectionManager;
public function boot(): void
{
// Limit connections per IP
app(ConnectionManager::class)->limitConnectionsPerIp(100);
}
Conclusion
Production Laravel Reverb requires proper scaling, monitoring, and failover strategies. Redis pub/sub enables horizontal scaling while nginx handles SSL termination and load balancing.
Deploying real-time features? Contact ZIRA Software for WebSocket infrastructure.