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.
Pauses only the current worker:// Pause locally (only this worker)
await queue.pause(true);
// Resume locally (only this worker)
await queue.resume(true);
Local pause only affects this specific worker instance. Other workers continue processing. Useful for graceful shutdown of individual workers.
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
Wait for Completion
Immediate Pause
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');
Don’t wait for active jobs:// Pause immediately (active jobs continue in background)
await queue.pause(false, true);
console.log('Queue paused (active jobs still running)');
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
Use local pause for graceful shutdown
process.on('SIGTERM', async () => {
await queue.pause(true); // Stop this worker only
await queue.whenCurrentJobsFinished();
await queue.close();
});
Use global pause for maintenance
// Stop all workers for system maintenance
await queue.pause(false);
await performSystemMaintenance();
await queue.resume(false);
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.