From BullMQ version 5.16.0 onwards, repeatable job APIs are deprecated in favor of Job Schedulers , which provide a more cohesive and robust API for handling repeatable jobs.
Overview
Repeatable jobs are a special type of meta job that keep repeating according to a predefined schedule. Adding a job with the repeat option:
Creates a Repeatable Job configuration
Schedules a regular delayed job for the job’s first run
The first run is scheduled “on the hour” - for example, a job repeating every 15 minutes created at 4:07 will first run at 4:15, then 4:30, and so on.
Repeatable Job configurations are not actual jobs, so they won’t show up in methods like getJobs(). Use getRepeatableJobs() to manage them instead.
Scheduling Methods
There are two ways to specify a repeatable job’s pattern:
Cron Pattern
Uses cron-parser ’s “unix cron w/ optional seconds” format:
import { Queue } from 'bullmq' ;
const myQueue = new Queue ( 'Paint' );
// Repeat job once every day at 3:15 (am)
await myQueue . add (
'submarine' ,
{ color: 'yellow' },
{
repeat: {
pattern: '0 15 3 * * *' ,
},
},
);
Fixed Interval
Specify milliseconds between repetitions:
// Repeat job every 10 seconds but no more than 100 times
await myQueue . add (
'bird' ,
{ color: 'bird' },
{
repeat: {
every: 10000 ,
limit: 100 ,
},
},
);
Important Considerations
No Duplicates BullMQ won’t add the same repeatable job if the repeat options are identical.
No Accumulation If no workers are running, repeatable jobs won’t accumulate. They’ll resume when workers come back online.
Manual Removal Use removeRepeatable() or removeRepeatableByKey() to remove repeatable job configurations.
Unique IDs Repeatable jobs require unique IDs to avoid being considered duplicates.
Removing Repeatable Jobs
import { Queue } from 'bullmq' ;
const repeat = { pattern: '*/1 * * * * *' };
const myQueue = new Queue ( 'Paint' );
const job1 = await myQueue . add ( 'red' , { foo: 'bar' }, { repeat });
const job2 = await myQueue . add ( 'blue' , { foo: 'baz' }, { repeat });
// Remove by key
const isRemoved1 = await myQueue . removeRepeatableByKey ( job1 . repeatJobKey );
// Remove by name and options
const isRemoved2 = await queue . removeRepeatable ( 'blue' , repeat );
Get All Repeatable Jobs
import { Queue } from 'bullmq' ;
const myQueue = new Queue ( 'Paint' );
const repeatableJobs = await myQueue . getRepeatableJobs ();
Job IDs with Repeatable Jobs
The jobId option works differently for repeatable jobs. It’s used to generate unique IDs rather than being the unique ID itself:
import { Queue } from 'bullmq' ;
const myQueue = new Queue ( 'Paint' );
// Two repeatable jobs with same name and options but different jobIds
await myQueue . add (
'bird' ,
{ color: 'bird' },
{
repeat: {
every: 10000 ,
limit: 100 ,
},
jobId: 'colibri' ,
},
);
await myQueue . add (
'bird' ,
{ color: 'bird' },
{
repeat: {
every: 10000 ,
limit: 100 ,
},
jobId: 'pigeon' ,
},
);
Slow Repeatable Jobs
If job processing time exceeds the repeat frequency:
// Job repeats every 1 second but takes 5 seconds to process
await myQueue . add (
'slowJob' ,
{ data: 'test' },
{
repeat: { every: 1000 },
},
);
With 1 worker:
Next job is added to the delayed set when the current job starts processing
The worker takes 5 seconds to complete
Next job waits in the queue until the worker is free
Effective frequency: every 5 seconds (not 1 second)
With 5 workers:
Workers can maintain the desired 1 second frequency
Each worker processes jobs in parallel
Custom Repeat Strategy
You can define a custom strategy for scheduling repeatable jobs. Here’s an example using RRULE:
import { Queue , Worker } from 'bullmq' ;
import { rrulestr } from 'rrule' ;
const settings = {
repeatStrategy : ( millis , opts ) => {
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 using RRULE
await myQueue . add (
'bird' ,
{ color: 'green' },
{
repeat: {
pattern: 'RRULE:FREQ=SECONDLY;INTERVAL=10;WKST=MO' ,
},
jobId: 'colibri' ,
},
);
const worker = new Worker (
'Paint' ,
async () => {
doSomething ();
},
{ settings },
);
The repeat strategy setting must be provided in both Queue and Worker classes:
Queue: Calculates the first iteration when adding the job
Worker: Calculates subsequent iterations during processing
The repeat strategy function receives an optional jobName third parameter.
Custom Repeatable Key
By default, repeatable keys are generated based on repeat options and job name. You can provide a custom key to differentiate repeatable jobs with the same repeat options:
import { Queue } from 'bullmq' ;
const myQueue = new Queue ( 'Paint' , { connection });
// Repeat job every 10 seconds
await myQueue . add (
'bird' ,
{ color: 'gray' },
{
repeat: {
every: 10_000 ,
key: 'colibri' ,
},
},
);
// Another job with same interval but different key
await myQueue . add (
'bird' ,
{ color: 'brown' },
{
repeat: {
every: 10_000 ,
key: 'eagle' ,
},
},
);
Updating Repeatable Job Options
Using custom keys allows updating existing repeatable jobs:
// Update the 'eagle' job to repeat every 25 seconds instead of 10
await myQueue . add (
'bird' ,
{ color: 'turquoise' },
{
repeat: {
every: 25_000 ,
key: 'eagle' ,
},
},
);
This updates the existing repeatable job’s interval without creating a new one. Any delayed job for the old interval is replaced with the new settings.
Read More
Repeat Strategy API View the Repeat Strategy type definition
Remove Repeatable Job removeRepeatable API Reference
Remove by Key removeRepeatableByKey API Reference
Job Schedulers Modern approach to scheduled jobs (recommended)