Laravel 13 + Reverb: Real-Time WebSockets Without Paying for Pusher
Reverb, Laravel's native WebSocket server, speaks the Pusher protocol: with Laravel 13 you can build live notifications and dashboards in pure PHP, with no Node.js and no paid services. This guide walks through installation, a working example, and production deployment.
What Reverb Is and Why It Matters in 2026
Reverb is Laravel's first-party WebSocket server, built on ReactPHP and launched in 2024. Its key trait: it speaks the Pusher protocol, which makes it a drop-in replacement for Laravel's broadcasting system and Echo on the frontend, with no third-party dependency at all. Since its launch it has become the default choice for most Laravel teams, and with Laravel 13, released on March 17, 2026, the stack is complete: a real-time server that runs in the same language as your application.
The Pusher Protocol and Echo Integration
The key to the ecosystem is that Reverb implements the Pusher protocol. That means Laravel Echo, the client library you already use to listen for events, works unchanged: you only swap the server URL. You do not need to adapt your events or your channels either: Laravel's broadcasting system publishes the events and Reverb distributes them to subscribed clients. The learning curve is practically zero if you have worked with broadcasting before.
Reverb vs. Pusher and Soketi
The 2026 comparison is straightforward. Pusher is a hosted service with excellent developer experience and flawless integration, but its cost grows with the number of connections and channels, and at scale it starts to weigh on the bill. Soketi is a self-hosted alternative that also speaks the Pusher protocol, but it requires running a separate Node.js process, one more piece of infrastructure to operate. Reverb gets the best of both: it runs entirely in PHP, needs no external services, and its cost is that of your own server.
Requirements and Installation with Laravel 13
You need Laravel 11 or newer (Laravel 13 works perfectly) and PHP 8.2 or later. Installation is done with Composer in two steps:
composer require laravel/reverb
php artisan reverb:installThe installer publishes the configuration and adds the required environment variables. Reverb needs its own app id, key, and secret, separate from your application's APP_KEY, to authenticate connections. They end up in your .env file like this:
BROADCAST_CONNECTION=reverb
REVERB_APP_ID=local-app-id
REVERB_APP_KEY=local-app-key
REVERB_APP_SECRET=local-app-secret
REVERB_HOST=localhost
REVERB_PORT=8080
REVERB_SERVER_HOST=0.0.0.0
REVERB_SERVER_PORT=8080It is worth understanding the difference between the two groups of variables: REVERB_SERVER_HOST and REVERB_SERVER_PORT tell you where the Reverb server listens, while REVERB_HOST and REVERB_PORT are the address Laravel and clients use to connect to it. In development both match; in production they point to different domains or ports, as you will see later.
Broadcasting: From Event to Browser
The real-time flow in Laravel is always the same: an event is fired on the server, broadcasting publishes it to a channel, and clients subscribed to that channel receive it instantly. Reverb only handles the delivery: it receives the event from Laravel and pushes it to the correct WebSocket connections.
Setting BROADCAST_CONNECTION=reverb
The BROADCAST_CONNECTION=reverb variable tells Laravel which driver to use for publishing events. With that in place, any event implementing ShouldBroadcast is automatically sent to Reverb, and from there to clients. Nothing else needs to change in your broadcasting configuration.
A Sample Event and Its Private Channel
Imagine a store that notifies customers when their order changes status. The event is defined like this:
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
class OrderShipped implements ShouldBroadcast
{
public function __construct(public Order $order) {}
public function broadcastOn(): PrivateChannel
{
return new PrivateChannel('orders.' . $this->order->id);
}
}Private channels require authorization: the server must verify that the user is allowed to listen to that channel. The rule is declared in routes/channels.php:
use Illuminate\Support\Facades\Broadcast;
Broadcast::channel('orders.{orderId}', function (User $user, int $orderId) {
return $user->id === Order::findOrFail($orderId)->user_id;
});If the function returns true, the subscription is allowed; if false, it is rejected. It is the same mechanism as with Pusher, so migrating an existing codebase takes minutes.
Hands-On: Live Notifications
With the event and authorization in place, the frontend only needs Laravel Echo subscribed to the channel. In Echo's bootstrap file you change the broadcaster configuration to point at Reverb:
import Echo from 'laravel-echo';
import Pusher from 'pusher-js';
window.Echo = new Echo({
broadcaster: 'pusher',
key: import.meta.env.VITE_REVERB_APP_KEY,
wsHost: import.meta.env.VITE_REVERB_HOST,
wsPort: import.meta.env.VITE_REVERB_PORT,
forceTLS: false,
encrypted: true,
disableStats: true,
});Because Reverb speaks the Pusher protocol, the broadcaster in Echo stays 'pusher': only the URL and key change. Subscribing and listening to the event are identical to what you already know:
Echo.private('orders.' + orderId)
.listen('OrderShipped', (e) => {
toast(`Your order ${e.order.id} is on its way`);
});That is the complete live notification example: an event on the server, an authorization rule, and a subscription on the client. The same pattern works for chats, real-time dashboard metrics, or live sports scoreboards.
Production: Supervisor, Redis, and Horizontal Scaling
Taking Reverb to production is more of an operations exercise than a coding one. These are the three fronts to cover.
Keeping Reverb Alive with Supervisor
Reverb is a long-running process that must restart if it crashes or if the server reboots. The standard way is Supervisor, the Linux process manager. A minimal configuration file:
[program:reverb]
command=php /home/forge/example.com/artisan reverb:start --host=0.0.0.0 --port=8080
directory=/home/forge/example.com
autostart=true
autorestart=true
redirect_stderr=true
stdout_logfile=/home/forge/example.com/storage/logs/reverb.log
user=forgeYou should also raise the process's open-files limit (ulimit): every open WebSocket connection consumes a file descriptor, and the default limit on many systems (1024) runs out quickly once you have real traffic. A single Reverb node supports on the order of tens of thousands of simultaneous connections; the practical limit is free ports and memory.
Scaling with Redis Pub/Sub and Sticky Sessions
When one process is not enough, Reverb scales horizontally with Redis pub/sub: all nodes subscribe to the same Redis channel and forward events to each other, so a client connected to any node receives messages published from any other. In the .env, REVERB_SERVER_HOST becomes the load balancer's internal host and REVERB_HOST the public URL, usually behind a proxy with WebSocket support. There is an important nuance: the load balancer must use sticky sessions, because a WebSocket connection stays tied to the node it was established with; if the balancer forwards it to another node, the connection breaks. Presence channels, which track who is online, also depend on Redis to keep state shared between nodes.
Monitoring with Laravel Pulse
Laravel Pulse, Laravel's monitoring dashboard, includes a Reverb card showing active connections, messages sent, and the server's memory usage in real time. It is the most direct way to spot connection spikes, struggling nodes, or memory leaks before they affect users. With the card on the dashboard and Supervisor alerts configured, your real-time system is monitored without extra tooling.
Conclusion
Reverb turns real-time into an accessible feature for any Laravel team: no paid services, no Node.js processes, just PHP and the infrastructure you already have. Start with the notification example in this guide and, once it is in production, scale with Redis and Supervisor confidently. If you enjoyed this, keep reading the blog for more Laravel and web development tutorials.
