Laravel Reverb is officially released, providing native WebSocket support without external services. Self-hosted real-time capabilities with horizontal scaling. At ZIRA Software, we've migrated projects from Pusher to Reverb with significant cost savings.
Installation
composer require laravel/reverb
php artisan reverb:install
This publishes configuration and sets up the necessary files.
Configuration
// config/reverb.php
return [
'default' => env('REVERB_SERVER', 'reverb'),
'servers' => [
'reverb' => [
'host' => env('REVERB_HOST', '0.0.0.0'),
'port' => env('REVERB_PORT', 8080),
'hostname' => env('REVERB_HOSTNAME', 'localhost'),
'options' => [
'tls' => [],
],
'scaling' => [
'enabled' => env('REVERB_SCALING_ENABLED', false),
'channel' => env('REVERB_SCALING_CHANNEL', 'reverb'),
],
'pulse_ingest_interval' => env('REVERB_PULSE_INGEST_INTERVAL', 15),
],
],
'apps' => [
'provider' => 'config',
'apps' => [
[
'key' => env('REVERB_APP_KEY', 'my-app-key'),
'secret' => env('REVERB_APP_SECRET', 'my-app-secret'),
'app_id' => env('REVERB_APP_ID', 'my-app-id'),
'options' => [
'host' => env('REVERB_HOST', '0.0.0.0'),
'port' => env('REVERB_PORT', 443),
'scheme' => env('REVERB_SCHEME', 'https'),
'useTLS' => env('REVERB_SCHEME', 'https') === 'https',
],
'allowed_origins' => ['*'],
'ping_interval' => 60,
'max_message_size' => 10_000,
],
],
],
];
# .env
BROADCAST_CONNECTION=reverb
REVERB_APP_ID=my-app-id
REVERB_APP_KEY=my-app-key
REVERB_APP_SECRET=my-app-secret
REVERB_HOST=localhost
REVERB_PORT=8080
REVERB_SCHEME=http
VITE_REVERB_APP_KEY="${REVERB_APP_KEY}"
VITE_REVERB_HOST="${REVERB_HOST}"
VITE_REVERB_PORT="${REVERB_PORT}"
VITE_REVERB_SCHEME="${REVERB_SCHEME}"
Starting the Server
# Development
php artisan reverb:start
# With verbose output
php artisan reverb:start --debug
# Production (with Supervisor)
php artisan reverb:start --host=0.0.0.0 --port=8080
# /etc/supervisor/conf.d/reverb.conf
[program:reverb]
command=php /var/www/app/artisan reverb:start --host=0.0.0.0 --port=8080
autostart=true
autorestart=true
user=www-data
redirect_stderr=true
stdout_logfile=/var/www/app/storage/logs/reverb.log
stopwaitsecs=3600
Broadcasting Events
// app/Events/MessageSent.php
class MessageSent implements ShouldBroadcast
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public function __construct(
public Message $message,
) {}
public function broadcastOn(): array
{
return [
new PrivateChannel('conversation.' . $this->message->conversation_id),
];
}
public function broadcastAs(): string
{
return 'message.sent';
}
public function broadcastWith(): array
{
return [
'id' => $this->message->id,
'content' => $this->message->content,
'user' => [
'id' => $this->message->user_id,
'name' => $this->message->user->name,
],
'sent_at' => $this->message->created_at->toISOString(),
];
}
}
// Dispatch event
broadcast(new MessageSent($message))->toOthers();
Laravel Echo Setup
// resources/js/bootstrap.js
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 ?? 80,
wssPort: import.meta.env.VITE_REVERB_PORT ?? 443,
forceTLS: (import.meta.env.VITE_REVERB_SCHEME ?? 'https') === 'https',
enabledTransports: ['ws', 'wss'],
});
Channel Authorization
// routes/channels.php
use Illuminate\Support\Facades\Broadcast;
Broadcast::channel('conversation.{conversationId}', function ($user, $conversationId) {
return $user->conversations()->where('id', $conversationId)->exists();
});
Broadcast::channel('user.{userId}', function ($user, $userId) {
return (int) $user->id === (int) $userId;
});
Presence Channels
// routes/channels.php
Broadcast::channel('chat.{roomId}', function ($user, $roomId) {
if ($user->canJoinRoom($roomId)) {
return [
'id' => $user->id,
'name' => $user->name,
'avatar' => $user->avatar_url,
];
}
});
// Frontend
Echo.join(`chat.${roomId}`)
.here((users) => {
this.onlineUsers = users;
})
.joining((user) => {
this.onlineUsers.push(user);
})
.leaving((user) => {
this.onlineUsers = this.onlineUsers.filter(u => u.id !== user.id);
})
.listen('MessageSent', (e) => {
this.messages.push(e.message);
});
Horizontal Scaling with Redis
// config/reverb.php
'scaling' => [
'enabled' => true,
'channel' => 'reverb',
],
// config/database.php - Redis connection
'redis' => [
'reverb' => [
'url' => env('REDIS_URL'),
'host' => env('REDIS_HOST', '127.0.0.1'),
'port' => env('REDIS_PORT', '6379'),
'database' => env('REDIS_REVERB_DB', '0'),
],
],
Production Deployment
# nginx.conf
upstream reverb {
server 127.0.0.1:8080;
}
server {
listen 443 ssl;
server_name ws.example.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
location / {
proxy_pass http://reverb;
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_read_timeout 60s;
proxy_send_timeout 60s;
}
}
Migration from Pusher
// Before (Pusher)
'pusher' => [
'driver' => 'pusher',
'key' => env('PUSHER_APP_KEY'),
// ...
],
// After (Reverb)
'reverb' => [
'driver' => 'reverb',
'key' => env('REVERB_APP_KEY'),
// ...
],
// Echo configuration update
// Change broadcaster from 'pusher' to 'reverb'
broadcaster: 'reverb',
Conclusion
Laravel Reverb provides native WebSocket support with easy setup and horizontal scaling. Self-hosted real-time eliminates third-party costs while maintaining full Laravel integration.
Need WebSocket implementation? Contact ZIRA Software for real-time features.