laravel/reverb is installed, Ingot runs Reverb inside the web process, next to FrankenPHP, and FrankenPHP passes WebSocket connections on the application’s own domain to it, so Reverb needs no process, domain or port of its own.
At a glance
What this guide adds to the application from Deploy a Laravel application:Environment variables
Before you begin
- A Laravel application deployed on Sevalla, as in Deploy Laravel.
Install Reverb
Install Reverb 1 and Laravel’s broadcasting files, then the two packages Echo needs in the browser:install:broadcasting installs laravel/reverb, writes config/broadcasting.php, config/reverb.php and routes/channels.php, registers the channels file in bootstrap/app.php, and adds resources/js/echo.js, which resources/js/app.js imports. It also writes Reverb’s settings and credentials for local development to .env, which Sevalla doesn’t use. --without-node skips its own npm install step, which needs an interactive terminal and otherwise fails with TTY mode requires /dev/tty to be read/writable.; the second command installs the same packages.
resources/js/echo.js reads the VITE_REVERB_* variables. Vite writes their values into the JavaScript it builds, so on Sevalla they must be available during the build.
Broadcast an event
Create an event that Laravel broadcasts on the publicorders channel. ShouldBroadcastNow sends it to Reverb at once; ShouldBroadcast queues it for a queue worker instead:
composer.lock and package-lock.json included. A private channel works the same way on Sevalla; it also needs a signed-in user and a rule in routes/channels.php.
Run Reverb on Sevalla
These steps use Ingot, which runs Reverb in the web process. With your own Dockerfile, see Deploy with a Dockerfile.Find the internal hostname
storefront-x7k2m-web.storefront-x7k2m.svc.cluster.local.Add the runtime variables
Add the build variables
Deploy
How Ingot runs Reverb
Ingot findslaravel/reverb in composer.lock, and its build plan has a Websockets row:
php artisan reverb:start --host=127.0.0.1 --port=6001 beside FrankenPHP and starts Reverb again if it stops. FrankenPHP passes requests that upgrade to a WebSocket under /app/ to Reverb, and requests to Reverb’s API under /apps/<id>/ (events, batch_events, connections, channels, users), so both share the application’s domain and the PORT with Laravel. Every other request goes to Laravel. When the web process stops, the script stops Reverb with it, and Reverb logs Gracefully terminating connections.
Two settings say where Reverb is, one for each side:
REVERB_HOST,REVERB_PORTandREVERB_SCHEMEare where Laravel sends events. The web process’s internal hostname on port8080keeps them on Sevalla’s private network, and reaches Reverb from every process of the application, so a queue worker can broadcast too. The application’s domain on port443withhttpsworks as well, through Sevalla’s proxy and Cloudflare.VITE_REVERB_*are where browsers connect: the application’s domain on port443, overwss://.
With BROADCAST_CONNECTION=reverb, Laravel neither builds nor starts without REVERB_APP_ID, REVERB_APP_KEY and REVERB_APP_SECRET and Reverb runs in each web instance. Both are covered by the variables in these steps and in Run more than one instance.
Verify
-
When the deployment’s status is Deployed, click the Logs tab. The web process’s runtime logs show Reverb’s start next to FrankenPHP’s:
Runtime logs
-
Open
/orderson the application’s domain, for examplehttps://storefront-x7k2m.sevalla.app/orders. The page shows Shipped orders. -
Click the Web terminal tab, and in the shell that opens in the Web process, broadcast an event:
The open page adds
Order 42 shippedwithout a reload.
Check from a terminal
These checks need no dashboard and no browser.API, H, APP_ID, WEB_ID and REVERB_APP_KEY are set as in Deploy with the API, and the checks need Node.js 22 or later, which has a built-in WebSocket.
-
Connect to Reverb as a browser does, with your application’s domain, subscribe to
ordersand wait for one event, in the background: -
Broadcast an event from the web process with Run a command in a process (
execProcessCommandV3), then wait for the listener:The listener prints each message it gets: the connection, the subscription, then the event with its data. The0is the exit code of the command in the web process.
Where Reverb can run on Sevalla
Browsers on anhttps:// page connect to Reverb over wss://, so Reverb must be behind a domain that Sevalla serves over HTTPS: the domain of a web process.
Run Reverb in its own application
A second application from the same repository can run Reverb in its web process, with its own domain, size and instances. The main application keeps serving Laravel and broadcasts to it over the private network.Create the Reverb application
storefront-reverb as the Name, keep the Size at H1, and click Create application.Start Reverb in its web process
exec php artisan reverb:start --host=0.0.0.0 --port=$PORT as the Custom start command. Under Health checks, turn on Readiness probe with the Path /up, Reverb’s health route, and click Update process.Add its environment variables
REVERB_APP_ID, REVERB_APP_KEY and REVERB_APP_SECRET with the main application’s values, and CACHE_STORE with the value file, all with Available during runtime selected. Reverb reads the cache when it starts, to see whether php artisan reverb:restart asked it to restart, and Laravel’s default cache store is the database, which this application doesn’t have: without CACHE_STORE, the runtime logs show Database file at path [/app/database/database.sqlite] does not exist. at every start.Deploy it
INFO Starting server on 0.0.0.0:8080.Connect the main application
storefront-reverb as the Service, and click Add internal connection.Point the main application at Reverb
REVERB_HOST to the Reverb application’s Web internal hostname, for example storefront-reverb-a1b2c-web.storefront-reverb-a1b2c.svc.cluster.local, and VITE_REVERB_HOST to its domain, for example storefront-reverb-a1b2c.sevalla.app. Then click Deploy now on the Overview tab./orders page now connects to the Reverb application’s domain, and the main application sends events to it over the internal connection. The main application’s web process still runs Ingot’s Reverb next to FrankenPHP, unused, because laravel/reverb is installed.
Run more than one instance
Sevalla spreads WebSocket connections across a web process’s instances, and each instance runs its own Reverb, which knows only the browsers connected to it. An event that the application sends reaches one instance, so only the browsers connected to that instance get it. With 2 instances and 8 connected clients, one event reached 3 of them, and another 5. Reverb’s scaling mode passes each event to every instance through Redis:Connect a Redis database
REDIS_HOST, REDIS_PORT, REDIS_PASSWORD and REDIS_URL, which Reverb reads.Turn on scaling
REVERB_SCALING_ENABLED with the value true, with Available during runtime selected, then deploy.Deploy with a Dockerfile
Ingot’s Reverb is part of the image Ingot builds. The Dockerfile in the deploy guide starts FrankenPHP only, so run Reverb in its own application from the same Dockerfile: its start command replaces the image’sCMD. Make two changes to the Dockerfile:
-
Install the
pcntlextension, with which Reverb handlesSIGTERMand closes its connections before the instance stops. Change the firstRUNline:Dockerfile -
Pass the
VITE_REVERB_*variables to the stage that builds the assets. Sevalla passes every variable available during the build as a build argument, and a stage sees only the arguments it declares:Dockerfile
ARG line, Vite builds echo.js without a key or host, and the page never connects. Switch both applications to the Dockerfile build strategy as in Deploy with a Dockerfile, and deploy both.
Troubleshoot
Deploy with the API
The same steps as Run Reverb on Sevalla, as calls to the Sevalla API.APP_ID, WEB_ID and APP_HOST are the application, its web process and its domain from Deploy with the API in the deploy guide, with API and H set as there. Push the Reverb changes first: the deployment builds the branch as it is.
Add the environment variables
getProcessV3) returns the web process’s internal hostname. Create environment variable (createEnvironmentVariableV3) adds one variable per call: new Reverb credentials and the server’s address for runtime, and the browser’s settings for the build:sevalla apps env-vars create $APP_ID --key BROADCAST_CONNECTION --value reverb, once per variable; the CLI makes each variable available during build and runtime. MCP: set_environment_variables with runtime and buildtime set as in the loops.Deploy
triggerDeploymentV3). The build reads the VITE_REVERB_* variables, so the deployment must build the image:sevalla apps deployments trigger $APP_ID. MCP: trigger_deployment.Wait for the deployment
getDeploymentV3) until the status is final: success, failed, cancelled or skipped. The loop keeps polling through a failed request and stops after 30 minutes:sevalla apps deployments get $DEPLOYMENT_ID --app-id $APP_ID. MCP: get_deployment.$APP_HOST as the domain.
Run Reverb in its own application with the API
The same steps as Run Reverb in its own application, after the steps before.REPO_URL, BRANCH, CLUSTER_ID and H1_ID are set as in the deploy guide, and REVERB_APP_ID, REVERB_APP_KEY and REVERB_APP_SECRET as in Add the environment variables.
Create the Reverb application
createApplicationV3) from the same repository and branch, then read its domain with List application domains (listApplicationDomainsV3):sevalla apps create --name storefront-reverb --cluster $CLUSTER_ID --source privateGit --git-type github --repository $REPO_URL --branch $BRANCH. MCP: create_application with deploy: false.Start Reverb in its web process
updateProcessV3) sets the start command, H1 and a readiness probe on Reverb’s /up:sevalla apps processes update $REVERB_WEB_ID --app-id $REVERB_ID --resource-type $H1_ID --command 'exec php artisan reverb:start --host=0.0.0.0 --port=$PORT'. MCP: sevalla_api_update with updateProcessV3 and the same body.Add the credentials and deploy
CACHE_STORE set to file, because it has no database. Add them with Create environment variable, then trigger a deployment and wait for it:sevalla apps deployments trigger $REVERB_ID. MCP: set_environment_variables, then trigger_deployment.Connect the main application to Reverb
createApplicationInternalConnectionV3) with target_type app:sevalla apps internal-connections create $APP_ID --target-id $REVERB_ID --target-type app. MCP: sevalla_api_create with createApplicationInternalConnectionV3 and the same body.Point the main application at Reverb
listEnvironmentVariablesV3) gives each variable’s ID, and Update environment variable (updateEnvironmentVariableV3) changes REVERB_HOST to the Reverb application’s internal hostname and VITE_REVERB_HOST to its domain. Then deploy the main application, so the build reads the new VITE_REVERB_HOST:sevalla apps env-vars update $VAR_ID --app-id $APP_ID --value <value>. MCP: set_environment_variables, then trigger_deployment.$REVERB_APP_HOST as the listener’s domain: the browser connects to the Reverb application now. To build both applications from the Dockerfile, send {"build_type": "dockerfile", "dockerfile_path": "Dockerfile", "docker_context": "."} to Update application (updateApplicationV3) for each before you deploy it.