Building Real-Time Laravel Apps with Reverb in 2026
Laravel Reverb is the first-party WebSocket server built for Laravel. Learn how to set it up, broadcast events, and scale to production.
Real-time features used to mean one of two things for Laravel developers: pay for Pusher, or operate a custom Node.js server alongside your PHP application. Laravel Reverb, the framework’s first-party WebSocket server, ended that trade-off. It is self-hosted, built in PHP, integrates natively with the Laravel broadcasting system, and in its current form it handles everything from a simple development environment to horizontally scaled production clusters on Laravel Cloud.
This is a practical look at how Reverb works, how to get it wired up, and the production details that matter once you are past the “hello world” stage.
What Reverb Actually Is
Reverb is a WebSocket server that speaks the Pusher protocol. That single design decision gives it immediate compatibility with Laravel Echo (the JavaScript broadcasting library) and any existing Pusher-compatible client libraries, without requiring changes to your frontend code if you were previously using Pusher.
Under the hood, Reverb runs on ReactPHP’s event loop, which means it handles thousands of concurrent WebSocket connections within a single PHP process without the overhead of forking a new process per connection. It shares the same codebase, environment, and service container as your Laravel application, which simplifies deployment significantly compared to running a separate Node server.
Installation
Reverb ships as a first-party package. If you are starting a new Laravel 12 or 13 project with the broadcasting stack, you may already have it. For an existing application:
composer require laravel/reverb
php artisan reverb:install
The install command publishes a config/reverb.php file and adds the necessary environment variables to your .env:
REVERB_APP_ID=your-app-id
REVERB_APP_KEY=your-app-key
REVERB_APP_SECRET=your-app-secret
REVERB_HOST=localhost
REVERB_PORT=8080
REVERB_SCHEME=http
BROADCAST_CONNECTION=reverb
Then start the server:
php artisan reverb:start
For local development, that is everything you need. Reverb listens on port 8080 and Laravel’s broadcasting system routes events to it automatically.
Broadcasting an Event
Reverb does not change how you write broadcastable events. The Laravel broadcasting API stays identical. Here is a standard event:
<?php
namespace App\Events;
use App\Models\Order;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class OrderStatusUpdated implements ShouldBroadcast
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public function __construct(public Order $order) {}
public function broadcastOn(): Channel
{
return new Channel('orders.' . $this->order->id);
}
public function broadcastWith(): array
{
return [
'order_id' => $this->order->id,
'status' => $this->order->status,
'updated_at' => $this->order->updated_at->toISOString(),
];
}
}
Dispatch it from anywhere in your application:
OrderStatusUpdated::dispatch($order);
Reverb receives the broadcast, and any subscribed WebSocket clients get the payload immediately. No polling. No delay between the database write and the client update.
Listening on the Frontend
Laravel Echo handles the client side. Install it alongside the Pusher JS client (which Echo uses for the Pusher protocol under the hood):
npm install laravel-echo pusher-js
Configure Echo to point at your Reverb server instead of Pusher’s infrastructure:
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 ?? 8080,
wssPort: import.meta.env.VITE_REVERB_PORT ?? 443,
forceTLS: (import.meta.env.VITE_REVERB_SCHEME ?? 'https') === 'https',
enabledTransports: ['ws', 'wss'],
});
Then listen for your event:
Echo.channel('orders.' + orderId)
.listen('OrderStatusUpdated', (event) => {
console.log('Order updated:', event.status);
updateOrderUI(event);
});
Private channels (authenticated via your Laravel application’s auth rules) work identically, just swap channel() for private().
Private and Presence Channels
For user-specific data, use a private channel. Reverb authenticates these by making a request to your application’s /broadcasting/auth endpoint, which uses your existing Laravel auth guards. No additional configuration is required.
public function broadcastOn(): PrivateChannel
{
return new PrivateChannel('user.' . $this->user->id);
}
Presence channels go a step further: they track which users are currently subscribed, making them useful for features like “who is currently viewing this document” or live collaboration indicators.
Echo.join('document.' + documentId)
.here((users) => {
// Called with current subscriber list
renderOnlineUsers(users);
})
.joining((user) => {
addUserToPresenceList(user);
})
.leaving((user) => {
removeUserFromPresenceList(user);
});
The Database Driver (Laravel 13)
One addition worth calling out for teams running Laravel 13: a native database driver for the broadcasting connection backend. Previously, Reverb required Redis for the connection channel pub/sub layer in most configurations. The database driver means you can run a full Reverb setup against your existing MySQL or PostgreSQL database with no additional infrastructure.
For small-to-medium applications, this significantly reduces the operational surface. You are not managing a Redis cluster, not paying for managed Redis, and not adding a new failure point to your stack. The database driver handles channel subscriptions and message routing through your existing connection.
For high-traffic applications, Redis remains the better choice. The database driver trades throughput for simplicity. Know your connection volume before choosing.
Horizontal Scaling
When a single Reverb server is not enough, you scale horizontally using Redis as a shared pub/sub backbone. Add the configuration to config/reverb.php:
'scaling' => [
'driver' => 'redis',
'connection' => 'default',
],
With Redis in place, multiple Reverb processes can share the connection and channel state. A load balancer in front of them distributes WebSocket connections across the pool. Laravel Cloud automates this entirely: its managed WebSocket clusters run on Reverb with horizontal scaling built in, and the team reports pricing roughly 50% below comparable Pusher plans for equivalent connection volumes.
Production Checklist
A few things that catch teams by surprise on the first production deploy:
Process management: Reverb runs as a long-lived process. Use Supervisor or a systemd unit file to keep it running and restart it on failure. The artisan command is just the entry point.
Proxy configuration: If Nginx sits in front of Reverb, you need to pass the Upgrade and Connection headers for WebSocket upgrades to work:
location /app {
proxy_pass http://localhost:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_set_header Host $host;
}
Queue workers: Events are dispatched synchronously by default, but if your broadcastable events implement ShouldBroadcastNow you skip the queue. For most production setups, routing broadcast jobs through the queue is preferable because it avoids slowing down the HTTP request that triggered the event.
TLS: In production, WebSocket connections should run over WSS (WebSocket Secure). Terminate TLS at Nginx or your load balancer, then proxy to Reverb over plain WS internally.
Worth the Switch
If you are currently paying for Pusher or running a Node.js WebSocket server alongside your Laravel application, Reverb is worth evaluating seriously. The integration with Laravel’s auth system, the shared configuration, and the fact that it speaks the Pusher protocol (meaning your existing Echo code needs minimal changes) make the migration path low-friction. The Laravel Cloud managed option removes the operational overhead entirely for teams that do not want to run their own process management.
Real-time features are no longer a specialty add-on. With Reverb, they are just another part of the standard Laravel stack.