Once ICanUp gained Reverb, background jobs, and Inertia SSR, PHP-FPM alone was no longer enough to represent the whole production application.
Three separate long-running processes need to stay alive: the queue worker, Reverb, and SSR.
Each can fail independently. More importantly, losing one of them does not always make the entire website obviously unavailable.
That is why I use Supervisor not merely as a convenient startup command but as the lifecycle contract for long-running application processes.

One Laravel Application Does Not Mean One Process
PHP-FPM handles HTTP requests in my production environment, but part of the application operates outside the normal HTTP request-response cycle.
The queue needs to process background jobs. Reverb needs to maintain WebSocket connections. SSR needs to be ready to render Inertia pages on the server.
These are separate processes with different commands, failure modes, and verification methods.
Process | Responsibility | What a user may notice when it fails |
|---|---|---|
Laravel queue worker | Background jobs | The site may still load while jobs accumulate or execute late |
Reverb | WebSocket and realtime events | HTTP still works, but live updates stop arriving |
Inertia SSR | Server rendering for Vue/Inertia | A page may still load while SSR or the SEO response becomes incorrect |
That Is What Makes Partial Failure Easy to Miss
When PHP-FPM fails, the problem is obvious because HTTP requests stop being processed correctly.
Long-running process failures are less obvious.
A stopped queue worker can remain unnoticed for minutes or hours. Reverb can fail while normal navigation remains healthy. SSR can fail only on one page or one rendering path.
"The site returns 200" and "all application processes are healthy" are different statements.
I Run Three Separate Supervisor Programs in Production
In the current ICanUp production environment, Supervisor manages separate processes for the worker, Reverb, and SSR.
The names are deliberately independent so each service can be checked or restarted separately.
icanup-worker:icanup-worker_00icanup-reverbicanup-ssrThis separation matters. I do not want to restart the entire application stack because only one process needs attention.
My First Post-Deployment Command Is a Real Status Check
sudo supervisorctl statusI expect the required processes to be in RUNNING state.
That is only the first verification layer.
icanup-worker:icanup-worker_00 RUNNINGicanup-reverb RUNNINGicanup-ssr RUNNINGRUNNING Only Means the Process Has Not Exited
Supervisor answers the question: "Is the process alive?" very well.
It cannot always answer: "Is this process doing its job correctly?"
Supervisor shows | What still needs separate verification |
|---|---|
worker = RUNNING | Whether queued jobs are actually being processed |
Reverb = RUNNING | Whether the expected port is listening and WebSocket connections work |
SSR = RUNNING | Whether a real page can complete server rendering |
The Queue Worker Is a Long-Running PHP Process
A Laravel queue worker does not exit after one job. It remains in memory and waits for more work.
After a new deployment, it can therefore continue running with previously loaded code until it is restarted correctly.
php artisan queue:restartqueue:restart tells workers to exit after their current work. Supervisor can then start them again.
During deployment I can also explicitly restart or verify the Supervisor group.
sudo supervisorctl restart icanup-worker:*Reverb Lives Outside Normal HTTP Requests
Reverb maintains long-lived WebSocket connections.
Its failure does not necessarily affect a normal HTTP request. A visitor may open a page while realtime features have already stopped working.
That is why Reverb has its own Supervisor program.
sudo supervisorctl restart icanup-reverbAfter restart, I verify more than Supervisor status. I also check that the server is actually listening on the expected ports.
ss -lntSSR Also Needs to Stay Alive
Adding Inertia SSR introduced another long-running process.
Its job is to accept rendering requests from Laravel and return server-rendered HTML for Inertia pages.
If the SSR process stops or begins failing, the failure may not look like a complete HTTP outage.
sudo supervisorctl restart icanup-ssrI also verify a real page response. This is how ICanUp previously exposed the difference between metadata that existed only in the browser and metadata present in the initial SSR HTML.
I described that case separately in my article about Inertia SSR and metadata in the initial HTML response.
curl -s https://icanup.com.ua/en/ \ | grep -E '<title>|og:title'Each Role Should Be a Separate Process
I do not combine the worker, Reverb, and SSR into one shell script that Supervisor sees as a single process.
If a combined process partially fails, it becomes harder to identify the failing role, restart only what is needed, and keep useful logs.
Supervisor|+-- icanup-worker|+-- icanup-reverb|+-- icanup-ssrConfiguration Should Define Lifecycle, Not Only the Command
For me, Supervisor configuration is more than command=....
I also want the working directory, process user, startup behavior, failure behavior, and shutdown semantics to be explicit.
[program:example-process]directory=/var/www/icanupcommand=<process-command>user=deploy autostart=trueautorestart=true startsecs=5stopsignal=TERMstopasgroup=truekillasgroup=true redirect_stderr=truestdout_logfile=<process-log>Why I Use autorestart
A long-running process can fail long after deployment has finished.
A runtime error, resource problem, or unexpected exception can terminate it later.
autorestart=true allows Supervisor to start it again without manual intervention.
An endless restart loop is not a fix, however. A repeatedly crashing process still needs log inspection and diagnosis.
The Process User Matters as Much as the Command
In my production environment, long-running CLI and SSR processes can run as deploy, while HTTP requests through PHP-FPM run as www-data.
That creates a separate filesystem permission contract.
I do not run application processes as root merely to bypass permission problems.
If several system users write shared files, permissions need to be designed explicitly. Supervisor does not repair an incorrect permission model automatically.
Changing Supervisor Config Requires More Than restart
When Supervisor configuration itself changes, I first ask Supervisor to reread it.
sudo supervisorctl rereadsudo supervisorctl updatesudo supervisorctl statusreread discovers configuration changes, while update applies the new program set.
That is different from restarting a process Supervisor already knows about.
Long-Running Processes Need the New Code After Deployment
Deployment is one of the main reasons I manage these processes through Supervisor.
After updating the Git revision, Composer dependencies, frontend build, or environment configuration, an old process may still remain in memory with old state.
A deployment therefore does not end when files are updated.
Deploy new code | vRebuild dependencies / caches | vRestart long-running processes | +-- Queue worker +-- Reverb +-- SSR | vVerify process state | vVerify real service behaviorMy Post-Deployment Order
- Update application code and dependencies.
- Run migrations and rebuild Laravel caches.
- Ask Laravel queue workers to exit gracefully.
- Restart the worker group under Supervisor.
- Restart Reverb.
- Restart SSR.
- Verify
supervisorctl status. - Verify listening ports for network services.
- Verify real HTTP and SSR output.
- Only then consider the deployment successful.
This Belongs in CI/CD, Not in a Manual Memory Checklist
I do not want to remember these steps manually on every deployment.
In ICanUp, restarting and verifying long-running processes is part of the deployment flow.
I described the broader setup in my article about a real Laravel CI/CD pipeline.
The Three Processes Do Not Scale the Same Way
Queue workers can naturally have multiple processes when job volume grows.
Supervisor supports process groups and multiple instances for that purpose.
Reverb and SSR on a single server have different constraints. Without an architectural reason, I do not start random duplicate processes that compete for the same port or endpoint.
Separate Logs Make Failures Easier to Diagnose
If the worker, Reverb, and SSR all write into one undifferentiated process log, every incident begins with identifying which service produced the message.
Separate logs make the relationship clearer:
worker failure -> worker logReverb failure -> Reverb logSSR failure -> SSR logLaravel application logging remains a separate layer. Supervisor logs and Laravel logs answer different questions.
What I Check When a Process Keeps Restarting
- Supervisor command and working directory.
- The user that runs the process.
- Environment variables available to the process.
- Permissions for required files and directories.
- The dedicated process log.
- Laravel log when the process boots Laravel.
- Whether the required port is already in use.
- Whether the same command works manually as the same system user.
Manual Verification Must Reproduce the Supervisor Context
A command working in my SSH shell does not prove it works under Supervisor.
The process can have a different user, working directory, environment, and PATH.
During diagnosis I therefore try to reproduce the same context Supervisor uses.
A command that works manually and a process Supervisor can keep stable in production are not always the same thing.
Supervisor Does Not Replace Health Checks
I deliberately separate process supervision from health verification.
Supervisor should keep the process alive. Deployment verification should prove that the process actually performs its function.
Layer | Example verification |
|---|---|
Process |
|
Network | Expected port is listening |
Application | A real job or SSR render succeeds |
Public behavior | The user-facing feature works |
The Worst Failure Is a Silent Partial Failure
The most dangerous situation for me is not a complete outage but a site that only looks healthy.
HTTP responds while the queue worker is stalled. A page opens while realtime is gone. The browser displays content while SSR fails and the initial HTML is wrong.
That is why the three long-running processes need independent management and independent verification.
What I Do Not Do
- Do not run the worker, Reverb, and SSR as one combined shell process.
- Do not run application processes as
rootfor convenience. - Do not treat
RUNNINGas sufficient proof of service health. - Do not manually start a second Reverb before checking Supervisor.
- Do not leave long-running processes on old code after deployment.
- Do not combine all process-role logs into one stream without a reason.
- Do not reboot the whole server merely because one application process failed.
- Do not verify production with only one HTTP 200 response.
My Short Supervisor Checklist
- Every long-running role has its own Supervisor program.
- Each program has the correct working directory.
- The process runs as the intended system user.
- autostart is enabled and autorestart behavior is intentional.
- Shutdown signals are propagated to child processes correctly.
- Worker, Reverb, and SSR have separate logs.
- After Supervisor config changes, run
rereadandupdate. - After deployment, long-running processes are restarted.
- After restart, verify
supervisorctl status. - After process verification, run a separate functional check.
What I Kept for Next Time
- Separate the HTTP runtime from long-running processes.
- Give the worker, Reverb, and SSR independent lifecycles.
- Do not confuse process status with service health.
- Refresh long-running processes after deployment.
- Verify Reverb through both process and network behavior.
- Verify SSR through a real HTML response.
- Verify the queue worker through actual job processing, not only RUNNING state.
- Do not hide permission or environment problems by running as root.
- Keep services independent enough that one failure does not require restarting the whole application stack.
Conclusion
On one ICanUp production server, different parts of the same Laravel application run at the same time: PHP-FPM, a queue worker, Reverb, and Inertia SSR.
Supervisor gives me one clear way to manage the three long-running processes: start them after reboot, restart them after failure, refresh them after deployment, and inspect their current state.
The most important lesson is different, however: process management and functional health verification are separate layers.
A worker can be RUNNING without doing useful work. Reverb can be RUNNING while realtime still needs verification. SSR can be RUNNING while a specific page still fails during server rendering.
My production contract is therefore simple: Supervisor keeps processes alive, while deployment and health checks prove that they are actually doing their job.



