Skip to main content
Laravel Reverb is Laravel’s WebSocket server. Browsers connect to it with Laravel Echo and subscribe to channels, and the application broadcasts events to them by sending each event to Reverb’s HTTP API. When 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

The application’s other variables are in Deploy a Laravel application.

Before you begin

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 public orders channel. ShouldBroadcastNow sends it to Reverb at once; ShouldBroadcast queues it for a queue worker instead:
app/Events/OrderShipped.php
Add a page that listens on the channel and lists each shipped order:
resources/views/orders.blade.php
routes/web.php
Commit and push the changes, 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.
1

Find the internal hostname

On the application’s Networking tab, copy the Web process’s hostname from Internal hostnames, for example storefront-x7k2m-web.storefront-x7k2m.svc.cluster.local.
2

Add the runtime variables

Click the Environment variables tab, then click Add environment variables. Add these variables, select only Available during runtime, and click Add environment variables:
3

Add the build variables

Click Add environment variables again. Add these variables, select only Available during buildtime, and click Add environment variables:
4

Deploy

Click the Overview tab, then click Deploy now under Recent deployments. Check the Branch and click Deploy. The build must run, so that Vite reads the new variables.

How Ingot runs Reverb

Ingot finds laravel/reverb in composer.lock, and its build plan has a Websockets row:
Build log
The web process’s start script then runs 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_PORT and REVERB_SCHEME are where Laravel sends events. The web process’s internal hostname on port 8080 keeps 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 port 443 with https works as well, through Sevalla’s proxy and Cloudflare.
  • VITE_REVERB_* are where browsers connect: the application’s domain on port 443, over wss://.
The deployment’s build log also shows Ingot’s warnings 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

  1. 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
  2. Open /orders on the application’s domain, for example https://storefront-x7k2m.sevalla.app/orders. The page shows Shipped orders.
  3. Click the Web terminal tab, and in the shell that opens in the Web process, broadcast an event:
    The open page adds Order 42 shipped without 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.
  1. Connect to Reverb as a browser does, with your application’s domain, subscribe to orders and wait for one event, in the background:
  2. 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. The 0 is the exit code of the command in the web process.

Where Reverb can run on Sevalla

Browsers on an https:// 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.
1

Create the Reverb application

In the sidebar, click Applications, then click Create > Application. Under Source, click Private repository, then choose the main application’s Git provider, Repository and Branch. Under Location, choose the same Data center. Enter storefront-reverb as the Name, keep the Size at H1, and click Create application.
2

Start Reverb in its web process

On the new application’s Processes tab, click ⋯ on the Web process, then click Update process. Enter 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.
3

Add its environment variables

On its Environment variables tab, add 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.
4

Deploy it

Click the Overview tab, then click Deploy now. The runtime logs show INFO Starting server on 0.0.0.0:8080.
5

Connect the main application

In the main application, click the Networking tab. Under Connected services, click Add internal connection, choose storefront-reverb as the Service, and click Add internal connection.
6

Point the main application at Reverb

On the main application’s Environment variables tab, set 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.
The /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:
1

Connect a Redis database

Create a Redis or Valkey database in the same location and add an internal connection to it with Add environment variables to the application turned on, as in Cache Laravel with Redis. Sevalla adds REDIS_HOST, REDIS_PORT, REDIS_PASSWORD and REDIS_URL, which Reverb reads.
2

Turn on scaling

Add the environment variable REVERB_SCALING_ENABLED with the value true, with Available during runtime selected, then deploy.
With scaling on, the same test reached 8 of 8 clients on 2 instances. Each Reverb instance knows only its own connections wherever it runs, so a Reverb application with more than one instance needs the same two steps.

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’s CMD. Make two changes to the Dockerfile:
  1. Install the pcntl extension, with which Reverb handles SIGTERM and closes its connections before the instance stops. Change the first RUN line:
    Dockerfile
  2. 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
Without the 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.
1

Add the environment variables

Get process (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:
CLI: 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.
2

Deploy

Trigger deployment (triggerDeploymentV3). The build reads the VITE_REVERB_* variables, so the deployment must build the image:
CLI: sevalla apps deployments trigger $APP_ID. MCP: trigger_deployment.
3

Wait for the deployment

Poll Get 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:
CLI: sevalla apps deployments get $DEPLOYMENT_ID --app-id $APP_ID. MCP: get_deployment.
Then run the checks in Check from a terminal, with $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.
1

Create the Reverb application

Create application (createApplicationV3) from the same repository and branch, then read its domain with List application domains (listApplicationDomainsV3):
CLI: 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.
2

Start Reverb in its web process

Update process (updateProcessV3) sets the start command, H1 and a readiness probe on Reverb’s /up:
CLI: 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.
3

Add the credentials and deploy

The Reverb application needs the same three credentials, and CACHE_STORE set to file, because it has no database. Add them with Create environment variable, then trigger a deployment and wait for it:
CLI: sevalla apps deployments trigger $REVERB_ID. MCP: set_environment_variables, then trigger_deployment.
4

Connect the main application to Reverb

Create internal connection (createApplicationInternalConnectionV3) with target_type app:
CLI: sevalla apps internal-connections create $APP_ID --target-id $REVERB_ID --target-type app. MCP: sevalla_api_create with createApplicationInternalConnectionV3 and the same body.
5

Point the main application at Reverb

List environment variables (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:
CLI: sevalla apps env-vars update $VAR_ID --app-id $APP_ID --value <value>. MCP: set_environment_variables, then trigger_deployment.
Then run the checks in Check from a terminal with $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.