Supervisor Setup for Laravel Queue Workers: Complete Production Guide (2026)
Laravel queues allow us to process slow tasks in the background.
Instead of making the user wait, we can send work such as emails, reports, notifications, imports, and API requests to a queue.
For example:
SendOrderConfirmation::dispatch($order);
The application adds the job to the queue, and a queue worker processes it separately.
During local development, we commonly start the worker manually:
php artisan queue:work
This is fine while developing.
However, it is not a reliable production setup.
The worker stops when:
-
You close the SSH connection
-
The server restarts
-
The worker crashes
-
PHP reaches a memory limit
-
The process is killed
-
A deployment restarts the application
This is where Supervisor becomes useful.
Supervisor keeps the Laravel queue worker running, restarts it when it exits, and gives us commands for checking and controlling worker processes.
Laravel’s official queue documentation recommends using a process manager such as Supervisor to monitor queue workers and restart them when they exit.
In this guide, we will install Supervisor on Ubuntu, configure Laravel queue workers, manage multiple queues, handle deployments, and fix common Supervisor errors.
What Is Supervisor?
Supervisor is a process-control system for Unix-like operating systems.
It can:
-
Start background processes
-
Restart processes after failure
-
Run processes automatically after a server reboot
-
Manage multiple process instances
-
Capture process output
-
Show whether a process is running or stopped
Supervisor consists mainly of two parts:
supervisord
This is the main Supervisor service that controls the processes.
supervisorctl
This is the command-line tool used to check and control those processes.
Supervisor is designed to monitor programs that remain in the foreground rather than programs that detach and run as their own daemons. Laravel’s queue:work command works well with this model.
Why Laravel Queue Workers Need Supervisor
A Laravel queue worker is a long-running PHP process.
When you run:
php artisan queue:work
the command continues waiting for new jobs.
If the command stops, jobs remain in the queue and are not processed.
You could manually log in to the server and start the worker again, but that is not practical for a production application.
Supervisor solves this by monitoring the process.
A simplified workflow looks like this:
Laravel dispatches a job
↓
The job enters Redis or the database queue
↓
Supervisor keeps queue:work running
↓
The Laravel worker receives the job
↓
The job is processed
If the worker unexpectedly exits, Supervisor starts it again.
Requirements
This guide assumes that you have:
-
An Ubuntu server
-
A deployed Laravel application
-
SSH access
-
PHP installed
-
Composer dependencies installed
-
A configured queue connection
-
Permission to run
sudocommands
Your project may be located at a path such as:
/var/www/example.com
Replace that path with your actual Laravel project path throughout this guide.
Step 1: Configure the Laravel Queue Connection
Open the Laravel .env file:
cd /var/www/example.com
nano .env
Choose a queue connection.
For a database queue:
QUEUE_CONNECTION=database
For Redis:
QUEUE_CONNECTION=redis
Avoid using the sync connection in production when you expect jobs to run in the background:
QUEUE_CONNECTION=sync
The sync driver executes the job immediately during the current web request. It does not require a background queue worker.
After changing the .env file, clear and rebuild the configuration cache:
php artisan config:clear
php artisan config:cache
Confirm the active queue configuration:
php artisan config:show queue
Step 2: Prepare the Database Queue
Skip this step when using Redis or another non-database queue driver.
For a database queue, Laravel needs a table to store pending jobs.
In newer Laravel applications, the migration may already exist.
Check your migrations:
ls database/migrations | grep jobs
When the migration does not exist, generate it:
php artisan make:queue-table
Then run:
php artisan migrate --force
You may also need a failed-jobs table.
Generate its migration when necessary:
php artisan make:queue-failed-table
Run the migration:
php artisan migrate --force
Now Laravel can store queued and failed jobs in the database.
Step 3: Test the Queue Worker Manually
Before configuring Supervisor, confirm that the Laravel queue works manually.
Move to the project directory:
cd /var/www/example.com
Run one worker:
php artisan queue:work
Dispatch a test job from your application.
You can also process only one job:
php artisan queue:work --once
Useful testing options include:
php artisan queue:work --tries=3 --timeout=60
If the manual command fails, Supervisor will not fix the underlying Laravel error.
First verify:
-
The
.envqueue connection -
Database or Redis connectivity
-
PHP extensions
-
File permissions
-
Job code
-
Application logs
Check Laravel logs:
tail -f storage/logs/laravel.log
Only continue after the worker operates correctly from the command line.
Step 4: Install Supervisor on Ubuntu
Update the package list:
sudo apt update
Install Supervisor:
sudo apt install supervisor -y
Check its service status:
sudo systemctl status supervisor
You should see a status similar to:
Active: active (running)
Enable Supervisor during system startup:
sudo systemctl enable supervisor
Start it when it is not already running:
sudo systemctl start supervisor
Check the installed version:
supervisord --version
Step 5: Find the Correct PHP Path
Do not assume the php command uses the expected PHP version.
Check its location:
which php
Example output:
/usr/bin/php
Check its version:
php -v
Your server may have multiple PHP versions installed.
For example:
/usr/bin/php8.3
/usr/bin/php8.4
Use the PHP binary that matches your application requirements.
Example:
/usr/bin/php8.3 /var/www/example.com/artisan queue:work
Test the full command manually before adding it to Supervisor:
/usr/bin/php8.3 /var/www/example.com/artisan queue:work --once
Step 6: Create the Supervisor Configuration
Supervisor program configuration files are commonly stored in:
/etc/supervisor/conf.d/
Create a new configuration:
sudo nano /etc/supervisor/conf.d/laravel-worker.conf
Add the following configuration:
[program:laravel-worker]
process_name=%(program_name)s_%(process_num)02d
command=/usr/bin/php /var/www/example.com/artisan queue:work --sleep=3 --tries=3 --timeout=60 --max-time=3600
directory=/var/www/example.com
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
user=www-data
numprocs=2
redirect_stderr=true
stdout_logfile=/var/www/example.com/storage/logs/worker.log
stdout_logfile_maxbytes=20MB
stdout_logfile_backups=5
stopwaitsecs=3600
Supervisor uses INI-style configuration files for defining and controlling programs.
Let us understand every setting.
Understanding the Supervisor Configuration
Program name
[program:laravel-worker]
This defines the program name.
You will use this name in commands such as:
sudo supervisorctl restart laravel-worker:*
Process name
process_name=%(program_name)s_%(process_num)02d
This creates names such as:
laravel-worker_00
laravel-worker_01
This is especially useful when running multiple worker processes.
Command
command=/usr/bin/php /var/www/example.com/artisan queue:work --sleep=3 --tries=3 --timeout=60 --max-time=3600
This is the command Supervisor runs.
Always use absolute paths where possible.
The command contains several worker options.
--sleep=3
--sleep=3
When no jobs are available, the worker waits three seconds before checking again.
This option mainly affects queue drivers where polling is used.
--tries=3
--tries=3
A job can be attempted up to three times before it is marked as failed.
Individual jobs may define their own $tries property.
--timeout=60
--timeout=60
The worker stops a job when it runs for longer than 60 seconds.
Make sure this value is shorter than the queue connection’s retry_after value. Otherwise, the queue system may make the same job available while the original worker is still processing it.
--max-time=3600
--max-time=3600
The worker exits after running for one hour.
Because Supervisor has autorestart=true, it starts a fresh worker.
Periodically restarting long-running workers can help release memory accumulated by the application or third-party packages.
Working directory
directory=/var/www/example.com
This tells Supervisor which directory to use before executing the command.
It helps Laravel locate files using the expected project context.
Automatic startup
autostart=true
Supervisor starts the worker automatically when Supervisor starts.
This means the worker can return after a server reboot.
Automatic restart
autorestart=true
Supervisor restarts the worker when it exits.
Laravel queue workers may exit after:
-
A queue restart signal
-
Reaching a maximum runtime
-
Reaching a memory limit
-
An unexpected PHP error
-
A server-level interruption
Process-group handling
stopasgroup=true
killasgroup=true
These settings help Supervisor stop the worker and its child processes together.
They are useful when a job launches another command or process.
Linux user
user=www-data
The worker runs as the www-data user.
This user must have permission to:
-
Read the Laravel project
-
Access the
.envfile -
Write to
storage -
Write to
bootstrap/cache -
Create exported files
-
Read uploaded files when required
Your server may use a different deployment user, such as:
user=deploy
or:
user=ubuntu
Use the correct user for your server configuration.
Number of worker processes
numprocs=2
Supervisor starts two queue worker processes.
This allows two jobs to be processed at the same time.
Do not increase this value without considering:
-
Available memory
-
CPU capacity
-
Database connections
-
External API limits
-
Job execution time
-
Whether jobs can safely run simultaneously
More workers do not always mean better performance.
Error redirection
redirect_stderr=true
This redirects standard error output to the standard output log.
It keeps process messages in one log file.
Worker log
stdout_logfile=/var/www/example.com/storage/logs/worker.log
Supervisor stores the process output in this file.
Supervisor supports activity logs and separate output logs for child processes.
Ensure the configured user can write to this path.
Log rotation
stdout_logfile_maxbytes=20MB
stdout_logfile_backups=5
This prevents the worker log from growing forever.
When the file reaches 20 MB, Supervisor rotates it and keeps up to five backups.
Graceful shutdown waiting time
stopwaitsecs=3600
Supervisor waits for the process to exit before forcefully killing it.
This value should be longer than your longest valid job.
For example, when a report job may take 15 minutes, a value of only 60 seconds may terminate the worker before that job completes.
Step 7: Load the Supervisor Configuration
After saving the file, ask Supervisor to check for new or changed configurations:
sudo supervisorctl reread
Example output:
laravel-worker: available
Apply the changes:
sudo supervisorctl update
Start the workers:
sudo supervisorctl start laravel-worker:*
Check the status:
sudo supervisorctl status
A healthy result looks similar to:
laravel-worker:laravel-worker_00 RUNNING
laravel-worker:laravel-worker_01 RUNNING
Supervisor’s command-line client reads the Supervisor configuration and communicates with the running supervisord service to control managed programs.
Useful Supervisor Commands
Check all processes
sudo supervisorctl status
Start the Laravel workers
sudo supervisorctl start laravel-worker:*
Stop the Laravel workers
sudo supervisorctl stop laravel-worker:*
Restart the Laravel workers
sudo supervisorctl restart laravel-worker:*
Restart all Supervisor-managed programs
sudo supervisorctl restart all
Be careful with this command because Supervisor may manage services unrelated to Laravel.
Reload changed configuration
sudo supervisorctl reread
sudo supervisorctl update
Use these commands after adding or changing a program configuration.
Step 8: Test the Production Worker
Once Supervisor reports the worker as running, dispatch a test job.
For example:
TestQueueJob::dispatch();
Watch the Laravel log:
tail -f /var/www/example.com/storage/logs/laravel.log
Watch the Supervisor worker output:
tail -f /var/www/example.com/storage/logs/worker.log
For the database queue, inspect pending jobs:
SELECT * FROM jobs ORDER BY id DESC;
The job should disappear from the jobs table after successful processing.
Check failed jobs:
php artisan queue:failed
Running a Specific Queue
Laravel jobs may be assigned to named queues.
For example:
SendInvoice::dispatch($invoice)->onQueue('emails');
Configure the worker to process only that queue:
command=/usr/bin/php /var/www/example.com/artisan queue:work --queue=emails --sleep=3 --tries=3 --timeout=60
This worker ignores jobs in other queues.
Processing Queues by Priority
You may have queues such as:
high
default
low
Configure the worker:
command=/usr/bin/php /var/www/example.com/artisan queue:work --queue=high,default,low --sleep=3 --tries=3 --timeout=60
Laravel checks the queues in the specified order, so jobs in high are processed before jobs from the lower-priority queues while high-priority jobs are available.
This is useful for separating:
-
Payment processing
-
Customer notifications
-
Report generation
-
Bulk imports
-
Low-priority cleanup tasks
Separate Workers for Different Queues
For better control, create separate Supervisor programs.
Example:
[program:laravel-high-worker]
process_name=%(program_name)s_%(process_num)02d
command=/usr/bin/php /var/www/example.com/artisan queue:work --queue=high --sleep=1 --tries=3 --timeout=60
directory=/var/www/example.com
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
user=www-data
numprocs=3
redirect_stderr=true
stdout_logfile=/var/www/example.com/storage/logs/high-worker.log
stopwaitsecs=3600
[program:laravel-default-worker]
process_name=%(program_name)s_%(process_num)02d
command=/usr/bin/php /var/www/example.com/artisan queue:work --queue=default --sleep=3 --tries=3 --timeout=120
directory=/var/www/example.com
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
user=www-data
numprocs=2
redirect_stderr=true
stdout_logfile=/var/www/example.com/storage/logs/default-worker.log
stopwaitsecs=3600
[program:laravel-report-worker]
process_name=%(program_name)s_%(process_num)02d
command=/usr/bin/php /var/www/example.com/artisan queue:work --queue=reports --sleep=5 --tries=2 --timeout=900
directory=/var/www/example.com
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
user=www-data
numprocs=1
redirect_stderr=true
stdout_logfile=/var/www/example.com/storage/logs/report-worker.log
stopwaitsecs=1800
This setup gives different resources and timeouts to different types of jobs.
For example:
-
Three workers for urgent jobs
-
Two workers for normal jobs
-
One worker for memory-heavy reports
Safe Queue Worker Restart During Deployment
Queue workers are long-running processes.
They load the application into memory when they start and do not automatically reload changed PHP files after every job.
After deploying new code, tell the existing workers to exit gracefully:
php artisan queue:restart
Laravel stores a restart signal in the configured cache. Workers finish their current jobs and then exit. A process manager such as Supervisor should start them again automatically.
A deployment sequence may look like this:
cd /var/www/example.com
git pull origin main
composer install --no-dev --optimize-autoloader
php artisan migrate --force
php artisan optimize:clear
php artisan config:cache
php artisan route:cache
php artisan view:cache
php artisan queue:restart
Confirm the worker status afterward:
sudo supervisorctl status
Do not rely only on this command
sudo supervisorctl restart laravel-worker:*
This may immediately stop workers, including workers that are currently processing a job.
php artisan queue:restart is usually safer because it allows the current job to complete before the worker exits.
Configure Memory and Job Limits
Long-running PHP workers may gradually consume more memory.
Laravel provides options for periodically stopping workers.
Memory limit
php artisan queue:work --memory=256
The worker exits when its memory usage exceeds the configured number of megabytes.
Supervisor then starts a fresh worker.
Maximum number of jobs
php artisan queue:work --max-jobs=1000
The worker exits after processing 1,000 jobs.
Maximum running time
php artisan queue:work --max-time=3600
The worker exits after one hour.
A production command may combine these options:
command=/usr/bin/php /var/www/example.com/artisan queue:work --sleep=3 --tries=3 --timeout=60 --memory=256 --max-jobs=1000 --max-time=3600
These limits do not stop the overall queue system because Supervisor starts a replacement worker.
Fixing Supervisor FATAL Status
You may see:
laravel-worker:laravel-worker_00 FATAL
This means the process failed repeatedly and Supervisor stopped trying to start it.
Check the Supervisor log:
sudo tail -f /var/log/supervisor/supervisord.log
Check the worker output:
tail -f /var/www/example.com/storage/logs/worker.log
Run the exact configured command manually:
sudo -u www-data /usr/bin/php /var/www/example.com/artisan queue:work --once
Common causes include:
-
Incorrect PHP path
-
Incorrect project path
-
Invalid Artisan command
-
Missing
.envfile access -
Permission problems
-
Missing PHP extension
-
Invalid Laravel configuration
-
Worker log path not writable
After fixing the issue:
sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl restart laravel-worker:*
Fixing Supervisor BACKOFF Status
You may see:
laravel-worker:laravel-worker_00 BACKOFF
BACKOFF usually means the process started but exited too quickly.
The worker did not remain alive long enough to be considered successfully running.
Common reasons include:
-
The command immediately crashes
-
The PHP binary does not exist
-
The project path is wrong
-
Laravel cannot boot
-
The queue configuration is invalid
-
The log directory is not writable
-
The command contains unsupported options
Run the command manually as the configured user:
sudo -u www-data /usr/bin/php /var/www/example.com/artisan queue:work
The manual output normally reveals the real error.
Fixing “No Such Process”
You may run:
sudo supervisorctl restart laravel-worker:*
and receive:
laravel-worker: ERROR (no such process)
First verify the program name:
sudo supervisorctl status
Then reload the configuration:
sudo supervisorctl reread
sudo supervisorctl update
Check that the configuration file has a .conf extension:
/etc/supervisor/conf.d/laravel-worker.conf
Also verify the program heading:
[program:laravel-worker]
Fixing Permission Denied Errors
Queue jobs often need to write files.
Common errors include:
Permission denied
or:
The stream or file could not be opened in append mode
Give the worker user appropriate access:
sudo chown -R www-data:www-data /var/www/example.com/storage
sudo chown -R www-data:www-data /var/www/example.com/bootstrap/cache
Apply directory permissions:
sudo chmod -R 775 /var/www/example.com/storage
sudo chmod -R 775 /var/www/example.com/bootstrap/cache
Do not automatically apply 777 permissions.
It grants unnecessary write access and can create security risks.
When your deployment user and web-server user both need access, use a shared group and appropriate group permissions instead.
Fixing Worker Log Permission Errors
Supervisor may fail because it cannot create the configured log file:
stdout_logfile=/var/www/example.com/storage/logs/worker.log
Create the file:
sudo touch /var/www/example.com/storage/logs/worker.log
Set ownership:
sudo chown www-data:www-data /var/www/example.com/storage/logs/worker.log
Then restart the worker:
sudo supervisorctl restart laravel-worker:*
Fixing Jobs That Stay Pending
When jobs remain in the queue, check the worker status:
sudo supervisorctl status
Check whether the worker listens to the correct queue:
--queue=emails
A worker listening only to emails will not process jobs on default.
Check the active connection:
php artisan config:show queue
Your web application and worker must use compatible configuration.
For example, a worker using Redis cannot process jobs that the application placed in a database queue.
Restart the worker after configuration changes:
php artisan queue:restart
Fixing Jobs That Run More Than Once
Duplicate execution can happen when the worker timeout and queue retry_after settings are incorrect.
For example:
Worker timeout: 120 seconds
retry_after: 90 seconds
After 90 seconds, the queue may make the job available to another worker even though the first worker is still processing it.
A safer example is:
Worker timeout: 60 seconds
retry_after: 90 seconds
The worker has time to terminate before the job becomes available again.
Also make important jobs idempotent.
A payment, inventory movement, webhook, or email job should check whether the action has already been completed before repeating it.
Supervisor Environment Differences
A command may work in your SSH shell but fail under Supervisor.
This can happen because Supervisor may have a different:
-
User
-
PATH
-
Home directory
-
Working directory
-
Environment variable set
-
PHP binary
Supervisor’s documentation specifically notes that commands can behave differently under Supervisor because it does not necessarily provide the same shell environment as an interactive terminal.
Use absolute paths:
command=/usr/bin/php /var/www/example.com/artisan queue:work
Set the directory:
directory=/var/www/example.com
When necessary, define environment values explicitly:
environment=APP_ENV="production",HOME="/var/www"
Avoid storing sensitive secrets directly in Supervisor configuration unless necessary. Keep application secrets in Laravel’s protected environment configuration.
Supervisor Versus Laravel Horizon
Supervisor and Laravel Horizon are not direct replacements for each other.
Supervisor
Supervisor is a general process manager.
It:
-
Starts processes
-
Restarts stopped processes
-
Runs workers after reboot
-
Works with database, Redis, and other queue connections
-
Does not provide a Laravel-specific monitoring dashboard
Laravel Horizon
Laravel Horizon is a queue management and monitoring system designed for Redis queues.
It provides information such as:
-
Queue throughput
-
Job runtime
-
Failed jobs
-
Worker processes
-
Queue balancing
-
Recent jobs
Horizon uses its own internal “supervisor” terminology for groups of worker processes, but the Horizon master process itself still needs to remain running in production. Laravel recommends managing that process using a system process monitor.
A production Horizon configuration under operating-system Supervisor may look like:
[program:laravel-horizon]
process_name=%(program_name)s
command=/usr/bin/php /var/www/example.com/artisan horizon
directory=/var/www/example.com
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
user=www-data
redirect_stderr=true
stdout_logfile=/var/www/example.com/storage/logs/horizon.log
stopwaitsecs=3600
During deployment, use:
php artisan horizon:terminate
The running Horizon process exits, and Supervisor starts it again.
Use normal queue:work with Supervisor when you need a simple queue setup.
Consider Horizon when:
-
You use Redis
-
You need queue metrics
-
You need automatic balancing
-
You operate several queues
-
You want a visual monitoring dashboard
Production-Ready Supervisor Configuration
Here is a complete configuration suitable as a starting point:
[program:laravel-worker]
process_name=%(program_name)s_%(process_num)02d
command=/usr/bin/php /var/www/example.com/artisan queue:work --queue=high,default,low --sleep=3 --tries=3 --timeout=60 --memory=256 --max-jobs=1000 --max-time=3600
directory=/var/www/example.com
autostart=true
autorestart=true
startsecs=3
startretries=3
stopasgroup=true
killasgroup=true
user=www-data
numprocs=2
redirect_stderr=true
stdout_logfile=/var/www/example.com/storage/logs/worker.log
stdout_logfile_maxbytes=20MB
stdout_logfile_backups=5
stopwaitsecs=3600
Adjust these values for your application:
PHP path
Project path
Linux user
Queue names
Number of processes
Timeout
Memory limit
Maximum runtime
Log path
Shutdown waiting time
Do not copy production values without considering the types of jobs your application processes.
Laravel Supervisor Deployment Checklist
Before considering the setup complete, verify the following.
Laravel configuration
-
QUEUE_CONNECTIONis not accidentally set tosync -
Database or Redis is accessible
-
Configuration cache contains the expected values
-
Failed-job storage is configured
-
Jobs can run manually
Supervisor configuration
-
The PHP path is correct
-
The Artisan path is absolute
-
The project directory is correct
-
The configured Linux user exists
-
The number of workers fits the server
-
The timeout is shorter than
retry_after -
The log file is writable
-
autostartandautorestartare enabled
Server operations
-
Supervisor starts after reboot
-
Worker status shows
RUNNING -
Workers restart after
queue:restart -
Deployment runs
queue:restart -
Logs are rotated
-
Failed jobs are reviewed
-
Important jobs prevent duplicate side effects
Frequently Asked Questions
Why does php artisan queue:work stop after closing SSH?
The process is attached to your terminal session. When that session closes, the process may be terminated.
Supervisor runs and monitors the worker independently from your SSH connection.
Does Supervisor process Laravel jobs itself?
No.
Supervisor only keeps the Laravel queue-worker command running.
Laravel still retrieves and processes the jobs.
Should I use queue:listen or queue:work?
For production, queue:work is generally preferred because it runs as a long-lived process and avoids rebooting the framework for every job.
Because it remains in memory, restart it after deployments.
How many Laravel queue workers should I run?
It depends on:
-
Server memory
-
Available CPU
-
Job duration
-
Database capacity
-
External service limits
-
Queue volume
Start with one or two workers, monitor resource usage and queue delays, and increase carefully.
Why does the worker use old Laravel code?
The queue worker loaded the application before the deployment.
Run:
php artisan queue:restart
The workers finish their current jobs and restart with the new code.
Will queue:restart delete queued jobs?
No.
It signals running workers to exit gracefully after completing their current jobs. Pending jobs remain in the queue.
Should Supervisor run as root?
Usually no.
Run the Laravel worker under a restricted deployment or web-server user that has only the permissions required by the application.
Can Supervisor manage scheduled Laravel tasks?
Supervisor is designed for long-running processes.
For the normal Laravel scheduler, configure cron to run:
php artisan schedule:run
every minute.
A long-running schedule:work process can also be managed, but cron remains the common production approach.
Final Thoughts
Running this command manually is not a complete production queue setup:
php artisan queue:work
A production application needs a reliable way to keep that command running.
Supervisor provides that reliability by:
-
Starting workers automatically
-
Restarting workers after they exit
-
Running multiple worker processes
-
Collecting process output
-
Restoring workers after server reboots
-
Giving administrators simple process-control commands
The most important setup steps are:
-
Confirm the Laravel queue works manually.
-
Install Supervisor.
-
Use absolute PHP and project paths.
-
Run the worker under the correct Linux user.
-
Configure sensible timeout, memory, and retry values.
-
Reload Supervisor after configuration changes.
-
Run
php artisan queue:restartduring deployment. -
Monitor failed jobs and worker logs.
Once Supervisor is configured correctly, Laravel queue processing becomes much more reliable and requires far less manual intervention.