Laravel Reverb: The Smarter Way to Go Real-Time

Laravel Reverb — Build Real-Time Features in Laravel Without Third-Party Services

The Story That Made Me Ditch Pusher Forever

Laravel Reverb changed the way I build real-time features — and honestly, it made me a little angry. Angry that I spent months paying Pusher $100+ every month for something Laravel now gives you completely free, right out of the box. If you’re building real-time apps — chat, live notifications, dashboards — and you’re still paying a third-party service for WebSockets, this post is going to save you money starting today.

Twelve hours after discovering Reverb, Pusher was gone from our stack. Our real-time features still worked perfectly. Our next invoice? $0 for WebSockets.

Let’s fix your stack. 👇

First, Let Me Show You What Real-Time Actually Feels Like

Imagine you’re using a project management tool — like Trello or Asana.

You add a comment. Your teammate is on the same screen. Instead of them having to press F5 to see what you wrote… it just appears. Instantly. Like magic.

That magic is WebSockets. And building it used to mean either:

  • Paying Pusher or Ably every single month
  • Wiring up a janky community package and praying it doesn’t break on deployment
  • Writing your own WebSocket server from scratch (please don’t)

Laravel Reverb is none of those things. It’s the official, first-party WebSocket server built by the Laravel team — and it ships free with Laravel 11.

What Exactly Is Laravel Reverb?

Think of Reverb as your own private Pusher — except it runs on your server, costs nothing extra, and integrates with Laravel so smoothly it feels like it was always there. (Because now it is.)

Here’s the simple version:

Laravel Reverb is a WebSocket server that you run alongside your Laravel app. It lets your app push data to users in real time — instantly, without polling, without refreshing.

Before Reverb, this is what the Laravel real-time landscape looked like:

OptionSelf-hosted?CostOfficial Support
Pusher❌ No💸 Paid tiersPartial
Ably❌ No💸 Paid tiersPartial
Laravel WebSockets (community)✅ YesFreeCommunity only
Laravel Reverb✅ YesFree✅ First-party

Reverb wins the table. But the real win is how painless it is to use. It speaks the same language as the rest of Laravel’s ecosystem — which means your existing channels.php, your broadcasting config, and your Laravel Echo setup all just… work.

Why You Should Actually Care About This

Look, I get it. Every week there’s a shiny new package promising to change your life. Most of them don’t.

Reverb is different. Here’s why it genuinely matters:

No More Monthly Bills Just for WebSockets

Pusher’s free tier gives you 200 concurrent connections. Sounds fine — until you actually launch and real users show up. Then you’re suddenly on a paid plan that scales with your growth. With Reverb, you own the server. Your cost is your hosting, which you’re already paying.

Your Data Stays on Your Server

Every message, every event, every real-time ping — with Pusher, it all flows through their servers. With Reverb, everything stays in your infrastructure. For apps in healthcare, finance, or anywhere with privacy requirements, this is a big deal.

You Control Everything

Need a custom auth logic for private channels? Go ahead. Want to rate-limit specific events? Do it. Need to log every WebSocket connection for debugging? It’s your code — make it do whatever you need.

It Fits Like a Glove

Because it’s first-party Laravel, you don’t have to fight it. The config feels familiar. The commands feel familiar. If you’ve touched Laravel broadcasting before, you’ll be productive with Reverb in under an hour.

How It Works — The Simple Version

Don’t worry, no CS degree required here. Here’s the mental model:

Old way (HTTP): Your browser asks → Server answers → Connection closes. Like texting someone and waiting for a reply.

New way (WebSockets / Reverb): Your browser connects once → The connection stays open → Server can push data anytime. Like a phone call where both sides can talk whenever they want.

Here’s the flow in a Laravel Reverb app:

PHP
1. Something happens in your app
   (user sends a message, payment completes, data changes)

2. Laravel fires a "broadcast event"
   (your PHP code says "hey, tell everyone on this channel what happened")

3. Reverb gets the message and pushes it out
   (the WebSocket server instantly sends it to all connected browsers)

4. Laravel Echo catches it on the frontend
   (your JavaScript reacts — updates the UI, shows a notification, appends data)

That’s it. Four steps. The whole thing.

Let’s Set It Up — Step by Step

Alright, enough theory. Let’s build.

I’ll assume you have a fresh Laravel 11 app. If you’re migrating from Pusher, the steps are basically the same — you’ll just swap out your .env values at the end.

Step 1: Install Reverb

PHP
composer require laravel/reverb
php artisan reverb:install

The reverb:install command is smart. It:

  • Publishes the Reverb config file
  • Switches your broadcasting driver to reverb
  • Pre-fills your .env with all the keys you need

You’re already halfway done.

Step 2: Check Your .env File

After the install command runs, open your .env and make sure these lines are there:

PHP
# The magic switch — tells Laravel to use Reverb
BROADCAST_CONNECTION=reverb

# Reverb server config (auto-generated, you can change these)
REVERB_APP_ID=my-app
REVERB_APP_KEY=my-app-key
REVERB_APP_SECRET=my-app-secret
REVERB_HOST=localhost
REVERB_PORT=8080
REVERB_SCHEME=http

# These feed into your frontend (Vite reads VITE_ prefixed variables)
VITE_REVERB_APP_KEY="${REVERB_APP_KEY}"
VITE_REVERB_HOST="${REVERB_HOST}"
VITE_REVERB_PORT="${REVERB_PORT}"
VITE_REVERB_SCHEME="${REVERB_SCHEME}"

Nothing scary here. The REVERB_APP_KEY and REVERB_APP_SECRET are just for authenticating connections — they’re not your API key for some external service.

Step 3: Start the WebSocket Server

PHP
php artisan reverb:start

You’ll see output like:

PHP
Starting server on 0.0.0.0:8080

That’s your WebSocket server running. Keep this terminal open while you develop. In production, you’ll use Supervisor to keep it running permanently (more on that later).

Step 4: Install the Frontend Libraries

PHP
npm install --save-dev laravel-echo pusher-js

Wait — pusher-js? Don’t panic. Reverb uses the Pusher protocol under the hood, so the client library is the same. You still never have to pay Pusher. This is just the browser-side JS package.

Building a Real-Time Chat — Full Working Example

Let’s build something you can actually use. A live chat where messages appear instantly for every user — no refreshing, no polling.

Part 1: Create the Event

Run this command to generate an event class:

PHP
php artisan make:event MessageSent

Now open app/Events/MessageSent.php and make it look like this:

PHP
<?php

namespace App\Events;

use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;

class MessageSent implements ShouldBroadcast
{
    use Dispatchable, InteractsWithSockets, SerializesModels;

    public function __construct(
        public readonly string $message,
        public readonly string $username,
    ) {}

    public function broadcastOn(): array
    {
        // This is the "room" — all users listening to 'chat' will get this
        return [new Channel('chat')];
    }

    public function broadcastAs(): string
    {
        // Custom event name — makes the frontend listener cleaner
        return 'message.sent';
    }
}

The key thing here: implements ShouldBroadcast. That one interface tells Laravel “when this event fires, don’t just handle it quietly — broadcast it over WebSockets too.”

Part 2: Fire the Event from a Controller

PHP
<?php

namespace App\Http\Controllers;

use App\Events\MessageSent;
use Illuminate\Http\Request;

class ChatController extends Controller
{
    public function send(Request $request)
    {
        $validated = $request->validate([
            'message' => 'required|string|max:500',
        ]);

        // This single line does the broadcasting magic
        MessageSent::dispatch(
            message: $validated['message'],
            username: auth()->user()->name,
        );

        return response()->json(['status' => 'Message sent!']);
    }
}

And add a route in routes/web.php:

PHP
Route::post('/chat/send', [ChatController::class, 'send'])->middleware('auth');

Part 3: Configure Laravel Echo on the Frontend

Open resources/js/bootstrap.js and add this:

PHP
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,
    wssPort: import.meta.env.VITE_REVERB_PORT,
    forceTLS: (import.meta.env.VITE_REVERB_SCHEME ?? 'https') === 'https',
    enabledTransports: ['ws', 'wss'],
});

Part 4: Listen for Messages in Your JavaScript

Now, in any JS file (or a Vue/React component), listen for the event:

PHP
// Connect to the 'chat' channel and wait for 'message.sent' events
window.Echo.channel('chat')
    .listen('.message.sent', (event) => {
        console.log('New message!', event);

        // Append the new message to your chat UI
        const chatBox = document.getElementById('chat-messages');
        const newMessage = document.createElement('div');
        newMessage.classList.add('message');
        newMessage.innerHTML = `
            <strong>${event.username}</strong>: ${event.message}
        `;
        chatBox.appendChild(newMessage);
        chatBox.scrollTop = chatBox.scrollHeight; // scroll to bottom
    });

Note the dot before .message.sent — when you use broadcastAs() to define a custom event name, you need to prefix it with a . in the Echo listener. Easy to forget, worth noting.

That’s the whole thing. User types a message → controller fires → Reverb broadcasts → Echo listens → UI updates. All in real time. All on your server.

My Honest Experience With Laravel Reverb

I’ve been using Reverb in production for a client project since it launched. Here’s the real, unfiltered take.

What surprised me: The migration from Pusher literally took one afternoon. I expected two days of debugging. Most of the time was spent double-checking config values, not actually fixing broken things.

What tripped me up:

  • Supervisor setup in production. The reverb:start command needs to run as a persistent background process. If you’ve never set up Supervisor before, set aside 30 minutes. It’s not hard, but it’s not automatic either.
  • Nginx WebSocket proxying. Routing WebSocket traffic through Nginx requires specific config. The official Laravel docs cover it, but I missed it the first time and spent an hour wondering why production wasn’t working.
  • The . prefix on custom event names. I mentioned it above. I forgot it. Three times. Learn from my pain.

What I genuinely love: Debugging is so much nicer when the WebSocket server is yours. You can add logging, inspect connections, and understand exactly what’s happening. With Pusher, you’re partially flying blind.

Would I go back to Pusher? Not unless a client specifically asks for it. Reverb does everything I need, and my infrastructure bill is cleaner for it.

When Should You Use Laravel Reverb?

Here’s a simple checklist. If any of these sounds like your app, Reverb is for you:

  • 💬 Live chat — support chat, team messaging, anything where messages need to appear instantly
  • 🔔 Real-time notifications — “John just commented on your post” appearing without a page refresh
  • 📊 Live dashboards — analytics, order counts, sales numbers updating as they happen
  • 🛒 Order/delivery tracking — “Your order is being prepared → Out for delivery → Arrived”
  • 👥 Collaborative tools — multiple users editing or viewing the same data simultaneously
  • 📡 Live feeds — stock prices, sports scores, social media streams
  • 🎮 Multiplayer features — turn-based games, live voting, audience polls

If users in your app are hitting refresh to see new data, that’s your cue. That pain point is exactly what Reverb is built to solve.

Quick Production Checklist

Before you ship Reverb to production, make sure you’ve done these:

  • Set up Supervisor to keep reverb:start running continuously
  • Configure Nginx to proxy WebSocket connections (check the official Laravel deployment docs)
  • Switch REVERB_SCHEME to https and use wss:// for SSL
  • If running multiple app servers, add Redis as the pub/sub backend for Reverb
  • Test reconnection behavior — what happens when a user’s internet drops briefly?

This One Is a No-Brainer

Here’s the honest truth: Laravel Reverb isn’t a revolutionary new concept. WebSockets have been around for years. Self-hosted solutions existed before.

But what Reverb does is take something that used to be annoying, expensive, or both — and makes it normal. First-party, well-documented, actively maintained, free. It fits into the way you already build Laravel apps.

If you’re building anything with Laravel that could benefit from real-time updates — and honestly, most apps can — there’s no reason to pay a third-party service for what you can now own yourself.

Try it on your next project. You’ll wonder why you ever paid for Pusher.


Enjoyed this post? Follow along for more practical Laravel deep-dives — new posts every week covering real patterns, real code, and real lessons from production.

Found an error or have a question? Drop it in the comments — I read every one.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *