Real-Time Updates with Laravel Reverb Broadcasting

PagibleAI CMS can broadcast content changes across admin sessions in real time using Laravel Reverb, Laravel's own WebSocket server. With broadcasting enabled, when one editor saves a page, uploads a file, or updates a shared element, the change appears in every other editor's admin panel without a page refresh.

This page explains how the synchronization works and how to configure both the CMS application and the Reverb server.

How real-time synchronization works

Real-time updates are opt-in and deliberately lightweight. Each time an editor saves, publishes, moves, deletes, or restores a page, element, or file, the CMS sends one small event over the WebSocket connection. The event carries only metadata: the item type, its ID, the editor's name, and a few status flags. It never includes the page content.

Every admin panel listens on one channel per content type (cms.page, cms.element, cms.file). List and tree views update in place: a save, publish, restore, or trash action patches the affected row directly. Additions, tree moves, and permanent deletions change the list structure, so instead of reloading they mark the view as outdated and show a Refresh button labelled Updated by another user. The list reloads only when you click it, which keeps your scroll position, filters, and selection intact. A detail view behaves differently: when its own item changes, it reloads itself and refetches the content through the GraphQL API, so you always edit the latest version.

The tab that made a change is excluded from its own event, so your actions never trigger a redundant update in your own panel. Changes from outside the admin panel still reach every connected editor, whether they come from the MCP server, the API, or a scheduled job. After a dropped connection reconnects, detail views reload and list and tree views show the Refresh notice, so nothing that changed while you were offline is missed.

How list and tree views react

Action
What happens
List and tree view reaction
added
A new page, element, or file is created
Shows a Refresh notice to reload
saved
A draft version is saved
Affected row patches in place
published
A version is published
Affected row patches in place
restored
A trashed item is restored
Affected row patches in place
dropped
An item is moved to the trash
Affected row patches in place
moved
A page is moved within the tree
Shows a Refresh notice to reload
purged
An item is permanently deleted
Shows a Refresh notice to reload

PagibleAI CMS Configuration

Broadcasting is disabled by default. To enable real-time updates, add these variables to your application's .env file. They tell the admin panel where to reach the Reverb server and how to authenticate with it.

CMS_BROADCAST=true
BROADCAST_CONNECTION=reverb

REVERB_HOST=127.0.0.1
REVERB_PORT=8800
REVERB_SCHEME=http

REVERB_APP_ID=pagible
REVERB_APP_KEY=pagible-key
REVERB_APP_SECRET=pagible-pass

CMS_BROADCAST: Master switch that enables real-time event broadcasting in the PagibleAI admin panel. Set to true to activate WebSocket connections for all admin users. It defaults to false.

BROADCAST_CONNECTION: Selects Reverb as the broadcasting driver for the application. The php artisan install:broadcasting --reverb command sets this automatically.

REVERB_HOST: The hostname or IP address where the Reverb server is reachable. The application uses it to publish events, and by default it is also advertised to the browser for the WebSocket connection. Use 127.0.0.1 for local development. For production behind a reverse proxy, advertise a separate public endpoint to the browser (see Production deployment behind a reverse proxy below).

REVERB_PORT: The port the WebSocket server listens on. The default 8800 avoids conflicts with common web server ports (80, 443, 8080).

REVERB_SCHEME: The protocol used for the WebSocket connection: http for local development, https for production environments with TLS termination.

REVERB_APP_ID, REVERB_APP_KEY, REVERB_APP_SECRET: Authentication credentials that must match exactly between the CMS application and the Reverb server. Replace the example values with your own secure secrets in production environments.

Laravel Reverb Server Setup

Reverb runs on the server that handles WebSocket connections, either alongside your application or on a dedicated machine. Install Laravel's broadcasting layer with Reverb as the driver:

php artisan install:broadcasting --reverb

This command publishes the Reverb configuration file, installs the required Composer and NPM dependencies, and registers the broadcasting service provider. Next, add these variables to the Reverb server's .env file:

REVERB_APP_ID=pagible
REVERB_APP_KEY=pagible-key
REVERB_APP_SECRET=pagible-pass

REVERB_SERVER_HOST=127.0.0.1
REVERB_SERVER_PORT=8800
REVERB_MAX_REQUEST_SIZE=100_000
REVERB_APP_MAX_MESSAGE_SIZE=100_000

REVERB_APP_ID, REVERB_APP_KEY, REVERB_APP_SECRET: Must match the values configured in the CMS application exactly. These credentials authenticate the connection between your Laravel app and the Reverb WebSocket server.

REVERB_SERVER_HOST: The network interface the Reverb server binds to. Use 127.0.0.1 to only accept local connections (recommended when running behind a reverse proxy like Nginx), or 0.0.0.0 to listen on all network interfaces.

REVERB_SERVER_PORT: The port Reverb listens on. This must match the REVERB_PORT value in the CMS configuration above.

REVERB_MAX_REQUEST_SIZE: Maximum allowed HTTP request payload size in bytes (default 100,000). Increase this value if you need to broadcast large content updates.

REVERB_APP_MAX_MESSAGE_SIZE: Maximum WebSocket message size in bytes (default 100,000). Should match or exceed REVERB_MAX_REQUEST_SIZE to prevent message truncation.

Then start the Reverb WebSocket server:

php artisan reverb:start

In production, run Reverb as a daemon under a process manager such as Supervisor so it restarts automatically after a crash. It usually sits behind a reverse proxy like Nginx, which terminates TLS and forwards the WebSocket upgrade. For Supervisor configuration, Nginx setup, and performance tuning, see the Laravel Reverb production documentation.

Production deployment behind a reverse proxy

The browser and the application reach Reverb by different routes, configured separately. The application publishes events over an internal connection, often plain HTTP on a private network or inside a container cluster, while browsers connect to a public TLS-secured endpoint through your reverse proxy. PagibleAI exposes a separate set of client variables for the browser-facing endpoint, independent of the internal publish address.

Reverb serves its WebSocket at the /app/{key} path. Route that prefix on your public host to the Reverb process and send every other request to the application. A single Reverb process keeps all connections in memory, so one instance needs no Redis fan-out. Leave REVERB_SCALING_ENABLED off unless you run several Reverb instances behind a load balancer.

# In addition to CMS_BROADCAST=true and BROADCAST_CONNECTION=reverb on the app

# Internal endpoint the app uses to publish events to Reverb (stays private)
REVERB_HOST=reverb.internal
REVERB_PORT=8080
REVERB_SCHEME=http

# Public WebSocket endpoint advertised to the browser (WSS through your proxy)
REVERB_CLIENT_HOST=cms.example.com
REVERB_CLIENT_PORT=443
REVERB_CLIENT_SCHEME=https

# Network interface and port the Reverb process binds to
REVERB_SERVER_HOST=0.0.0.0
REVERB_SERVER_PORT=8080

# A single Reverb instance needs no Redis fan-out
REVERB_SCALING_ENABLED=false

REVERB_HOST, REVERB_PORT, REVERB_SCHEME: The endpoint your Laravel application uses to publish events to Reverb. Keep this on the internal network, for example plain HTTP to a private hostname, so broadcasting traffic never leaves your infrastructure.

REVERB_CLIENT_HOST, REVERB_CLIENT_PORT, REVERB_CLIENT_SCHEME: The endpoint the admin panel advertises to the browser for the WebSocket connection. Point these at your public domain over HTTPS (port 443) so the client opens a secure wss:// connection through your reverse proxy. When they are not set, the browser falls back to the REVERB_HOST values.

REVERB_SERVER_HOST, REVERB_SERVER_PORT: The network interface and port the Reverb process itself binds to. Use 0.0.0.0 so the proxy, or other nodes in a cluster, can reach it.

REVERB_SCALING_ENABLED: Enables Redis-backed scaling so several Reverb instances share connections. Leave it false for a single instance; set it to true and configure Redis only when you run more than one Reverb process behind a load balancer.

Security and permissions

Every subscription is authorized on the server before any event is delivered. Reverb authenticates each channel through Laravel's /broadcasting/auth route, and the CMS grants a subscription only when the user is signed in, holds the view permission for that content type (page:view, element:view, or file:view), and belongs to the tenant that owns the channel. Editors never receive events for content they cannot see.

The cms.broadcast-middleware setting controls the middleware on the authorization route. It defaults to the standard web session, authentication, and admin rate-limiting stack. In a multi-tenant install, add your tenancy-initialization middleware here so the current tenant resolves during channel authorization. Each tenant then has its own channels in the form cms.{tenant}.{type}.

Troubleshooting

Changes don't appear in other editors' panels

Confirm CMS_BROADCAST=true is set on the application, the Reverb server is running, and the REVERB_APP_ID, REVERB_APP_KEY, and REVERB_APP_SECRET values match exactly on both sides. Check the browser console for a successful WebSocket connection, and make sure your reverse proxy forwards the /app/ path to Reverb with the WebSocket upgrade headers intact.

The WebSocket connection fails over HTTPS

Set REVERB_CLIENT_SCHEME=https and REVERB_CLIENT_PORT=443 so the client opens a wss:// connection, and make sure your proxy terminates TLS and forwards the WebSocket upgrade on the /app/ path. A page served over https:// cannot open an insecure ws:// connection; the browser blocks it.

Broadcasts fail with a "Payload too large" error

This is the publish-side HTTP limit on the Reverb server. Raise REVERB_MAX_REQUEST_SIZE, the maximum request body Reverb accepts, rather than the message-size limit. PagibleAI events are metadata-only and normally stay well within the default, so check for unusually large custom events if you reach this limit.

Do I need Redis?

Not for a single Reverb instance. One process holds every connection, so events reach all editors without a shared backend. Redis is only needed when you run multiple Reverb instances behind a load balancer; set REVERB_SCALING_ENABLED=true and configure a Redis connection in that case.

Updates pause after a period of inactivity

The admin panel closes the WebSocket after a few minutes with no open channels to save resources and reconnects automatically when you next need it. On reconnect, open detail views reload and list and tree views show a Refresh notice, so you can catch up on anything that changed while disconnected.