How7o
  • Home
  • Tools
  • Prank Screens
  • Learn
  • Blog
  • Contact
Reading: How to Run Laravel Queue Workers in Production with Supervisor
Share
How7oHow7o
Font ResizerAa
  • OS
Search
  • Home
  • Tools
  • Prank Screens
  • Learn
  • Blog
  • Contact
Follow US
© 2024–2026 How7o. All rights reserved.
How7o > Free Laravel, PHP, WordPress & Server Tutorials > Web Development > How to Run Laravel Queue Workers in Production with Supervisor
Web Development

How to Run Laravel Queue Workers in Production with Supervisor

how7o
By how7o
Last updated: May 23, 2026
12 Min Read
Run Laravel queue workers with Supervisor
SHARE

To run Laravel queue workers in production, put Supervisor in charge of php artisan queue:work. Supervisor restarts crashed workers, runs multiple workers in parallel, captures their output to a log, and brings them all back on reboot. A 15-line config file in /etc/supervisor/conf.d/ is the entire setup.

Contents
  • Why queue:work alone isn’t enough
  • Prerequisites
  • Step 1 — install Supervisor
  • Step 2 — create the worker config
  • Step 3 — load the config and start workers
  • Step 4 — graceful restart after deploys
  • Step 5 — multiple queues with different priorities
  • Step 6 — log rotation
  • Step 7 — handle failed jobs
  • Step 8 — Horizon (optional, Redis-only)
  • Useful supervisorctl commands
  • Frequently asked questions
  • Related guides
  • References

Last verified: 2026-05-17 on Ubuntu 24.04 with Laravel 11, Supervisor 4.x, and PHP 8.3.

Why queue:work alone isn’t enough

In development, you run php artisan queue:work in a terminal and it dequeues jobs forever. In production this falls apart fast:

  • Closing the SSH session kills the worker.
  • A crashing job (out-of-memory, uncaught exception) takes the worker down with it — no auto-restart.
  • Server reboots leave no workers running until you SSH back in.
  • One worker can’t keep up with even modest traffic — you need several in parallel.

Supervisor solves all four. It’s a tiny process manager originally designed for exactly this kind of “keep N copies of this command running forever” task.

Laravel queue worker with Supervisor — config file, numprocs, graceful restart, log rotation

Prerequisites

  • A working Laravel app deployed to the server (see How to Install Laravel).
  • A queue driver configured — Redis (recommended), database, or SQS. For Redis setup see How to Install and Configure Redis on Ubuntu.
  • Root or sudo access.

Step 1 — install Supervisor

sudo apt update
sudo apt install -y supervisor

sudo systemctl enable --now supervisor

# Confirm
sudo supervisorctl status
# (empty — no programs defined yet)

Supervisor’s main config file is /etc/supervisor/supervisord.conf — don’t edit it. Per-program configs live in /etc/supervisor/conf.d/*.conf and are auto-loaded.

Step 2 — create the worker config

sudo tee /etc/supervisor/conf.d/laravel-worker.conf >/dev/null << 'EOF'
[program:laravel-worker]
process_name=%(program_name)s_%(process_num)02d
command=php /var/www/myapp/artisan queue:work redis --sleep=3 --tries=3 --max-time=3600 --timeout=120
autostart=true
autorestart=true
user=www-data
numprocs=4
redirect_stderr=true
stdout_logfile=/var/log/laravel-worker.log
stdout_logfile_maxbytes=50MB
stdout_logfile_backups=10
stopwaitsecs=130
EOF

Walking through each directive:

  • process_name — gives each of the numprocs workers a distinct name like laravel-worker_00, _01, etc.
  • command — what Supervisor runs. Adjust /var/www/myapp/ to your app path; redis to your queue driver.
  • --sleep=3 — when the queue is empty, sleep 3 seconds before polling again. Lower = faster pickup of new jobs, higher = less Redis traffic.
  • --tries=3 — failed jobs retry up to 3 times before being marked failed (recorded in the failed_jobs table).
  • --max-time=3600 — worker exits after 1 hour and Supervisor restarts it. Bounds memory growth from leaks in third-party packages.
  • --timeout=120 — kill any single job that runs longer than 120 seconds. Match this to the longest legitimate job duration; pair with stopwaitsecs below.
  • numprocs=4 — four worker processes in parallel. Tune based on load.
  • user=www-data — run as the same user as PHP-FPM and the web server. Required so the worker can read app files and write to storage/.
  • stopwaitsecs=130 — when Supervisor stops the program, wait up to 130 seconds for workers to finish their current job (slightly longer than --timeout). Without this, in-flight jobs get killed mid-execution.

Step 3 — load the config and start workers

sudo supervisorctl reread       # detect new config files
sudo supervisorctl update       # start any newly-defined programs
sudo supervisorctl status       # see what's running

Expected output:

laravel-worker:laravel-worker_00   RUNNING   pid 12345, uptime 0:00:05
laravel-worker:laravel-worker_01   RUNNING   pid 12346, uptime 0:00:05
laravel-worker:laravel-worker_02   RUNNING   pid 12347, uptime 0:00:05
laravel-worker:laravel-worker_03   RUNNING   pid 12348, uptime 0:00:05

Four workers running. Drop a job into the queue (from Tinker or a controller) and tail the log to see one of them pick it up:

tail -f /var/log/laravel-worker.log

Step 4 — graceful restart after deploys

# After deploying new code
php artisan queue:restart

# That's it — Supervisor takes care of the rest

queue:restart sets a Redis flag that workers check between jobs. Each worker finishes its current job, sees the flag, and exits cleanly. Supervisor restarts each one with the updated code. No in-flight jobs are lost.

Include php artisan queue:restart in your deploy script (after composer install and migrations). Forgetting it means workers keep running the previous version of your code until the next --max-time expiry or server restart — a subtle bug that’s painful to debug.

Step 5 — multiple queues with different priorities

Apps often have a fast “interactive” queue (emails, notifications) and a slow “batch” queue (reports, exports). Run dedicated workers per queue:

sudo tee /etc/supervisor/conf.d/laravel-worker.conf >/dev/null << 'EOF'
[program:laravel-worker-default]
process_name=%(program_name)s_%(process_num)02d
command=php /var/www/myapp/artisan queue:work redis --queue=high,default --sleep=1 --tries=3 --max-time=3600 --timeout=60
autostart=true
autorestart=true
user=www-data
numprocs=6
redirect_stderr=true
stdout_logfile=/var/log/laravel-worker-default.log
stopwaitsecs=70

[program:laravel-worker-long]
process_name=%(program_name)s_%(process_num)02d
command=php /var/www/myapp/artisan queue:work redis --queue=long --sleep=5 --tries=2 --max-time=3600 --timeout=600
autostart=true
autorestart=true
user=www-data
numprocs=2
redirect_stderr=true
stdout_logfile=/var/log/laravel-worker-long.log
stopwaitsecs=610
EOF

sudo supervisorctl reread
sudo supervisorctl update

The --queue=high,default argument tells the worker to process the high queue first, falling back to default when high is empty. Six fast workers handle interactive jobs; two long workers (with a 10-minute --timeout) handle slow batch work without blocking the fast ones.

From your app, dispatch jobs to specific queues: SendInvoiceEmail::dispatch($order)->onQueue('high');.

Step 6 — log rotation

Supervisor’s built-in stdout_logfile_maxbytes rotates at 50 MB by default but only keeps the last 10 files. For longer retention, hand log rotation off to logrotate:

sudo tee /etc/logrotate.d/laravel-worker >/dev/null << 'EOF'
/var/log/laravel-worker*.log {
    daily
    rotate 14
    compress
    delaycompress
    missingok
    notifempty
    copytruncate
}
EOF

copytruncate is important here — Supervisor keeps the log file open, so a regular log rotation that renames the file would orphan Supervisor’s handle. copytruncate copies the contents to the rotated name and truncates in place, keeping Supervisor happy.

Step 7 — handle failed jobs

# Create the failed_jobs table (if you haven't already)
php artisan queue:failed-table
php artisan migrate

# List failed jobs
php artisan queue:failed

# Retry a specific failed job
php artisan queue:retry 5e3ab7c2-...

# Retry all failed jobs
php artisan queue:retry all

# Forget a failed job (permanently)
php artisan queue:forget 5e3ab7c2-...

# Clear all failed jobs
php artisan queue:flush

After a job exceeds --tries, it’s moved to the failed_jobs table. Review failures regularly — most are bugs you can fix and retry, but some are permanent (e.g., a deleted user, a network call to a now-deprecated API) and should be investigated, fixed at the source, then flushed.

Step 8 — Horizon (optional, Redis-only)

If you’re on Redis, install Horizon for the dashboard:

composer require laravel/horizon
php artisan horizon:install
php artisan migrate

Then replace your Supervisor config to run horizon instead of queue:work:

[program:laravel-horizon]
process_name=%(program_name)s
command=php /var/www/myapp/artisan horizon
autostart=true
autorestart=true
user=www-data
numprocs=1
redirect_stderr=true
stdout_logfile=/var/log/laravel-horizon.log
stopwaitsecs=3600

Horizon itself manages worker processes based on the configuration in config/horizon.php — you set processes, tries, and timeout there instead of as CLI arguments to queue:work. Visit /horizon in your browser for the dashboard (protected by Laravel’s auth gate).

Useful supervisorctl commands

sudo supervisorctl status                  # all programs
sudo supervisorctl start  laravel-worker:*  # start every worker in the group
sudo supervisorctl stop   laravel-worker:*  # stop every worker
sudo supervisorctl restart laravel-worker:* # full restart (use queue:restart for graceful)
sudo supervisorctl tail   laravel-worker_00 # last stdout chunk for one worker
sudo supervisorctl tail   laravel-worker_00 -f stdout  # follow live

Frequently asked questions

Should I use Horizon or plain Supervisor + queue:work?

Horizon is the dashboard layer on top of queue workers — beautiful UI, real-time metrics, failed-job inspection, throughput graphs. It still needs a process manager underneath (Supervisor or systemd) to keep horizon running. For Redis-backed queues, Horizon is worth the extra dependency. For database or SQS queues, Horizon isn’t supported and Supervisor + queue:work is the standard. The skills overlap completely; this guide’s Supervisor config is what Horizon docs themselves recommend.

Why does php artisan queue:restart not always kill workers immediately?

By design. queue:restart flags every worker to exit after finishing their current job. This prevents losing work mid-execution. If a worker is processing a 5-minute job when you call queue:restart, it finishes that job and then exits. Supervisor starts a fresh worker (now running your updated code) in its place. Workers stuck on a job longer than --timeout are killed forcefully — which is why setting --timeout matters.

How many worker processes do I need?

Depends on job type. I/O-bound jobs (sending emails, calling external APIs): match worker count to expected concurrent jobs — 4–8 is typical for a small app. CPU-bound jobs (image processing, PDF generation): worker count = CPU cores; more than that wastes context-switches. Start with 4, watch queue depth via php artisan queue:monitor or Horizon, scale up if backlog grows. Each worker uses 30–80 MB of PHP memory, so a small VPS can easily run a dozen.

What happens to in-flight jobs during a server reboot?

If your queue driver is database or Redis, in-flight jobs are reserved — marked as being processed by a specific worker. When the worker process dies (or the server reboots) without completing the job, the reservation has a TTL (default 60s, set via --timeout) after which the job becomes available again and another worker picks it up. The job runs again from the start. Design jobs to be idempotent: running twice should leave the same state as running once.

Related guides

  • How to Install Laravel
  • How to Install and Configure Redis on Ubuntu
  • How to Create a Login and Registration System in Laravel
  • How to Run a Cron Job as a Non-Root User
  • How to Stop Cron Output

References

Laravel queues documentation: laravel.com/docs/queues. Laravel Horizon: laravel.com/docs/horizon. Supervisor documentation: supervisord.org. logrotate manual: man logrotate.

TAGGED:background-jobsLaravelphpqueuesupervisorUbuntu

Sign Up For Daily Newsletter

Be keep up! Get the latest breaking news delivered straight to your inbox.
[mc4wp_form]
By signing up, you agree to our Terms of Use and acknowledge the data practices in our Privacy Policy. You may unsubscribe at any time.
Share This Article
Facebook Copy Link Print
Previous Article Nginx as a reverse proxy for a Node.js app on Ubuntu How to Set Up Nginx as a Reverse Proxy for Node.js on Ubuntu
Leave a Comment

Leave a Reply Cancel reply

You must be logged in to post a comment.

FacebookLike
XFollow
PinterestPin
InstagramFollow
Most Popular
Run Laravel queue workers with Supervisor
How to Run Laravel Queue Workers in Production with Supervisor
May 23, 2026
Nginx as a reverse proxy for a Node.js app on Ubuntu
How to Set Up Nginx as a Reverse Proxy for Node.js on Ubuntu
May 23, 2026
Install and configure Redis on Ubuntu for Laravel and WordPress
How to Install and Configure Redis on Ubuntu (for Laravel & WordPress)
May 23, 2026
Harden a fresh Ubuntu VPS with UFW, Fail2Ban, and SSH key auth
How to Harden a Fresh Ubuntu VPS: UFW + Fail2Ban + SSH Key Auth
May 23, 2026
Set up Let's Encrypt SSL with Certbot on Ubuntu
How to Set Up Let’s Encrypt SSL with Certbot on Ubuntu (Apache & Nginx)
May 23, 2026

You Might Also Like

Remove unwanted characters from a PHP string with regex
Web Development

How to Remove Unwanted Characters from a String in PHP

5 Min Read
Open a file selection dialog from a button click
Web Development

How to Open a File Dialog When Clicking a Button

4 Min Read
JavaScript check valid URL — URL constructor vs regex
Web Development

How to Check if a JavaScript String Is a Valid URL

5 Min Read
Deleting a Laravel user cascades to remove related posts, photos, and notifications
Web Development

How to Delete Related Records in Laravel Eloquent

6 Min Read
How7o

We provide tips, tricks, and advice for improving websites and doing better search.

Tools

  • Age Calculator
  • Word Counter
  • Image Upscaler
  • Password Generator
  • QR Code Generator
  • See all tools→

Pranks

  • Fake Blue Screen Prank
  • Hacker Typer
  • Fake iMessage Generator
  • Windows XP Crash Prank
  • Windows 11 Update Prank
  • See all prank screens →

Company

  • About Us
  • Blog
  • Contact
  • Privacy Policy
  • Terms of Service
  • Sitemap
© 2024–2026 How7o. All rights reserved.
Welcome Back!

Sign in to your account

Username or Email Address
Password

Lost your password?