# How to Run Laravel Queue Workers in Production: Best Practices for High Availability

> Learn best practices for running Laravel queue workers in production. Ensure high availability by supervising daemons, tuning options, and using zero-downtime restarts.

- Repository: [Laravel/framework](https://github.com/laravel/framework)
- Tags: best-practices
- Published: 2026-02-16

---

**Run Laravel queue workers as supervised daemons using `php artisan queue:work`, tune `WorkerOptions` for memory limits and job timeouts, and leverage the built-in `queue:restart` signal to enable zero-downtime deployments.**

The `laravel/framework` repository provides a robust queue system centered around the `Illuminate\Queue\Worker` class. In production environments, maintaining persistent, efficient queue processing requires understanding the daemon architecture, configuring process supervision, and implementing proper restart strategies to prevent job loss during deployments.

## Understanding the Laravel Queue Worker Architecture

The queue worker operates as a long-running daemon through the `Worker::daemon()` method in [`src/Illuminate/Queue/Worker.php`](https://github.com/laravel/framework/blob/main/src/Illuminate/Queue/Worker.php). This method executes a continuous loop of *getNextJob → runJob → sleep* until a stop condition triggers.

The worker remains responsive to external commands through stateless cache checks. On every iteration, `Worker::stopIfNecessary()` evaluates multiple shutdown conditions:

- **Memory limits** via `Worker::memoryExceeded()`
- **Restart signals** via `Worker::queueShouldRestart()` checking the `illuminate:queue:restart` cache key
- **Max job/time constraints** via `WorkerOptions` configurations
- **POSIX signals** including `SIGTERM`, `SIGINT`, `SIGQUIT` for shutdown and `SIGUSR2`, `SIGCONT` for pause/resume functionality

The `Worker::listenForSignals()` method registers async signal handlers, allowing the process to handle timeouts through `Worker::registerTimeoutHandler()` (which sends `SIGALRM` for job killing) and graceful shutdowns without losing active jobs.

## Process Management Strategies for Production

Running workers directly from the command line risks process death during server restarts or application crashes. Production environments require process managers to ensure workers remain alive.

### Supervisor Configuration (Recommended)

Supervisor remains the industry standard for managing Laravel queue workers. It automatically restarts failed processes and manages worker pools.

Create a configuration file at [`/etc/supervisor/conf.d/laravel-queue-worker.conf`](https://github.com/laravel/framework/blob/main//etc/supervisor/conf.d/laravel-queue-worker.conf):

```ini
[program:laravel-queue-worker]
process_name=%(program_name)s_%(process_num)02d
command=php /var/www/myapp/artisan queue:work redis \
    --daemon \
    --queue=high,default \
    --sleep=3 \
    --tries=3 \
    --timeout=60 \
    --memory=256
autostart=true
autorestart=true
stopwaitsecs=3600
user=www-data
numprocs=4
redirect_stderr=true
stdout_logfile=/var/log/laravel/queue-worker.log

```

The `stopwaitsecs=3600` directive ensures Supervisor waits up to one hour for the current job to complete before force-killing the process, respecting the graceful shutdown logic in `Worker::stopIfNecessary()`.

Apply the configuration:

```bash
sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl start laravel-queue-worker:*

```

### Systemd Unit Alternative

For systems preferring systemd over Supervisor, create `/etc/systemd/system/laravel-queue-worker.service`:

```ini
[Unit]
Description=Laravel Queue Worker
After=network.target

[Service]
User=www-data
Group=www-data
Restart=always
ExecStart=/usr/bin/php /var/www/myapp/artisan queue:work redis \
    --queue=high,default \
    --sleep=3 \
    --tries=3 \
    --timeout=60 \
    --memory=256

[Install]
WantedBy=multi-user.target

```

Enable and start the service:

```bash
sudo systemctl enable laravel-queue-worker
sudo systemctl start laravel-queue-worker

```

## Tuning WorkerOptions for Performance

The `WorkerOptions` class ([`src/Illuminate/Queue/WorkerOptions.php`](https://github.com/laravel/framework/blob/main/src/Illuminate/Queue/WorkerOptions.php)) defines behavioral parameters passed via command-line arguments to `queue:work`. Optimize these values based on your workload characteristics:

- **`--memory=256`**: Sets the megabyte threshold for `Worker::memoryExceeded()`. When exceeded, the worker exits cleanly after the current job, allowing the process manager to respawn a fresh instance.
- **`--timeout=60`**: Configures the maximum seconds a job may run before `Worker::registerTimeoutHandler()` sends `SIGALRM` to kill the process.
- **`--sleep=3`**: Controls the seconds to wait between polling the queue when empty. Higher values reduce CPU usage but increase latency.
- **`--tries=3`**: Specifies retry attempts before marking a job as failed.
- **`--max-jobs=1000`** and **`--max-time=3600`**: Enable controlled worker recycling, preventing memory leaks in long-running processes by exiting after processing 1,000 jobs or running for one hour.

Example optimized command:

```bash
php artisan queue:work redis \
    --name=high-priority \
    --queue=high,default \
    --sleep=3 \
    --timeout=60 \
    --tries=3 \
    --memory=256 \
    --max-jobs=1000 \
    --max-time=3600

```

## Implementing Graceful Restarts and Zero-Downtime Deployments

The Laravel queue worker supports zero-downtime deployments through a cache-based coordination mechanism. When running `php artisan queue:restart`, the `RestartCommand` class ([`src/Illuminate/Queue/Console/RestartCommand.php`](https://github.com/laravel/framework/blob/main/src/Illuminate/Queue/Console/RestartCommand.php)) writes a fresh timestamp to the `illuminate:queue:restart` cache key.

Each worker checks `Worker::getTimestampOfLastQueueRestart()` on every daemon loop iteration via `Worker::queueShouldRestart()`. Detecting a change triggers a graceful exit after the current job completes, allowing your deployment script to start new workers with updated code.

Deployment script example:

```bash

# Place application in maintenance mode (optional)

php /var/www/myapp/artisan down

# Signal all workers to exit after current job

php /var/www/myapp/artisan queue:restart

# Deploy new code

git pull origin main
composer install --no-dev --optimize-autoloader

# Bring application back online

php /var/www/myapp/artisan up

# Supervisor/systemd automatically starts new workers

```

This pattern prevents "half-finished" jobs and ensures no worker processes linger with outdated code.

## Monitoring Queue Events and Health

The worker fires events through methods like `raiseJobProcessing()`, `raiseJobProcessed()`, and `raiseJobFailed()` in [`Worker.php`](https://github.com/laravel/framework/blob/main/Worker.php). Listen to these events in your application service provider for observability:

```php
use Illuminate\Support\Facades\Event;
use Illuminate\Queue\Events\JobProcessed;
use Illuminate\Queue\Events\JobFailed;

Event::listen(JobProcessed::class, function (JobProcessed $event) {
    logger()->info('Job processed', [
        'id' => $event->job->getJobId(),
        'name' => $event->job->resolveName(),
        'connection' => $event->connectionName,
    ]);
});

Event::listen(JobFailed::class, function (JobFailed $event) {
    logger()->error('Job failed', [
        'id' => $event->job->getJobId(),
        'exception' => $event->exception->getMessage(),
    ]);
    
    // Send metrics to monitoring service (Honeycomb, Datadog, etc.)
});

```

For temporary maintenance without stopping processes, set `Cache::put('illuminate:queue:pause', true)` to pause processing. The `Worker::daemonShouldRun()` method checks this cache key, causing workers to sleep until the key clears.

## Summary

- **Run workers as supervised processes** using Supervisor or systemd to ensure automatic restarts after crashes or memory limit hits.
- **Tune `WorkerOptions`** with appropriate `--memory`, `--timeout`, and `--max-jobs` values to prevent resource exhaustion and memory leaks.
- **Use `queue:restart`** during deployments to trigger graceful shutdowns via the cache-based signal mechanism in `Worker::queueShouldRestart()`.
- **Implement event listeners** for `JobProcessed` and `JobFailed` to capture metrics and maintain visibility into queue health.
- **Leverage cache-based pausing** via `illuminate:queue:pause` for maintenance windows without killing worker processes.

## Frequently Asked Questions

### How do I prevent memory leaks in long-running Laravel queue workers?

Memory leaks occur when PHP objects accumulate across thousands of job executions. Configure the `--memory` option (e.g., `--memory=256`) to set a megabyte limit, and use `--max-jobs` (e.g., `--max-jobs=1000`) to force worker recycling. The `Worker::memoryExceeded()` method checks consumption on each loop, exiting cleanly when limits breach to allow your process manager to spawn fresh instances.

### What happens to active jobs when I run `queue:restart`?

Active jobs complete normally. The `RestartCommand` writes a timestamp to the `illuminate:queue:restart` cache key, which `Worker::queueShouldRestart()` detects on the next iteration. The worker sets `shouldQuit` to true and exits after `stopIfNecessary()` confirms the current job finished, ensuring no jobs terminate mid-execution.

### Should I use `queue:work` or `queue:listen` in production?

Always use `queue:work` with the `--daemon` flag for production. The `WorkCommand` class ([`src/Illuminate/Queue/Console/WorkCommand.php`](https://github.com/laravel/framework/blob/main/src/Illuminate/Queue/Console/WorkCommand.php)) initiates a daemon process that sleeps between jobs, making it significantly more CPU-efficient than `queue:listen`, which boots the framework anew for every job. Daemon mode requires proper signal handling and process supervision but provides superior performance for high-throughput queues.

### How do I handle queue workers during database maintenance windows?

Set `Cache::put('illuminate:queue:pause', true, $seconds)` before maintenance. The `Worker::daemonShouldRun()` method checks this cache key each loop iteration, causing workers to sleep rather than poll for jobs. Remove the key or let it expire to resume processing. This approach keeps worker processes alive and responsive to restart signals while preventing job processing during sensitive operations.