skip to content

After a deploy, a Laravel Reverb server keeps running old code; why, and how do reverb:restart and a process manager fix it?

level: middleimportance: should knowfreq 28%

answer

  1. a long-running process keeps old code
  2. reverb:restart writes a cache key
  3. servers poll it every five seconds
  4. graceful disconnect, then stop
  5. Supervisor starts it again

basics

~20 s

Reverb is a long-running process that keeps the code it booted with. reverb:restart stores a timestamp in the cache; each server notices it, disconnects clients gracefully and exits, and a process manager such as Supervisor starts it with the new code.

solid answer

~50 s

`reverb:start` boots the framework once and then runs an event loop indefinitely, so code, config and `.env` changes deployed afterwards are invisible to it, exactly as with a queue worker. `php artisan reverb:restart` does not kill anything itself: it writes the current time to the cache key `laravel:reverb:restart`. Every running Reverb server checks that key every five seconds; when it changes, the server disconnects all its connections gracefully and stops. It is then up to a **process manager** such as Supervisor, with `autorestart`, to start `reverb:start` again with the new code, and Echo clients reconnect on their own. Two things follow: the cache store must be one that every Reverb server reads (the skeleton's database store works, a per-host file or array store does not), and without a process manager a restart simply leaves the server down.

code

bash · 4 lines
bash
php artisan migrate --force
php artisan optimize
php artisan queue:restart
php artisan reverb:restart

go deeper

for a junior

Recall that Reverb is a long-running process and that reverb:restart is part of every deploy.

for a middle

Explain the cache-key signal, the five-second check, the graceful disconnect and why a process manager must restart the server.

for a senior

Make restarts reliable across hosts with a shared cache, order them after the deploy completes, and manage the reconnect burst.

for a principal

Decide between all-at-once and rolling Reverb restarts for the product, weighing deploy speed against reconnect storms and user-visible disruption.

## Why a deploy does not reach Reverb PHP-FPM builds the application from scratch on every request, so new code takes effect as soon as the files change. Reverb is different: `php artisan reverb:start` boots Laravel **once** and then runs a ReactPHP event loop for as long as it lives. Everything loaded at boot stays in memory: - the application's classes and any code Reverb calls, such as its internal event listeners - the configuration, including `config/reverb.php` values like `allowed_origins` and app credentials - the environment it was started with So a deploy that changes any of these has no effect on a running Reverb server until the process is replaced. This is the same rule that applies to queue workers. ## What reverb:restart actually does `php artisan reverb:restart` is a **signal**, not a kill: 1. It stores the current timestamp in the cache under the key `laravel:reverb:restart`, forever. 2. Each Reverb server remembered the key's value when it started and checks it on a **five-second** timer. 3. When the value differs, the server **gracefully disconnects** every connection it holds and stops listening, and the process exits. 4. A process manager notices the exit and starts `reverb:start` again, which boots the new code. Browsers running Echo see their connection close and reconnect automatically, landing on the fresh process. ## What has to be true for it to work | Requirement | What goes wrong otherwise | |---|---| | A process manager with auto-restart | the server stops and stays down | | A cache store shared by every Reverb server | servers that read a different store never see the signal | | The command runs against the same cache as the servers | a deploy script with another `CACHE_STORE` signals nobody | | The deploy finishes before the signal | servers restart onto half-deployed code | The Laravel 13 skeleton's `CACHE_STORE=database` is shared by every host that uses the same database, so it works across servers. A `file` store on each host, or the `array` store, breaks the signal between machines. ## Running it under Supervisor A minimal Supervisor program for the whiteboard's Reverb server: - `command=php /var/www/whiteboard/artisan reverb:start` - `autorestart=true`, so the exit after a restart signal is followed by a new start - a `user` other than root, and a raised `minfds` in `[supervisord]` for high connection counts - a `stopwaitsecs` long enough for graceful disconnects when Supervisor itself stops the program ## The deploy sequence 1. Put the new release in place and build assets. 2. Run migrations and cache configuration as your deploy normally does. 3. Run `php artisan reverb:restart`, alongside `queue:restart` for the queue workers. 4. Watch that Reverb servers come back and clients reconnect. ## What a restart costs A restart disconnects every whiteboard user at once, and they reconnect within seconds. That is harmless for a small app but can produce a reconnect burst on a large one, and any state only held in the browser (a stroke in flight) must survive a reconnect. Presence channels will show members leaving and rejoining. Restarting servers one by one behind a load balancer spreads the burst, at the cost of a slower rollout. ## Stopping by signal Reverb also handles `SIGINT` and `SIGTERM`: stopping the process from the terminal or from the process manager triggers the same graceful disconnect before exit, so a plain Supervisor restart is also safe; `reverb:restart` is the convenient way to reach every server at once without shell access to each.

  • You ran reverb:restart on the web server, but the Reverb server on another host never restarted; what is the likely cause?
    The two hosts do not share a cache store. `reverb:restart` writes the signal to the cache of the machine that ran it, and the Reverb host polls its own store. With a per-host `file` store, or different `CACHE_STORE` values, the signal never arrives. Use a shared store such as the database or Redis.
  • Why is reverb:restart not enough on a server where Reverb was started by hand in a terminal?
    The command only asks Reverb to disconnect clients and exit. Nothing then starts it again, so the whiteboard loses realtime until someone runs `reverb:start` manually. A process manager with automatic restart is what turns the exit into a restart.

saying these in an interview costs you the question

  • Reverb reloads changed PHP files on every message, so deploys apply immediately.
  • reverb:restart kills the server process directly over a signal.
  • reverb:restart starts a fresh server by itself, so Supervisor is optional.
  • Any cache store works for the restart signal, even a per-host file cache.
  • Clients must reload the page after a Reverb restart to reconnect.