Skip to main content
BullMQ comes with two predefined strategies for creating repeatable jobs:
  • Every strategy: Schedule jobs at fixed intervals measured in milliseconds
  • Cron strategy: Use cron expressions for intricate scheduling patterns
Additionally, BullMQ lets you create custom strategies, giving you the flexibility to define your own logic for setting job intervals.

Every Strategy

The every strategy is used when you want to produce repeatable jobs at specific intervals:
import { Queue, Worker } from 'bullmq';

const connection = {
  host: 'localhost',
  port: 6379,
};

const myQueue = new Queue('my-repeatable-jobs', { connection });

// Upserting a repeatable job in the queue
await myQueue.upsertJobScheduler(
  'repeat-every-10s',
  {
    every: 10000, // Job will repeat every 10000 milliseconds (10 seconds)
  },
  {
    name: 'every-job',
    data: { jobData: 'data' },
    opts: {}, // Optional additional job options
  },
);

// Worker to process the jobs
const worker = new Worker(
  'my-repeatable-jobs',
  async (job) => {
    console.log(`Processing job ${job.id} with data: ${job.data.jobData}`);
  },
  { connection },
);

Common Intervals

// Every second
{ every: 1000 }

// Every 30 seconds
{ every: 30000 }

// Every minute
{ every: 60000 }

// Every 5 minutes
{ every: 5 * 60 * 1000 }

// Every hour
{ every: 60 * 60 * 1000 }

// Every day
{ every: 24 * 60 * 60 * 1000 }
every
number
required
Interval in milliseconds between job executions
You cannot use both pattern and every options together. Choose one strategy per scheduler.

Cron Strategy

The cron strategy leverages the cron-parser library to use cron expressions for scheduling jobs with greater specificity. This approach is ideal for jobs requiring execution at precise times or intervals, such as automated reports or maintenance tasks.

Cron Expression Format

BullMQ supports a 6-field cron format with an optional seconds field:
*    *    *    *    *    *
┬    ┬    ┬    ┬    ┬    ┬
│    │    │    │    │    │
│    │    │    │    │    └ day of week (0 - 7, 1L - 7L, where 0 or 7 is Sunday)
│    │    │    │    └───── month (1 - 12)
│    │    │    └────────── day of month (1 - 31, L for the last day of the month)
│    │    └─────────────── hour (0 - 23)
│    └──────────────────── minute (0 - 59)
└───────────────────────── second (0 - 59, optional)
This format includes the optional second field, which is not typically available in standard cron schedules, allowing for even more precise scheduling.

Basic Example

import { Queue, Worker } from 'bullmq';

const connection = {
  host: 'localhost',
  port: 6379,
};

const myQueue = new Queue('my-cron-jobs', { connection });

// Upserting a job with a cron expression
await myQueue.upsertJobScheduler(
  'weekday-morning-job',
  {
    pattern: '0 0 9 * * 1-5', // Runs at 9:00 AM every Monday to Friday
  },
  {
    name: 'cron-job',
    data: { jobData: 'morning data' },
    opts: {}, // Optional additional job options
  },
);

// Worker to process the jobs
const worker = new Worker(
  'my-cron-jobs',
  async (job) => {
    console.log(
      `Processing job ${job.id} at ${new Date()} with data: ${
        job.data.jobData
      }`,
    );
  },
  { connection },
);

Common Cron Patterns

// Every minute
'* * * * *'

// Every 5 minutes
'*/5 * * * *'

// Every hour at minute 30
'30 * * * *'

// Every day at midnight
'0 0 * * *'

// Every day at 2:30 PM
'30 14 * * *'

// Every Monday at 9 AM
'0 9 * * 1'

// Every 1st of the month at midnight
'0 0 1 * *'

// Every weekday at 6 PM
'0 18 * * 1-5'

// Every 15 minutes during business hours (9 AM - 5 PM, Mon-Fri)
'*/15 9-17 * * 1-5'

// Every 3 hours
'0 */3 * * *'

// Last day of the month at midnight
'0 0 L * *'
pattern
string
required
Cron expression for scheduling (e.g., ‘0 * * * *’ for every hour)

Timezone Support

Cron expressions support timezone differences and daylight saving time transitions, crucial for tasks that depend on local times:
await myQueue.upsertJobScheduler(
  'tokyo-morning-job',
  {
    pattern: '0 0 9 * * *', // 9 AM
    tz: 'Asia/Tokyo',
  },
  {
    name: 'morning-task',
    data: {},
  },
);

Special Characters

Cron patterns support special characters for flexible scheduling:
  • * - Any value
  • , - Value list separator (e.g., 1,3,5)
  • - - Range of values (e.g., 1-5)
  • / - Step values (e.g., */5 for every 5 units)
  • L - Last day of the month (day field only)
If you’re new to cron expressions, Wikipedia is an excellent starting point to learn how to use them.

Custom Strategy

It’s possible to define a different strategy to schedule repeatable jobs. The idea is that the repeat strategy, based on a pattern and the latest job’s milliseconds, returns the next desired timestamp.
Only one repeatStrategy can be defined for a given queue. The strategy applies to all job schedulers in that queue.

Implementation Requirements

A custom repeat strategy is a function with this signature:
type RepeatStrategy = (
  millis: number,
  opts: RepeatOptions,
  jobName: string,
) => number | undefined;
  • millis: Current timestamp in milliseconds
  • opts: Repeat options containing pattern and other settings
  • jobName: Name of the job (for conditional logic)
  • Returns: Next execution timestamp in milliseconds, or undefined to stop

RRULE Example

Here’s an example using RRULE for complex recurrence patterns:
import { Queue, Worker } from 'bullmq';
import { rrulestr } from 'rrule';
import type { RepeatOptions } from 'bullmq';

const settings = {
  repeatStrategy: (millis: number, opts: RepeatOptions, _jobName: string) => {
    const currentDate =
      opts.startDate && new Date(opts.startDate) > new Date(millis)
        ? new Date(opts.startDate)
        : new Date(millis);

    const rrule = rrulestr(opts.pattern);

    if (rrule.origOptions.count && !rrule.origOptions.dtstart) {
      throw new Error('DTSTART must be defined to use COUNT with rrule');
    }

    const next_occurrence = rrule.after(currentDate, false);
    return next_occurrence?.getTime();
  },
};

const myQueue = new Queue('Paint', { settings });

// Repeat job every 10 seconds
await myQueue.upsertJobScheduler(
  'colibris',
  {
    pattern: 'RRULE:FREQ=SECONDLY;INTERVAL=10;WKST=MO',
  },
  {
    data: { color: 'green' },
  },
);

// Repeat job every 20 seconds
await myQueue.upsertJobScheduler(
  'pigeons',
  {
    pattern: 'RRULE:FREQ=SECONDLY;INTERVAL=20;WKST=MO',
  },
  {
    data: { color: 'gray' },
  },
);

const worker = new Worker(
  'Paint',
  async (job) => {
    console.log('Processing', job.data.color);
  },
  { settings },
);
The repeat strategy setting must be provided in both the Queue and Worker classes. The Queue uses it when first adding the job to calculate the next iteration. After that, the Worker uses its configured settings.

Custom Strategy Best Practices

1

Handle edge cases

Return undefined when no more jobs should be scheduled:
if (nextTime > endDate) return undefined;
2

Respect startDate

Always check if startDate is in the future:
const currentDate = opts.startDate && new Date(opts.startDate) > new Date(millis)
  ? new Date(opts.startDate)
  : new Date(millis);
3

Use job name for conditional logic

Different jobs can have different scheduling behavior:
if (jobName === 'priority-task') {
  // Special scheduling logic
}
4

Configure in both Queue and Worker

Ensure consistent behavior across your application:
const settings = { repeatStrategy: myStrategy };
const queue = new Queue('name', { settings });
const worker = new Worker('name', processor, { settings });

Default Repeat Strategy

BullMQ’s default repeat strategy for cron patterns:
const defaultRepeatStrategy = (
  millis: number,
  opts: RepeatOptions,
): number | undefined => {
  const { pattern } = opts;

  const dateFromMillis = new Date(millis);
  const startDate = opts.startDate && new Date(opts.startDate);
  const currentDate = startDate > dateFromMillis ? startDate : dateFromMillis;
  
  const interval = parseExpression(pattern, {
    ...opts,
    currentDate,
  });

  try {
    if (opts.immediately) {
      return new Date().getTime();
    } else {
      return interval.next().getTime();
    }
  } catch (e) {
    // Ignore error
  }
};

Comparison

FeatureEvery StrategyCron StrategyCustom Strategy
PrecisionMillisecondsSecondsCustom
ComplexitySimpleModerateFlexible
Use CaseFixed intervalsTime-based schedulesComplex patterns
Timezone SupportNoYesCustom
DST HandlingNoYesCustom
Example{ every: 60000 }{ pattern: '0 * * * *' }{ pattern: 'RRULE:...' }

Repeat Options

Configure start dates, end dates, limits, and more

Overview

Learn the basics of Job Schedulers

Management

Manage and monitor job schedulers

API Reference

Build docs developers (and LLMs) love