Pause All Laravel Queues During a Deploy with queue:pause --all
Laravel Tips & Tricks #Laravel #Queues #Deployment #Laravel 13 #PHP

Pause All Laravel Queues During a Deploy with queue:pause --all

3 min read Mohamed Said Mohamed Said

The Deploy Race Condition Every Laravel Developer Knows

There is a brief window during most deploys where new code is on disk but queue workers have not restarted yet. A worker that picks up a job in that window deserializes a payload written by the old code and runs it through the new code. Usually nothing breaks. Occasionally a renamed job class or a changed constructor signature produces a failed job you have to replay by hand.

The traditional fixes each have drawbacks:

  • Maintenance mode stops HTTP traffic too, which is often more than you want.
  • queue:restart is a polite request, not a guarantee — a worker only sees it between jobs.

Laravel 13.25 adds a third option: a global pause switch that stops every worker on every connection from reserving new work, while leaving HTTP traffic completely unaffected.

The Commands

# Pause all queues before deploying
php artisan queue:pause --all

# Resume after the deploy is complete
php artisan queue:resume --all

The queue argument is now optional on both commands. Without --all they behave as before and accept a connection:queue pair.

The same functionality is available on the Queue facade for deploy scripts written in PHP or for an admin panel controller:

use Illuminate\Support\Facades\Queue;

Queue::pauseAll();

// run your deploy steps here

Queue::resumeAll();

What Pausing Actually Does

Pausing stops workers from reserving new jobs. The worker process stays alive and keeps looping — it just sleeps instead of popping from the queue. A job already being processed when you pause runs to completion, so queue:pause --all will not interrupt anything mid-flight.

Producers are unaffected: SomeJob::dispatch() continues writing to Redis or the database, and those jobs wait there until you resume.

Under the hood, the feature writes a single cache key — illuminate:queues:paused — using forever(). Workers already read the cache once per loop to check for restart and per-queue pause signals, and the global key is fetched in the same many() call, so there are no extra round trips.

Individual and Global Pauses Are Independent

pause() and pauseAll() write different cache keys and are deliberately unaware of each other. If a queue was paused individually before the deploy, resumeAll() leaves it paused:

Queue::pause('redis', 'imports');    // parked earlier to investigate a bad job

Queue::pauseAll();                   // deploy starts
Queue::resumeAll();                  // deploy finishes

Queue::isPaused('redis', 'imports'); // still true

This is exactly the behavior you want. Someone parked the imports queue on purpose an hour ago, and a deploy running resumeAll() should not silently undo that. Clearing an individual pause still requires queue:resume redis:imports.

Events

Two new events fire alongside the existing per-queue QueuePaused and QueueResumed:

use Illuminate\Queue\Events\QueuesPaused;
use Illuminate\Queue\Events\QueuesResumed;

Event::listen(function (QueuesPaused $event) {
    Log::warning('All queues paused — deploy in progress');
});

These are useful for alerting, audit logs, or triggering external monitoring integrations.

Key Takeaways

  • php artisan queue:pause --all and Queue::pauseAll() are new in Laravel 13.25.
  • Workers stay alive but stop reserving jobs; in-flight jobs finish normally.
  • Producers keep dispatching; jobs accumulate and are processed after resumeAll().
  • Global and per-queue pauses are independent — resumeAll() does not clear individual pauses.
  • The feature is implemented as a single cache key with no extra round trips per worker loop.
  • Contributed by Jack Bayliss in PR #61126.

Source: Pause All Laravel Queues During a Deploy — Laravel News

Found this useful?

Frequently Asked Questions

3 questions
Q01 Does queue:pause --all interrupt jobs that are already running?
No. Pausing stops workers from reserving new jobs, but any job already being processed when the pause is issued runs to completion. Only new reservations are blocked.
Q02 Will queue:resume --all unpause a queue that was individually paused before the deploy?
No. Global and per-queue pauses are independent. If a queue was paused individually with Queue::pause() or queue:pause, resumeAll() leaves it paused. You must run queue:resume connection:queue explicitly to clear an individual pause.
Q03 Do dispatched jobs get lost while all queues are paused?
No. Producers are unaffected by the pause. Jobs dispatched with SomeJob::dispatch() are written to Redis or the database as normal and simply wait there until you call queue:resume --all.

Continue reading

More Articles

View all