Kokil Thapa - Professional Web Developer in Nepal
Freelancer Web Developer in Nepal with 15+ Years of Experience

Kokil Thapa is an experienced full-stack web developer focused on building fast, secure, and scalable web applications. He helps businesses and individuals create SEO-friendly, user-focused digital platforms designed for long-term growth.

Real-Time Laravel with Reverb and WebSockets

By Kokil Thapa | Last reviewed: September 2026

Your users expect live updates, but polling adds latency and server load. A laravel reverb websocket stack solves that by pushing events from your app to browsers over a persistent connection. Laravel Reverb is the first-party WebSocket server for Laravel 13.x. It replaces paid SaaS drivers and separate Node.js daemons while staying compatible with Laravel Echo and the Pusher protocol. If you run Laravel applications in production, Reverb is now the default path for chat, notifications, booking status, and dashboard counters.

This guide walks through install, frontend wiring, production deployment, scaling, and debugging. It assumes PHP 8.3 or higher, Laravel 12 or 13, and a reverse proxy you control. For broader backend context, see my notes on building SaaS products with Laravel in Nepal and when real-time features belong in the MVP versus a later phase.

How do you install and configure Laravel Reverb for WebSocket broadcasting?

Reverb ships as a Composer package and hooks into Laravel's existing broadcasting layer. You do not rewrite event classes or channel definitions when moving from Pusher. The install command publishes config and environment keys in one step.

Backend install steps

  1. Require the package: composer require laravel/reverb using Composer 2.10 on PHP 8.3 or higher.
  2. Run the installer: php artisan reverb:install. This publishes config/reverb.php, updates config/broadcasting.php, and adds keys to .env.
  3. Set the driver: BROADCAST_CONNECTION=reverb in .env.
  4. Create a broadcastable event that implements ShouldBroadcast and dispatch it from a controller, job, or observer.
  5. Start the server locally: php artisan reverb:start. Production uses Supervisor instead of a terminal session.
# .env — backend Reverb settings
BROADCAST_CONNECTION=reverb
REVERB_APP_ID=my-app-id
REVERB_APP_KEY=my-app-key
REVERB_APP_SECRET=my-app-secret
REVERB_HOST=0.0.0.0
REVERB_PORT=8080
REVERB_SCHEME=http

# Vite exposes these to the browser bundle
VITE_REVERB_APP_KEY="${REVERB_APP_KEY}"
VITE_REVERB_HOST="ws.example.com"
VITE_REVERB_PORT="443"
VITE_REVERB_SCHEME="https"

Bind REVERB_HOST to 0.0.0.0 on the server, not localhost. Localhost accepts only loopback traffic and breaks connections through Nginx. Treat REVERB_APP_SECRET like any API secret. Never commit it to Git.

Define channels in routes/channels.php. Public channels need no auth. Private and presence channels call /broadcasting/auth before Reverb accepts the subscription. That endpoint must share session cookies with your main app domain or subdomain.

BrowserEcho + ViteWSS :443NginxSSL + UpgradeProxy :8080ReverbWebSocketPHP event loopLaravelHTTP + JobsBroadcastsLaravel Reverb WebSocket FlowEvent fires in Laravel → Reverb pushes to subscribed clientsPrivate channels auth via /broadcasting/auth first
End-to-end laravel reverb websocket architecture with SSL termination at Nginx

Official docs live at Laravel Broadcasting and the Reverb server documentation. Read both before changing production config.

How does Laravel Reverb compare to Pusher and Socket.io?

Reverb is not the only option. Pusher remains the fastest path if you want zero ops. Socket.io fits teams already running Node.js. For most Laravel shops, Reverb hits the sweet spot between cost and control.

CriteriaLaravel ReverbPusherSocket.io (Node)
Monthly costSelf-hosted (Rs 0 beyond VPS)USD subscription tiersSelf-hosted
Laravel integrationNative driver, same eventsNative driverCustom bridge required
ProtocolPusher-compatiblePusher proprietarySocket.io protocol
Data residencyYour server, your regionVendor regions onlyYour server
Ops burdenSupervisor + Nginx + Redis at scaleNoneNode cluster + adapter
Frontend clientLaravel Echo + pusher-jsLaravel Echo + pusher-jssocket.io-client

Reverb speaks the Pusher protocol. Switch drivers by changing BROADCAST_CONNECTION and env keys. Frontend Echo config stays the same. That matters when you prototype on Pusher and move to self-hosted Reverb later.

For legal-tech and client portals, keeping WebSocket traffic on your own VPS is often a hard requirement. SaaS broadcasters may not meet data-handling policies. On booking systems like those I have shipped with Livewire, live seat counts and status badges are low-risk wins for Reverb. See Adventure Third Pole Trek for the kind of operational dashboard that benefits from push updates.

If you only need one-way server pushes without bidirectional chat, read Server-Sent Events versus WebSockets before committing to a full WebSocket stack.

How do you connect Laravel Echo and Vite to Reverb?

The browser side defines whether real-time features feel instant or broken. Laravel Echo is the official client. Vite 8.x bundles it in modern Laravel apps. Reverb still requires pusher-js because it implements the Pusher wire format.

Frontend setup checklist

  • Install packages: npm install laravel-echo pusher-js using npm 12 with Node.js 26 LTS.
  • Create resources/js/echo.js and import it from resources/js/app.js before any channel subscriptions.
  • Match VITE_REVERB_* values to your public WebSocket hostname, not the internal server IP.
  • Use wss on port 443 in production so corporate firewalls do not block the connection.
  • Test with a public channel first, then private channels that hit /broadcasting/auth.
// resources/js/echo.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 ?? 8080,
    wssPort: import.meta.env.VITE_REVERB_PORT ?? 443,
    forceTLS: (import.meta.env.VITE_REVERB_SCHEME ?? 'https') === 'https',
    enabledTransports: ['ws', 'wss'],
});

// Subscribe example
Echo.channel('orders')
    .listen('OrderStatusUpdated', (e) => {
        console.log(e.orderId, e.status);
    });

During local dev, Vite serves assets on port 5173 while Reverb listens on 8080. Add the Vite origin to config/cors.php if auth requests fail. In production, serve the app and WebSocket under the same registrable domain. A subdomain like ws.example.com works when cookies use .example.com.

Blade-only apps can load Echo from Vite without a SPA framework. Livewire can react to Echo events in Alpine listeners. For heavier client state, compare Livewire 3 versus Inertia and Vue with Laravel before adding a second frontend architecture.

BrowserReverbLaravel1. Request private channel2. Auth POST3. Signed response4. Subscription OK5. Broadcast event6. Client receives payload
Laravel Reverb WebSocket private channel auth before messages flow

Validate event payloads with a JSON formatter when debugging shape mismatches between PHP and JavaScript.

How do you deploy and scale Laravel Reverb in production?

Running Reverb in a SSH session is fine for local work. Production needs a process supervisor, TLS, and a plan for restarts after deploy. I use Supervisor on Ubuntu 22 or 24 with Nginx in front, the same pattern as deploying Laravel on Ubuntu with Nginx.

Supervisor unit

[program:reverb]
process_name=%(program_name)s
command=php /var/www/current/artisan reverb:start --host=0.0.0.0 --port=8080
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
user=www-data
numprocs=1
redirect_stderr=true
stdout_logfile=/var/log/reverb.log
stopwaitsecs=3600

Reload Supervisor after deploy: sudo supervisorctl reread && sudo supervisorctl update. Pair this with zero-downtime Deployer releases so PHP-FPM and Reverb restart in the right order.

Nginx WebSocket proxy

server {
    listen 443 ssl http2;
    server_name ws.example.com;

    location / {
        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_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_read_timeout 86400s;
        proxy_send_timeout 86400s;
    }
}

Long proxy_read_timeout values prevent idle chat connections from dropping at the proxy layer. See Nginx reverse proxy setup for TLS certificate basics with Certbot.

Horizontal scaling needs Redis 8.10 pub/sub so every Reverb node receives the same broadcast. Enable scaling in config/reverb.php under servers.reverb.scaling. The Redis footprint overlaps with queue workers. Read scaling Laravel queues for high traffic and Redis queue production setup before sizing one Redis instance for both jobs and Reverb.

Multi-server Laravel apps also need shared sessions for private channel auth. Review Laravel session config for multi-server and Redis caching patterns so auth cookies resolve on every node.

Nginx Load BalancerReverb ANode 1Reverb BNode 2Reverb CNode 3Redis Pub/SubSyncs events across nodes
Multi-node laravel reverb websocket scaling with Redis as the message bus

Need help sizing infrastructure for real-time features? Enterprise application development services cover architecture, deploy pipelines, and production hardening for Laravel stacks in Nepal and abroad.

What causes WebSocket connection failures with Laravel Reverb?

Real-time bugs fail quietly. The browser console shows "WebSocket closed" without naming the root cause. A fixed checklist saves hours on client projects.

Diagnostic workflow

  1. Confirm Reverb is listening: ss -tulpn | grep 8080 on the server. If empty, check Supervisor status and UFW rules.
  2. Test outside the browser with wscat -c wss://ws.example.com/app/your-key. This isolates TLS and proxy issues from JavaScript bugs.
  3. Inspect the Network tab for failed /broadcasting/auth responses on private channels. 403 usually means session or channel policy mismatch.
  4. Compare VITE_REVERB_* build-time values to the hostname users actually load. Stale assets after deploy are a common miss.
  5. Read /var/log/reverb.log and storage/logs/laravel.log together. Reverb logs connections; Laravel logs broadcast exceptions.

Always terminate TLS at Nginx and expose WSS on port 443. Many ISPs and office networks in Nepal block non-standard ports. Port 443 looks like normal HTTPS traffic and passes through most filters.

Queue synchronous broadcasts during heavy traffic. Use ShouldBroadcastNow only when latency demands it. Otherwise let ShouldBroadcast events run through your queue worker so HTTP requests stay fast. Follow patterns in building real-time Laravel features with Redis and realtime APIs with Reverb for event design.

Isolate payment webhooks from broadcast routes. Callback URLs and WebSocket paths should not share fragile middleware stacks. See Laravel payment integrations for route and CSRF boundaries with Khalti, eSewa, and Stripe.

Connection failed?Check ReverbSupervisor + portCheck NginxUpgrade headersCheck EchoVite env keysPrivate channel?Test /broadcasting/authMulti-server?Redis + sessions
Debugging decision tree for laravel reverb websocket connection errors

Harden auth endpoints per OWASP practices for Laravel. Rate-limit /broadcasting/auth if bots probe channel names. Run through the Laravel production deployment checklist before go-live.

The Pusher protocol spec is documented at Pusher channel authentication. Reverb follows the same signing rules for private channels.

Key Takeaways

  • Install Reverb with composer require laravel/reverb and set BROADCAST_CONNECTION=reverb on Laravel 12 or 13 with PHP 8.3+.
  • Terminate WSS on port 443 through Nginx; bind Reverb to 0.0.0.0 internally and never expose raw port 8080 publicly.
  • Wire Laravel Echo with pusher-js and matching VITE_REVERB_* variables built through Vite 8.x.
  • Run Reverb under Supervisor, restart after deploy, and add Redis pub/sub before adding a second Reverb node.
  • Debug from server port → proxy headers → Vite env → /broadcasting/auth in that order.
  • Start with public channels in staging; add private channels only after session and cookie domains are correct.

People Also Ask

Does Laravel Reverb require Pusher subscription?

No. Reverb is self-hosted and free to run on your own hardware. You still install pusher-js on the frontend because Reverb implements the Pusher wire protocol. Laravel Echo talks to Reverb through that client library without a Pusher account.

Can Laravel Reverb run on shared hosting?

Generally no. Reverb is a long-running WebSocket process. Shared hosting blocks persistent daemons and custom ports. You need a VPS or cloud instance with Supervisor, plus Nginx for WSS proxying.

What is the difference between Reverb and Laravel Echo?

Reverb is the server that maintains WebSocket connections and delivers events. Laravel Echo is the JavaScript client that subscribes to channels in the browser. Your Laravel app broadcasts events; Reverb pushes them; Echo receives them.

Do I need Redis for a single-server Reverb setup?

Not for basic use on one machine. Redis becomes required when you run multiple Reverb instances or need pub/sub sync across nodes. Many teams already run Redis for queues and cache, so enabling Reverb scaling reuses existing infrastructure.

Ship real-time Laravel without vendor lock-in

A production laravel reverb websocket stack gives you native broadcasting, Pusher-compatible clients, and full data control on your own VPS. Start with one Supervisor-managed node, validate WSS early, and scale with Redis only when connection counts justify it. The payoff shows up fast in booking dashboards, order tracking, and client portals where stale pages erode trust.

For architecture review, deploy automation, or troubleshooting an existing WebSocket setup, contact me or explore web development services to plan your real-time rollout.

Frequently Asked Questions

Laravel Reverb is a first-party WebSocket server introduced in Laravel 11 that runs directly on your own infrastructure. It eliminates third-party SaaS fees for real-time features while maintaining full compatibility with the existing Laravel Echo client API and broadcasting drivers.

Self-hosting Reverb costs only your VPS expense, typically Rs 1,500 to Rs 3,000 monthly (USD 11–22) for a standard 2GB RAM instance. Pusher charges usage-based fees starting around USD 50/month for moderate traffic, making Reverb significantly cheaper for Nepali businesses at scale.

Yes. Laravel Reverb supports Laravel 11 and 12, requiring PHP 8.2 minimum. It runs stably on PHP 8.3 and 8.4 in production. Ensure your composer.json allows laravel/reverb ^1.0 and run php artisan reverb:install to configure credentials automatically.

Run composer require laravel/reverb followed by php artisan reverb:install. This publishes the config/reverb.php file, adds REVERB_APP_ID, REVERB_APP_KEY, REVERB_APP_SECRET, and REVERB_HOST variables to your .env, and registers the reverb:serve Artisan command. Update your broadcasting.php driver to reverb and rebuild frontend assets.

Absolutely. Reverb works with any frontend consuming Laravel Echo. Install laravel-echo and pusher-js via npm, configure Echo to use the reverb broadcaster with your self-hosted host and port, and listen to channels in Vue components or Alpine x-data blocks exactly as you would with Pusher.

Use Supervisor to manage the php artisan reverb:serve process persistently. Create /etc/supervisor/conf.d/reverb.conf pointing to your release path, set numprocs=1, autostart=true, and autorestart=true. On Deployer 7 setups, add a post-deploy task to restart supervisorctl reload reverb:* to pick up new code without downtime.

Reverb transmits over WSS when behind a reverse proxy terminating SSL. Authorize channels using Laravel Policies or Gate checks in routes/channels.php to prevent unauthorized listeners. Never broadcast PII directly; send only resource IDs and fetch sensitive details via authenticated REST endpoints. In my experience building legal-tech portals like Mijar Law Associates, this pattern satisfies client confidentiality requirements.

Reverb uses the same /broadcasting/auth endpoint as Pusher. Laravel validates the authenticated user against channel authorization rules defined in routes/channels.php before granting access. For API-only apps using Sanctum, ensure your frontend sends the session cookie or bearer token with the auth request so Reverb can verify permissions server-side.

Plan for 2GB RAM and 2 CPU cores minimum for 1,000 concurrent WebSocket connections. Reverb uses ReactPHP's event loop, so memory stays relatively flat per connection. Monitor with htop and Supervisor logs. On shared EC2 instances running multiple sister sites, I allocate dedicated resources to avoid contention during peak hours.

Check browser DevTools Network tab for failed ws:// or wss:// handshakes. Verify REVERB_HOST matches your local or dev domain, not localhost if accessing remotely. Run php artisan reverb:serve --debug to see verbose server logs. Confirm .env variables match config/reverb.php values after caching with php artisan config:clear.

Yes. Run Reverb on a non-standard port like 6001 and configure Nginx or Apache as a reverse proxy forwarding /app/websocket traffic to localhost:6001. This lets your main site serve HTTP on 443 while Reverb handles WebSocket upgrades separately. Always terminate SSL at the web server layer, not inside Reverb itself.

Use Redis Pub/Sub as the scaling driver by setting REVERB_SCALING_ENABLED=true and configuring a shared Redis instance. Each Reverb node subscribes to the same Redis channel, broadcasting messages across all connected clients regardless of which server they hit. This requires sticky sessions or consistent hashing at the load balancer level.

Common causes include insufficient Supervisor workers, PHP memory limits, or missing opcache preload. Increase max_execution_time and memory_limit in php.ini for the CLI SAPI. Add more Supervisor processes if CPU allows. In production deployments I maintain, enabling JIT compilation on PHP 8.3+ reduced event latency noticeably during high-throughput periods.

Yes. Presence channels work identically to Pusher, returning member lists via the auth endpoint. Client events are supported but must be explicitly allowed in channel authorization logic. Note that client events bypass your backend entirely between peers, so validate trust boundaries carefully before enabling them in multi-tenant applications.

Choose Reverb when already invested in the Laravel ecosystem and wanting zero vendor lock-in with familiar Echo syntax. Prefer Socket.io for complex room/namespace logic outside Laravel. Mercure excels for Symfony or async PHP projects. For most Laravel shops in Nepal, Reverb reduces operational complexity and cost versus maintaining separate real-time infrastructure.

Share this article

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.

Quick Contact Options
Choose how you want to connect me: