Milind Daraniya

Laravel Queue Workers: What Happens When Your Background Jobs Stop Working

Published September 16th, 2026 11 min read

In many Laravel applications, not everything should happen during the user's request.

Sending emails, processing files, generating reports, importing large CSV files, calling external APIs and many other tasks can take time.

This is where Laravel queues are useful.

Instead of making the user wait for the complete operation, we can create a job and send it to a queue.

For example:

SendWelcomeEmail::dispatch($user);

The request can finish quickly, while the queue worker processes the job in the background.

This works very well.

But there is one important part that developers sometimes forget.

The queue worker must keep running.

If the worker stops, jobs can continue entering the queue, but nobody will process them.

This can create a situation where the application looks completely normal from the frontend, but emails, reports or other background operations are stuck.

How Laravel queues work

A simple flow looks like this:

User Request
     ↓
Laravel Application
     ↓
Dispatch Job
     ↓
Queue
     ↓
Queue Worker
     ↓
Job Processing
     ↓
Completed

For example, suppose a user uploads a CSV file containing 50,000 records.

Instead of processing all 50,000 records inside the HTTP request, I would normally create a background job.

ImportCustomers::dispatch($file);

The worker picks up the job and starts processing it.

This keeps the web request faster and makes large operations easier to manage.

Running a worker manually

During development, we can run:

php artisan queue:work

Laravel will start listening for new jobs.

This is useful while developing and debugging.

But I would not depend on a terminal session for production.

If the terminal closes, the worker stops.

If the server restarts, the worker does not automatically come back.

This is where a process manager becomes important.

Using Supervisor

On a Linux production server, Supervisor is commonly used to keep Laravel queue workers running.

The basic idea is:

Supervisor
   ↓
Laravel Queue Worker
   ↓
Job

If the worker crashes, Supervisor can start it again.

If the server restarts, Supervisor can start the worker automatically.

This is much better than manually running:

php artisan queue:work

and hoping that the terminal remains open.

One worker is not always enough

Suppose an application receives many jobs.

For example:

Email jobs
Import jobs
Report jobs
Notification jobs
Webhook jobs
Image processing jobs

Putting everything into one queue can become a problem.

A large import job could take several minutes.

During that time, smaller email or notification jobs may have to wait.

For larger applications, separate queues can make more sense.

For example:

default
emails
imports
reports
notifications

Then workers can be assigned according to the workload.

Queue priority

Laravel also allows workers to listen to multiple queues.

For example:

php artisan queue:work --queue=high,default

This means the worker checks the high-priority queue before the default queue.

This can be useful when some jobs should be processed before less important background work.

But queue priority should be designed according to the actual application requirements.

Don't create many queues just because they are available.

What happens when a job fails?

Jobs can fail.

For example:

  • External API is unavailable
  • Database connection fails
  • File is missing
  • Third-party service returns an error
  • Invalid data is received
  • Code throws an exception

Laravel provides failed-job handling so failed jobs can be stored and reviewed.

You can check failed jobs with:

php artisan queue:failed

Depending on the application, failed jobs can also be retried.

php artisan queue:retry all

But blindly retrying every failed job is not always a good idea.

If the job has invalid data, retrying it repeatedly will not solve the actual problem.

Retry configuration matters

A job may need to be retried when a temporary service is unavailable.

For example:

API temporarily unavailable
        ↓
Wait
        ↓
Retry
        ↓
Success

But if the input itself is invalid:

Invalid customer data
        ↓
Retry
        ↓
Invalid customer data
        ↓
Retry again

This does not solve anything.

So I normally think about retries based on the type of failure.

Don't put everything inside one huge job

Another issue I have seen in real applications is creating one very large job.

For example:

Process 1 million records
     ↓
One Laravel job

If that job fails after processing 900,000 records, handling the failure becomes difficult.

For large datasets, smaller jobs are usually easier to manage.

For example:

1,000,000 records

↓
10,000 records/job

↓
100 smaller jobs

Now each job can be monitored separately.

This also makes retrying failed work easier.

Database transactions and queues

Another important point is database consistency.

Suppose we create an order and immediately dispatch a job:

$order = Order::create($data);

SendOrderEmail::dispatch($order);

If this happens inside a database transaction, we need to make sure the job does not start before the transaction has actually committed.

Otherwise, the worker may try to read data that is not yet committed.

This is one reason queue jobs and database transactions need to be designed together.

Long-running workers

A queue worker is a long-running PHP process.

It does not start PHP, process one request and exit like a normal web request.

It stays alive and processes multiple jobs.

Because of this, I prefer restarting workers after deployments.

For example:

php artisan queue:restart

This tells Laravel workers to gracefully restart after completing their current job.

Then Supervisor can start the workers again.

This is useful after deploying new application code.

Monitor your queue

Don't wait for users to tell you that emails are not being sent.

A production application should have some level of monitoring.

Depending on the project, you can monitor:

  • Queue size
  • Failed jobs
  • Worker status
  • Job execution time
  • Memory usage
  • Database performance
  • External API failures

Laravel Horizon can also be useful for applications using Redis queues because it provides visibility into queue processing.

But I don't consider Horizon mandatory for every Laravel application.

The monitoring solution should match the application's size and requirements.

Common production mistake

One common mistake is:

Application deployed
        ↓
Queue worker forgotten
        ↓
Jobs keep increasing
        ↓
Users report missing emails

The application itself can still appear completely normal.

The login works.

The dashboard works.

The API works.

The database works.

Only background processing is broken.

This is why queue workers should be considered part of the application infrastructure, not an optional extra.

My basic production checklist

Before putting a Laravel queue-based application into production, I normally check:

✓ Queue connection configured
✓ Queue worker configured
✓ Supervisor/systemd configured
✓ Worker starts after reboot
✓ Failed jobs configured
✓ Retry strategy defined
✓ Long-running jobs reviewed
✓ Large jobs divided where required
✓ Deployment restart strategy configured
✓ Queue monitoring available
✓ Logs checked

These checks can prevent many production problems.

Final thoughts

Laravel queues are simple to start with.

You can write:

SomeJob::dispatch();

and immediately get the benefit of background processing.

But production queue systems need more than just dispatching jobs.

You need workers, process management, retries, failure handling, monitoring and a proper deployment strategy.

In small applications, this may be very simple.

In a large SaaS or ERP application, queues can become an important part of the entire backend architecture.

The more background processing an application has, the more important it becomes to treat queue workers as production infrastructure rather than just another Laravel command.