A queue lets you move slow work — sending emails, generating reports, processing uploaded files — out of an HTTP request into a background process. Both Laravel and Symfony can put jobs into a queue, but the job handler itself is a separate long-running PHP process that needs to be watched. That is exactly what Supervisor is for.
Why a separate worker process is needed
A command like php artisan queue:work in Laravel or
messenger:consume in Symfony starts once and keeps running
until it is stopped or crashes from an unhandled exception. Without a
supervisor, such a process eventually terminates, and jobs pile up in the
queue with no handler at all. Setting up Symfony itself on a server is
covered in the article on
Symfony on a VPS.
Supervisor is a daemon that starts the specified processes, restarts them on a crash, and lets you keep several identical workers running at once for parallel queue processing.
Installation and a base config
sudo apt install supervisor
sudo mkdir -p /etc/supervisor/conf.d
sudo nano /etc/supervisor/conf.d/laravel-worker.conf
An example config for a Laravel queue with several workers:
[program:laravel-worker]
process_name=%(program_name)s_%(process_num)02d
command=php /var/www/app/artisan queue:work redis --sleep=3 --tries=3 --max-time=3600
directory=/var/www/app
autostart=true
autorestart=true
user=www-data
numprocs=4
redirect_stderr=true
stdout_logfile=/var/www/app/storage/logs/worker.log
stopwaitsecs=3600
Key config settings
| Setting | Value | Why it matters |
|---|---|---|
| numprocs | 4 | number of parallel workers per queue |
| autorestart | true | restart when the process crashes |
| stopwaitsecs | 3600 | time to finish the current job before kill |
| max-time / --time-limit | 3600 | scheduled worker restart once an hour |
The scheduled restart matters: a PHP process that runs for hours gradually accumulates memory from leaks in third-party libraries. Once an hour, Supervisor simply brings up a fresh copy of the process instead of the old one.
Management commands and applying the config
supervisorctl reread— Supervisor sees a new or changed config file inconf.d.supervisorctl update— applies the changes: starts new programs, stops removed ones.supervisorctl restart laravel-worker:*— restarts every worker in the group after deploying new code.supervisorctl status— shows the state of each worker and the time of its last start.
Common reasons jobs get stuck or lost
After deploying a new release, old workers keep running the old code
still in memory until they are restarted by hand with
supervisorctl restart — that step is worth adding to the
deploy script as mandatory. Another common cause is a worker holding a
database connection that MySQL drops after an idle timeout, and the next
job then fails with "MySQL server has gone away". For CLI worker runs it
helps to understand the difference from request handling through FPM —
covered in the article on PHP CLI and
FPM. The easiest way to diagnose crashes is through the worker's own log
and the general PHP error log — the parsing process is described in the
article on parsing PHP logs.
Checklist for queue setup in production
- Set
--triesand--max-timein the worker command so a stuck job does not block the queue forever. - Add
supervisorctl restartto the deploy script right after updating the code on the server. - Set up a separate log for workers and monitor the queue length so you notice a growing backlog before your users do.