WSBro
Pusher-compatible WebSocket server for Laravel in one small Go binary. A drop-in replacement for Reverb and Pusher.
Real-time for Laravel without Pusher bills or a PHP process to babysit. WSBro is one 20 MB binary that speaks the Pusher protocol: point Laravel at it and broadcasting just works.
Why WSBro
- Drop-in for Reverb and Pusher. Laravel's own
reverbandpusherbroadcasters, Laravel Echo and every Pusher SDK work unchanged. No package to install, no code to change. - One binary, nothing else. No PHP worker, no Node.js, no database.
docker runand you are live. - Fast and frugal. 100,000 concurrent connections in 3.3 GB of RAM; 160,000 messages per second with a p99 latency of 3 ms on one instance.
- A dashboard you will actually use. Live debug console, presence members, test events, connections you can disconnect. Built in, works offline.
- Proven, not promised. Every change runs a real Laravel 12 and 13 app with laravel-echo and pusher-js through WSBro, on both broadcasters.
Quick start
docker run -d --name wsbro -p 6001:6001 \
-e WSBRO_APP_ID=my-app \
-e WSBRO_APP_KEY=my-key \
-e WSBRO_APP_SECRET=my-secret \
-e WSBRO_DASHBOARD_PASSWORD=change-me \
ghcr.io/axvilab/wsbro
Without Docker, download a binary from the releases page or run go install github.com/axvilab/wsbro@latest. The dashboard is at http://localhost:6001/dashboard.
Use it with Laravel
Already on Reverb? Keep your code and your .env, stop running php artisan reverb:start and point REVERB_HOST and REVERB_PORT at WSBro:
BROADCAST_CONNECTION=reverb
REVERB_APP_ID=my-app
REVERB_APP_KEY=my-key
REVERB_APP_SECRET=my-secret
REVERB_HOST=127.0.0.1
REVERB_PORT=6001
REVERB_SCHEME=http
Everything works as it does with Reverb or Pusher:
broadcast(new OrderShipped($order))->toOthers();
Echo.private(`App.Models.User.${userId}`).listen('.order.shipped', (e) => { /* ... */ });
Echo.join('chat')
.here((users) => {})
.joining((user) => {})
.leaving((user) => {})
.listenForWhisper('typing', (e) => {});
Features
- Pusher protocol 7: public, private, presence, encrypted and cache channels; client events (whisper); user authentication,
sendToUserand terminating connections. - Pusher HTTP API: trigger, batch, channel info and presence users, signed exactly like Pusher, with a replay-safe
body_md5check. - Webhooks:
channel_occupied,channel_vacated,member_addedandmember_removedin the Pusher format, verifiable with the official SDKs. - Multi-app: many apps on one server, fully isolated, each with its own limits, allowed origins and webhook.
- Protection: connection, message rate, channel and payload limits; allowed origins; slow and dead clients are dropped without affecting others.
- Scaling: Redis or NATS to run several instances; a graceful shutdown hands clients over.
- Observability: JSON and Prometheus metrics, JSON logs and a health endpoint.
- Operations: TLS, a 20 MB multi-arch image running as non-root, a systemd-friendly binary and strict configuration validation.
Dashboard
The dashboard is built in and enabled with WSBRO_DASHBOARD_PASSWORD. It shows every app, channel and connection, and lets you send test events.

The debug console shows every connection, subscription and event live:

How it compares
| Feature | WSBro | Laravel Reverb | Soketi | Sockudo |
|---|---|---|---|---|
| Runs as | single Go binary | PHP process | Node.js | single Rust binary |
| Pusher protocol | yes | yes | yes | yes |
Laravel's reverb broadcaster |
yes | yes | yes | yes |
| Dashboard | built in, with a live debug console | Laravel Pulse cards | none | separate service |
| Scaling | Redis, NATS | Redis | Redis, NATS, cluster | many brokers |
| Focus | small, simple, Laravel-first | Laravel's official solution | general purpose | feature-rich platform |
All of them are good projects. WSBro is for teams that want Reverb's compatibility without running PHP for WebSockets, and the smallest possible thing to deploy and understand.
Roadmap
- Shared presence state across instances: exact
here()lists and channel counts with Redis. - A ready-made Helm chart.
- Webhook retries with back-off.
- Subscription count events (
pusher:subscription_count).