Skip to main content
Pause and resume functionality allows you to control when a queue processes jobs. This is essential for maintenance, graceful shutdowns, or temporarily halting job processing without losing queued jobs.

Basic Usage

Pause and resume a queue:
const Queue = require('bull');
const queue = new Queue('tasks');

// Pause the queue
await queue.pause();
console.log('Queue is paused');

// Resume the queue
await queue.resume();
console.log('Queue is resumed');
Pausing a queue prevents it from processing new jobs, but jobs currently being processed will continue until completion.

Local vs Global Pausing

Bull supports two types of pausing:
Pauses all workers across all queue instances:
// Pause globally (affects all workers)
await queue.pause();
// or explicitly
await queue.pause(false);

// Resume globally
await queue.resume();
// or explicitly
await queue.resume(false);
Global pause affects all workers in all queue instances for a given queue name. Useful for system-wide maintenance.

Pause Method Signature

pause(isLocal?: boolean, doNotWaitActive?: boolean): Promise

Parameters

  • isLocal: If true, pauses only this worker. If false or omitted, pauses globally.
  • doNotWaitActive: If true, resolves immediately without waiting for active jobs. If false or omitted, waits for active jobs to finish.

Waiting for Active Jobs

Default behavior - waits for active jobs to finish:
// Pause and wait for active jobs to complete
await queue.pause();
console.log('All active jobs completed, queue paused');

Checking Pause State

Check if a queue is paused:
// Check global pause state
const isGloballyPaused = await queue.isPaused();
console.log('Globally paused:', isGloballyPaused);

// Check local pause state
const isLocallyPaused = await queue.isPaused(true);
console.log('Locally paused:', isLocallyPaused);
isPaused() without arguments checks global state. Pass true to check if this specific worker instance is paused.

Waiting for Current Jobs

Wait for all currently processing jobs to finish:
// Wait for active jobs without pausing
await queue.whenCurrentJobsFinished();
console.log('All current jobs are finished');
whenCurrentJobsFinished() is useful for graceful shutdowns or when you need to ensure all active work completes before proceeding.

Common Use Cases

Graceful Shutdown

const queue = new Queue('app-tasks');

process.on('SIGTERM', async () => {
  console.log('Shutting down gracefully...');
  
  // Pause queue locally (stop taking new jobs)
  await queue.pause(true);
  
  // Wait for active jobs to complete
  await queue.whenCurrentJobsFinished();
  
  // Close queue connections
  await queue.close();
  
  console.log('Shutdown complete');
  process.exit(0);
});

Maintenance Window

async function performMaintenance() {
  console.log('Starting maintenance...');
  
  // Pause globally
  await queue.pause();
  
  // Perform maintenance tasks
  await backupDatabase();
  await updateSchema();
  
  // Resume processing
  await queue.resume();
  
  console.log('Maintenance complete');
}

Temporary Worker Shutdown

const worker1 = new Queue('shared-queue');
const worker2 = new Queue('shared-queue');

worker1.process(processor);
worker2.process(processor);

// Pause only worker1 (worker2 continues)
await worker1.pause(true);

// Later, resume worker1
await worker1.resume(true);

Rate Limit Recovery

const apiQueue = new Queue('api-calls');

apiQueue.on('failed', async (job, err) => {
  if (err.message.includes('rate limit')) {
    console.log('Rate limit hit, pausing for 1 minute...');
    
    // Pause queue
    await apiQueue.pause();
    
    // Wait for rate limit to reset
    setTimeout(async () => {
      await apiQueue.resume();
      console.log('Queue resumed after rate limit');
    }, 60000);
  }
});

Events

Monitor pause and resume events:
queue.on('paused', () => {
  console.log('Queue has been paused');
});

queue.on('resumed', () => {
  console.log('Queue has been resumed');
});

// Pause the queue
await queue.pause();
// Logs: "Queue has been paused"

// Resume the queue
await queue.resume();
// Logs: "Queue has been resumed"

Example from README

From the Bull README:
queue.pause().then(function () {
  // queue is paused now
});

queue.resume().then(function () {
  // queue is resumed now
});
A queue can be paused and resumed globally (pass true to pause processing for just this worker).

Important Behaviors

Pausing Already Paused Queue

await queue.pause();
await queue.pause();  // Does nothing
Pausing a queue that is already paused does nothing.

Resuming Not Paused Queue

await queue.resume();  // Does nothing if not paused
Resuming a queue that is not paused does nothing.

Global Resume vs Local Pause

// Worker paused locally
await queue.pause(true);

// Global resume does NOT resume locally paused workers
await queue.resume(false);

const stillPaused = await queue.isPaused(true);
console.log(stillPaused);  // true

// Must resume locally
await queue.resume(true);
Resuming a queue globally will not resume workers that have been paused locally. For locally paused workers, you must call resume(true) directly on their instances.

Advanced Patterns

Circuit Breaker Pattern

class CircuitBreaker {
  constructor(queue, threshold = 5) {
    this.queue = queue;
    this.failures = 0;
    this.threshold = threshold;
    
    queue.on('failed', () => this.onFailed());
    queue.on('completed', () => this.onSuccess());
  }
  
  async onFailed() {
    this.failures++;
    
    if (this.failures >= this.threshold) {
      console.log('Circuit breaker opened');
      await this.queue.pause();
      
      // Try to resume after 30 seconds
      setTimeout(() => this.reset(), 30000);
    }
  }
  
  onSuccess() {
    this.failures = 0;
  }
  
  async reset() {
    console.log('Attempting to close circuit breaker');
    this.failures = 0;
    await this.queue.resume();
  }
}

const queue = new Queue('protected-api');
const breaker = new CircuitBreaker(queue);

Scheduled Pause/Resume

const schedule = require('node-schedule');

// Pause queue every night at midnight
schedule.scheduleJob('0 0 * * *', async () => {
  console.log('Nightly pause');
  await queue.pause();
});

// Resume queue every morning at 6 AM
schedule.scheduleJob('0 6 * * *', async () => {
  console.log('Morning resume');
  await queue.resume();
});

Conditional Processing

let processingEnabled = true;

async function toggleProcessing(enabled) {
  processingEnabled = enabled;
  
  if (enabled) {
    await queue.resume();
    console.log('Processing enabled');
  } else {
    await queue.pause();
    console.log('Processing disabled');
  }
}

// Control via API endpoint
app.post('/admin/queue/toggle', async (req, res) => {
  await toggleProcessing(req.body.enabled);
  res.json({ success: true });
});

Best Practices

1

Use local pause for graceful shutdown

process.on('SIGTERM', async () => {
  await queue.pause(true);  // Stop this worker only
  await queue.whenCurrentJobsFinished();
  await queue.close();
});
2

Use global pause for maintenance

// Stop all workers for system maintenance
await queue.pause(false);
await performSystemMaintenance();
await queue.resume(false);
3

Always wait for active jobs when pausing

// Good - ensures clean shutdown
await queue.pause(true, false);

// Risky - jobs may be interrupted
await queue.pause(true, true);
Combine pause() with whenCurrentJobsFinished() for the most reliable graceful shutdowns.
Jobs that are currently active when you pause will continue to completion. If you need to stop them immediately, you’ll need to implement cancellation logic within your job processors.

Build docs developers (and LLMs) love