Realtime (Laravel Reverb) Deployment
How to deploy and configure the Laravel Reverb WebSocket server on Forge and wire the Vercel-hosted Next.js frontend to it.
The admin "LOS Debug Logs" live stream is powered by Laravel Reverb, a
WebSocket server that runs as a persistent daemon on the API host (Forge). The
Next.js frontend (Vercel) connects to it with Laravel Echo over the pusher
protocol; the private-channel auth handshake is proxied through the BFF
(/api/broadcasting/auth) so the Sanctum token never reaches the browser.
Reverb cannot run on Vercel
Reverb is a long-lived daemon and must live on the API host (Forge). Vercel only serves the Next.js client that connects to it. The "Public hostname" you configure for Reverb is the Reverb server's own host on Forge — not the Vercel frontend URL.
The two halves
Reverb is configured in two places that must agree:
- API (
apps/flex-hub-api, Forge) — runs the Reverb daemon and the broadcast config. - Web (
apps/flex-hub, Vercel) — the browser-side Echo client. ItsNEXT_PUBLIC_REVERB_*values mirror the public-facing API values.
Two ports, two roles (do not confuse them)
| Variable | Role | Production value |
|---|---|---|
REVERB_SERVER_PORT | The local port the daemon binds to (behind Nginx). | 8080 |
REVERB_SERVER_HOST | The interface it binds to. | 0.0.0.0 |
REVERB_PORT | The public port the browser connects to. | 443 |
REVERB_HOST | The public hostname the browser connects to. | your custom host |
REVERB_SCHEME | The public scheme (drives forceTLS). | https |
Nginx terminates TLS on 443 and reverse-proxies the WebSocket traffic to
127.0.0.1:8080. The browser never talks to 8080 directly.
Enabling Reverb on Forge
Use Forge's native "Enable Laravel Reverb" integration on the API site. It
installs Reverb, creates the daemon (reverb:start), and extends the site's
Nginx config with the WebSocket proxy (including the Upgrade/Connection
headers) on the host you provide.
The modal asks for:
- Public hostname — the address the browser opens the socket against. It must resolve (DNS) to this Forge server.
- Port — the internal proxy target. Leave it
8080. - Max concurrent connections —
1000is a reasonable default.
Forge domains are rejected
Forge will not accept a Forge-owned domain (*.on-forge.com) as the
Reverb public hostname — it shows "You cannot use a Forge domain as the
hostname for a Reverb instance." You must use a custom domain or
subdomain you control (e.g. ws.example.com) with a DNS
record pointing at the Forge server and a Let's Encrypt certificate issued for
it.
API environment variables
apps/flex-hub-api/.env (on Forge). The APP_ID / APP_KEY / APP_SECRET are a
self-generated shared secret between the app and its reverb:start process — not
vendor credentials. Generate a set with php artisan reverb:install.
BROADCAST_CONNECTION=reverb
REVERB_APP_ID= # digits
REVERB_APP_KEY= # random string
REVERB_APP_SECRET= # random string
# Public-facing — what the browser connects to.
REVERB_HOST=ws.example.com # custom domain (NOT *.on-forge.com)
REVERB_PORT=443
REVERB_SCHEME=https
# Where the daemon binds (server-side, behind Nginx).
REVERB_SERVER_HOST=0.0.0.0
REVERB_SERVER_PORT=8080
# Leave blank when using Forge's native integration (it proxies the standard
# /app endpoints on the host). Only set this for a manual path-prefix proxy.
REVERB_SERVER_PATH=
# The browser origin allowed to open a socket — the Vercel frontend URL.
REVERB_ALLOWED_ORIGINS=https://app.example.comREVERB_ALLOWED_ORIGINS is the one place Vercel appears
The Next.js app is cross-origin to the Reverb host, so its public URL must be
listed here or the handshake is rejected. Do not leave it as * in
production.
Web (Vercel) environment variables
apps/flex-hub reads these in src/lib/client/echo.ts. Each NEXT_PUBLIC_REVERB_*
value mirrors the matching public API value.
| Vercel variable | Must equal (API) | Production value |
|---|---|---|
NEXT_PUBLIC_REVERB_KEY | REVERB_APP_KEY | byte-identical to the API key |
NEXT_PUBLIC_REVERB_HOST | REVERB_HOST | ws.example.com |
NEXT_PUBLIC_REVERB_PORT | REVERB_PORT | 443 |
NEXT_PUBLIC_REVERB_SCHEME | REVERB_SCHEME | https |
NEXT_PUBLIC_REVERB_PATH | REVERB_SERVER_PATH | blank (Forge integration) |
API_BASE_URL | — | your Laravel API URL (powers /api/broadcasting/auth) |
NEXT_PUBLIC_* are build-time
These are inlined at build time. After changing them you must trigger a fresh
deploy (not a redeploy of an old build), and set them for the
Production environment (and Preview if you test there). The
APP_ID / APP_SECRET are server-side only — never add them to Vercel.
Verification checklist
- The Reverb daemon shows running in Forge's Daemons list.
REVERB_HOSTis a custom domain with a valid TLS cert, resolving to the Forge server.NEXT_PUBLIC_REVERB_KEYis byte-identical toREVERB_APP_KEY.REVERB_ALLOWED_ORIGINSincludes the Vercel app URL.- Both
*_PATHvars are blank (Forge native integration). - Loading the admin LOS Debug Logs page opens a
wss://connection toREVERB_HOSTinstead of degrading to the offline state.
When NEXT_PUBLIC_REVERB_KEY or NEXT_PUBLIC_REVERB_HOST is blank, the client
returns null from reverbConfig() and realtime is disabled — the page
degrades to an "offline" state rather than erroring.