Skip to main content
Delayed jobs allow you to schedule jobs to be processed at a specific time in the future. This is useful for scenarios like sending reminder emails, scheduling reports, or triggering time-sensitive workflows.

Basic Usage

Add a job with the delay option to schedule it for future processing:
const Queue = require('bull');
const myQueue = new Queue('delayed-tasks');

// Process jobs in 5 seconds
await myQueue.add({ task: 'send-email' }, { delay: 5000 });

// Process jobs in 1 hour (3600000 milliseconds)
await myQueue.add({ task: 'generate-report' }, { delay: 3600000 });

How Delays Work

The delay option accepts a number representing milliseconds to wait before the job can be processed.
interface JobOpts {
  delay: number; // An amount of milliseconds to wait until this job can be processed.
}
For accurate delays, both server and clients should have their clocks synchronized. Time drift between systems can affect when delayed jobs are processed.

Common Use Cases

const emailQueue = new Queue('email-reminders');

// Send password reset email after 5 minutes if not completed
await emailQueue.add(
  { 
    type: 'password-reset-reminder',
    userId: user.id,
    email: user.email 
  },
  { delay: 300000 } // 5 minutes
);

Working with Delayed Jobs

Retrieving Delayed Jobs

You can get all delayed jobs from the queue:
const delayedJobs = await myQueue.getDelayed();
console.log(`Found ${delayedJobs.length} delayed jobs`);

Checking Delay Count

const count = await myQueue.getDelayedCount();
console.log(`${count} jobs are currently delayed`);

Promoting Delayed Jobs

You can promote a delayed job to execute immediately:
const job = await myQueue.getJob(jobId);
if (job) {
  await job.promote();
  console.log('Job promoted to waiting state');
}

Combining with Other Options

Delayed jobs can be combined with other job options:
await myQueue.add(
  { task: 'important-delayed-task' },
  {
    delay: 10000,           // Wait 10 seconds
    attempts: 3,            // Retry up to 3 times if it fails
    priority: 1,            // High priority
    removeOnComplete: true  // Clean up after completion
  }
);
Delayed jobs use Redis sorted sets internally. Very large numbers of delayed jobs (millions) may impact Redis memory usage.

Delay vs Repeatable Jobs

  • Delayed Jobs: Execute once at a specific future time
  • Repeatable Jobs: Execute repeatedly on a schedule (see Repeatable Jobs)
// Delayed job - runs once
await queue.add({ task: 'one-time' }, { delay: 5000 });

// Repeatable job - runs on schedule
await queue.add({ task: 'recurring' }, { repeat: { cron: '0 9 * * *' } });

Best Practices

1

Use absolute timestamps for precision

Calculate the exact delay based on timestamps rather than estimating:
const targetTime = new Date('2026-12-31T23:59:59Z');
const delay = targetTime.getTime() - Date.now();
await queue.add(data, { delay });
2

Handle clock skew

Be aware that delays depend on system time. Use NTP synchronization in production environments.
3

Set appropriate cleanup policies

Use removeOnComplete to prevent delayed job metadata from accumulating:
await queue.add(data, { 
  delay: 60000,
  removeOnComplete: true 
});
For delays longer than a few days, consider using repeatable jobs with a cron expression instead, as they’re more reliable for long-term scheduling.

Build docs developers (and LLMs) love