
September 07, 2026
14 min read
By Kokil Thapa | Last reviewed: September 2026
Real-time notifications, live dashboards, and chat-style UI used to mean signing up for Pusher or running a separate Node socket server. Laravel Broadcasting with Reverb Complete Setup changes that: Reverb is Laravel’s first-party WebSocket server, and it plugs directly into the broadcasting layer you already know from events, channels, and Echo. If you run Laravel for a booking portal, client dashboard, or order tracker, this is the path that keeps your stack PHP-first without sacrificing production reliability. This guide walks through a full install on Laravel 13 with PHP 8.3+, front-end wiring with Vite 8.x, and the deployment details I use on Ubuntu servers.
laravel/reverb, setting BROADCAST_CONNECTION=reverb, running php artisan reverb:start, and connecting Laravel Echo on the client with matching host, port, and app keys—then broadcasting events that implement ShouldBroadcast.What is Laravel Broadcasting with Reverb and when should you use it?
Broadcasting in Laravel lets application events reach browsers (or other clients) over WebSockets instead of polling. Reverb implements the WebSocket server side using PHP, built on Laravel’s official Reverb package and compatible with the Pusher protocol. Your app still dispatches normal Laravel events; the framework serialises them and pushes them through a connection driver—Reverb replaces Pusher, Ably, or Redis pub/sub for many teams.
You reach for Reverb when:
- Users need instant updates: order status, booking confirmations, admin alerts, document-upload progress.
- You want to avoid third-party WebSocket billing at scale (typical Pusher costs add up on high-traffic dashboards).
- Your team already deploys Laravel on Ubuntu with PHP-FPM—you can run Reverb on the same box or a small sidecar VM.
- You are building on Laravel 13.x with PHP 8.3 or 8.5 and want a supported, documented path rather than maintaining Soketi or a custom Ratchet setup.
On a legal-tech portal I built, staff needed to see new client messages without refreshing. Polling every five seconds hammered the database and felt sluggish. Moving to broadcasting with private channels cut server load and made the inbox feel instant. The same pattern applies to trek booking dashboards where guide assignments change during the day.
Reverb is not a replacement for every real-time pattern. High-frequency trading ticks, collaborative CRDT editing, or million-connection fan-out still need specialised infrastructure. For typical Laravel SaaS, admin panels, and customer portals, Reverb hits the sweet spot.
How do you install and configure Laravel Reverb for broadcasting?
Start with Laravel 13.x on PHP 8.3 or higher (PHP 8.5 is the current anchor; 8.4 remains common in production). Composer 2.10 and a queue worker are assumed—you will almost always queue broadcasts in production.
Step 1: Install Reverb and publish config
composer require laravel/reverb
php artisan reverb:install
php artisan migrate The install command adds config/reverb.php, updates .env with Reverb keys, and registers the Reverb service provider. It also prompts for broadcasting configuration.
Step 2: Environment variables
Set these in .env for local development:
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}" Match config/broadcasting.php—the default Reverb connection reads these values. Never commit real secrets; generate fresh keys per environment.
Step 3: Create a broadcastable event
php artisan make:event OrderStatusUpdated --broadcast Edit the generated event:
<?php
namespace App\Events;
use App\Models\Order;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class OrderStatusUpdated implements ShouldBroadcast
{
use Dispatchable, SerializesModels;
public function __construct(public Order $order) {}
public function broadcastOn(): array
{
return [new PrivateChannel('orders.'.$this->order->user_id)];
}
public function broadcastAs(): string
{
return 'order.status.updated';
}
public function broadcastWith(): array
{
return [
'id' => $this->order->id,
'status' => $this->order->status,
];
}
} Dispatch it from a controller or listener:
OrderStatusUpdated::dispatch($order); Step 4: Define channel authorisation
In routes/channels.php:
use Illuminate\Support\Facades\Broadcast;
Broadcast::channel('orders.{userId}', function ($user, int $userId) {
return (int) $user->id === $userId;
}); Private and presence channels require this callback. A common mistake is authorising too loosely—always tie channels to authenticated user IDs or team membership, especially on enterprise client portals.
Step 5: Enable broadcasting routes
Ensure bootstrap/app.php (Laravel 13) or your route service provider loads channel routes. Laravel 13 typically includes:
->withBroadcasting(__DIR__.'/../routes/channels.php') Step 6: Start Reverb and the queue worker
php artisan reverb:start
php artisan queue:work In production, run both under Supervisor or systemd—not in a foreground terminal. Broadcast events that implement ShouldBroadcast are queued by default; without a worker, nothing reaches Reverb.
How do you connect Laravel Echo and Vite to Reverb on the frontend?
Reverb speaks the Pusher protocol, so Laravel Echo with the Pusher client library is the standard front-end stack. Use Node.js 26 LTS and npm 12 locally; production servers often skip Node entirely if you commit built assets—same pattern as Vite config for Laravel projects.
Install JavaScript dependencies
npm install --save-dev laravel-echo pusher-js In resources/js/bootstrap.js (or your main JS entry):
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'],
authEndpoint: '/broadcasting/auth',
auth: {
headers: {
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]')?.content,
},
},
}); Import bootstrap from resources/js/app.js, then build:
npm run build Subscribe to a private channel
Echo.private(`orders.${userId}`)
.listen('.order.status.updated', (payload) => {
console.log('Order updated:', payload);
updateOrderBadge(payload);
}); Note the leading dot before the event name when you use broadcastAs(). Without it, Echo listens for the wrong event class name.
Blade + Alpine pattern
For Livewire-heavy apps, you can still use Echo in Alpine components:
<div x-data="notificationListener()" x-init="init()">
<span x-text="message"></span>
</div>
<script>
function notificationListener() {
return {
message: '',
init() {
Echo.private(`orders.${this.$el.dataset.userId}`)
.listen('.order.status.updated', (e) => {
this.message = `Order #${e.id} is now ${e.status}`;
});
}
};
}
</script> If you prefer Livewire’s built-in polling or wire:poll, compare complexity first—broadcasting shines when updates are frequent or latency-sensitive. See Laravel Livewire for beginners for when polling is enough.
Vue integration
On Vue + Laravel stacks, initialise Echo once in app.js and inject it or attach to window.Echo. A composable can wrap channel subscription and cleanup on unmount—critical to avoid duplicate listeners during SPA navigation. The Vue with Laravel setup guide covers the Vite side; add Echo after Sanctum or session auth is wired.
Testing locally
Open two browser sessions: trigger the event from one, confirm the listener fires in the other. Use php artisan tinker to dispatch:
event(new \App\Events\OrderStatusUpdated(\App\Models\Order::first())); Check Reverb logs in the terminal running reverb:start. If nothing appears, verify the queue worker processed the job and that BROADCAST_CONNECTION is not still set to log or null.
How does Laravel Reverb compare to Pusher, Soketi, and Redis broadcasting?
Pick your WebSocket backend based on ops capacity, budget, and scale—not hype.
| Option | Hosting | Protocol | Best for | Trade-offs |
|---|---|---|---|---|
| Laravel Reverb | Self-hosted PHP process | Pusher-compatible | Laravel 13 teams wanting first-party support | You manage process supervision, SSL, scaling |
| Pusher | Managed SaaS | Native Pusher | Fastest launch, minimal DevOps | Cost scales with connections/messages |
| Soketi | Self-hosted Node | Pusher-compatible | Teams already running Node services | Extra runtime beside PHP |
| Redis + socket server | Self-hosted | Custom / Laravel WebSockets legacy | Existing Redis 8.10 infra | More moving parts, older packages deprecated |
Reverb’s advantage is alignment: same vendor docs, php artisan commands, and upgrade path as the framework. Pusher remains valid when you have zero appetite for WebSocket ops—paying roughly USD 49–299/month (~Rs 6,500–39,000) for managed tiers can be cheaper than engineer time on a small project.
For related background on event-driven design, read modern Laravel architecture best practices and real-time Laravel with Reverb and WebSockets.
How do you deploy Laravel Reverb in production on Ubuntu?
In my experience working on production Laravel applications, Reverb fails in production for predictable reasons: no queue worker, wrong REVERB_HOST behind a proxy, or WebSocket connections blocked by Cloudflare orange-cloud settings. Treat Reverb like any long-running service—same discipline as Ubuntu server setup and Linux system administration.
Supervisor configuration
Create /etc/supervisor/conf.d/reverb.conf:
[program:reverb]
process_name=%(program_name)s
command=php /var/www/myapp/current/artisan reverb:start --host=0.0.0.0 --port=8080
autostart=true
autorestart=true
user=www-data
redirect_stderr=true
stdout_logfile=/var/www/myapp/shared/storage/logs/reverb.log
stopwaitsecs=3600 Reload Supervisor:
sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl start reverb Run a separate Supervisor program for queue:work or Laravel Horizon. Use Redis 8.10 as your queue backend for reliability under broadcast bursts.
Nginx reverse proxy with SSL
Terminate TLS at Nginx and proxy WebSocket upgrades to Reverb:
location /app {
proxy_pass http://127.0.0.1:8080;
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;
} Production .env:
REVERB_HOST=ws.example.com
REVERB_PORT=443
REVERB_SCHEME=https
VITE_REVERB_HOST="${REVERB_HOST}"
VITE_REVERB_PORT="${REVERB_PORT}"
VITE_REVERB_SCHEME="${REVERB_SCHEME}" Rebuild front-end assets after changing Vite variables. A classic post-deploy bug is stale JavaScript still pointing at localhost:8080.
Scaling Reverb horizontally
Reverb supports scaling via Redis pub/sub when multiple Reverb instances share message state. Set in config/reverb.php:
'scaling' => [
'enabled' => env('REVERB_SCALING_ENABLED', false),
'channel' => env('REVERB_SCALING_CHANNEL', 'reverb'),
'server' => [
'url' => env('REDIS_URL'),
'host' => env('REDIS_HOST', '127.0.0.1'),
'port' => env('REDIS_PORT', '6379'),
],
], Enable only when a single Reverb process becomes a bottleneck—most SMB Laravel apps never need this on day one.
Security hardening
- Restrict Reverb port 8080 to localhost; expose only Nginx 443 publicly.
- Use strong
REVERB_APP_SECRETand rotate on deploy if compromised. - Authorise every private and presence channel in
routes/channels.php—never trust client-side channel names alone. - Rate-limit
/broadcasting/authto prevent channel enumeration. - Keep PHP 8.3+ and Laravel 13 patched; Reverb inherits your app’s attack surface.
For API-only backends that also broadcast to mobile clients, align auth with Sanctum tokens—see Laravel API best practices and building RESTful APIs with Laravel.
Monitoring and debugging
Log Reverb stdout to a dedicated file and ship it to your log aggregator. Watch queue latency—broadcast jobs piling up means users see delayed updates even when WebSockets are healthy. Use php artisan reverb:restart after deploys; pair with PHP-FPM reload for opcache invalidation on the same release cycle I run via Deployer 7.
On client portals with document uploads, I log broadcast payload sizes. Large Eloquent models serialised by mistake can bloat messages—always use broadcastWith() to send only what the UI needs. Validate payloads with the JSON formatter tool during development.
What are advanced Laravel broadcasting patterns with Reverb?
Once basic private channels work, these patterns cover most production apps.
Presence channels for “who is online”
public function broadcastOn(): array
{
return [new PresenceChannel('chat.'.$this->roomId)];
} Authorise in routes/channels.php and return user data Echo exposes to .join(), .here(), and .leaving() callbacks.
ShouldBroadcastNow for synchronous push
Implement Illuminate\Contracts\Broadcasting\ShouldBroadcastNow when latency under ~100ms matters and the payload is tiny—admin “payment received” toasts, for example. Default queued broadcasting is safer for DB-heavy serialisation.
Notification broadcasting
Laravel notifications can use the broadcast channel. Combine with Reverb so users see bell icons update without polling—useful on digital commerce dashboards.
Testing with Pest or PHPUnit
use Illuminate\Support\Facades\Event;
Event::fake();
// action that should broadcast
Event::assertDispatched(OrderStatusUpdated::class); For integration tests, BROADCAST_CONNECTION=log avoids needing a live Reverb process in CI. Switch to reverb only in staging.
Database and performance notes
Broadcasting does not replace database design. If your event loads heavy relationships, fix the query first—PostgreSQL for Laravel developers covers indexing patterns that keep dispatch fast. Pair with testing and optimization before load-testing WebSockets.
For payment status updates after Khalti or Stripe callbacks, broadcast only after the transaction commits—dispatch from a queued listener registered after DB::commit() to prevent ghost notifications.
Key Takeaways
- Install
laravel/reverb, setBROADCAST_CONNECTION=reverb, and run bothreverb:startand a queue worker—broadcasts are queued by default. - Wire Laravel Echo with Vite env vars; rebuild assets after every production env change to WebSocket host or port.
- Authorise every private and presence channel in
routes/channels.php; usebroadcastWith()to limit payload size. - Terminate SSL at Nginx, proxy WebSocket upgrades to localhost Reverb, and manage the process with Supervisor.
- Choose Reverb over Pusher when you already operate Laravel on Ubuntu; choose Pusher when managed ops beats self-hosting cost.
- Debug with Reverb logs, queue metrics, and
Event::fake()in tests— not by leavingBROADCAST_CONNECTION=login staging.
People Also Ask
Does Laravel Reverb require Pusher?
No. Reverb is a self-hosted WebSocket server that uses the Pusher protocol for compatibility with Laravel Echo. You do not need a Pusher account or API key from Pusher’s SaaS—only the REVERB_APP_* credentials generated during reverb:install.
Can Laravel Reverb run on shared hosting?
Generally no. Shared hosting rarely allows long-running daemons or custom WebSocket ports. You need a VPS or cloud VM where Supervisor can keep reverb:start alive—typical setups start around Rs 1,500/month (~USD 11) for a small DigitalOcean or local Nepali VPS droplet.
Do I need Redis for Laravel Reverb?
Redis is not mandatory for a single-server Reverb install, but you need a queue driver—Redis 8.10 is the practical choice. Redis becomes required when you enable Reverb horizontal scaling or run multiple Reverb nodes behind a load balancer.
Is Laravel 12 supported with Reverb?
Yes. Reverb works on Laravel 12 (PHP 8.2+) and Laravel 13 (PHP 8.3+). Laravel 11 reached end-of-life in March 2026, so plan upgrades before greenfield Reverb work. Official docs live in the Laravel broadcasting documentation.
Ship real-time features with confidence
Laravel Broadcasting with Reverb Complete Setup gives you a maintainable, PHP-native WebSocket stack without renting external realtime infrastructure—provided you treat Reverb as production infrastructure: Supervisor, SSL, queues, and tight channel authorisation. Start with one private channel and a single Echo listener, prove the flow in staging, then expand to presence rooms and notification streams. If you want help wiring Reverb into a booking system, client portal, or custom Laravel application, contact us and we can map the architecture to your hosting reality.
Frequently Asked Questions
0 Comments
Leave a comment
Your email is not published. Comments appear once they have been read. Sign in to have your details filled in.

