Laravel queues process background jobs — emails, notifications, file processing, API calls, and anything else you push onto the queue. A queue worker must be running for those jobs to execute.
There are two approaches: a cron-based worker (self-service, simpler) and a Supervisor-managed persistent worker (more robust, requires admin access).
Option 1: Cron Job (Self-Service)
php artisan queue:work --stop-when-empty processes all waiting jobs and then exits. Run it every minute via cron and you get near-real-time queue processing without a long-running process.
Go to Hosting Mode → Cron Jobs → New Cron Job and add:
* * * * * /usr/bin/php8.2 /home/username/domain.com/laravel-app/artisan queue:work --stop-when-empty >> /home/username/domain.com/logs/queue.log 2>&1
Replace php8.2 with the PHP version your app uses (check under Hosting Mode → PHP).
Characteristics of this approach:
- No persistent process — no risk of a stuck worker holding database connections.
- Maximum latency is ~60 seconds (the time between cron ticks).
- Simple to set up, no admin involvement required.
- If cron fires while a previous instance is still running, two workers briefly overlap — safe with standard Laravel queue drivers (database, Redis).
[!TIP] This is the recommended starting point for most applications. Move to Supervisor only when you need lower latency or guaranteed single-worker semantics.
Option 2: Supervisor (Persistent Worker)
For lower latency or high-throughput queues, a persistent queue:work --daemon process is more efficient. This requires Supervisor to be installed on the server and configured by your server admin.
Request your admin to create a Supervisor program config. The standard config looks like this:
; /etc/supervisor/conf.d/laravel-worker-username.conf
[program:laravel-worker-username]
process_name=%(program_name)s_%(process_num)02d
command=/usr/bin/php8.2 /home/username/domain.com/laravel-app/artisan queue:work --sleep=3 --tries=3 --max-time=3600
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
user=username
numprocs=1
redirect_stderr=true
stdout_logfile=/home/username/domain.com/logs/worker.log
stopwaitsecs=3600
After adding the config:
sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl start laravel-worker-username:*
Characteristics of this approach:
- Worker stays running continuously. New jobs are picked up within seconds.
- Supervisor restarts the worker automatically if it crashes.
- Requires admin access to write the Supervisor config.
Restarting Workers After Deployment
[!WARNING]
queue:work --daemoncaches the application in memory at startup. After deploying new code, the running worker is still executing the old code until it is restarted.
Always restart queue workers after every deployment:
php artisan queue:restart
This signals all running queue:work daemons to exit cleanly after they finish their current job. Supervisor then starts a fresh worker that loads the new code.
If you are using the cron approach, queue:restart is still relevant if you run queue:work without --stop-when-empty in any context — but with --stop-when-empty the worker exits after each cron tick anyway, so each run picks up fresh code automatically.
Monitoring and Failed Jobs
Check the failed jobs table:
php artisan queue:failed --path=/home/username/domain.com/laravel-app/
Retry a specific failed job:
php artisan queue:retry {job-id} --path=/home/username/domain.com/laravel-app/
Retry all failed jobs:
php artisan queue:retry all --path=/home/username/domain.com/laravel-app/
[!NOTE] The
failed_jobstable must exist in your database. Laravel creates it via migration — runphp artisan migrateif it is missing.