Skip to content
Get started
Get started
Adding Jobs: Priorities, Delays and Bulk
View Markdown
guide · queue

Getting work into the queue.

One job or a hundred thousand, ordered by priority, held back by a delay, or written straight to disk when losing it is not an option.

const basicJob = await queue.add('job-name', { key: 'value' });
// With options
const configuredJob = await queue.add('job-name', data, {
priority: 10, // Higher = processed first
delay: 5000, // Wait 5s before processing
attempts: 5, // Max total executions, first run included (default: 3)
backoff: 2000, // Exponential base delay in ms (default: 1000, jitter applied, capped at 1h)
// OR: backoff: { type: 'exponential', delay: 2000 } // 'fixed' | 'exponential'
timeout: 30000, // Fail the job if processing takes longer
jobId: 'custom-id', // Custom ID, makes the add idempotent (see Deduplication)
removeOnComplete: true, // Delete job data after it completes
});

The full option list is in the reference table.

timeout starts when the broker marks the job active. The broker tracks the absolute processing deadline and fails the job with reason timeout when it is reached; it is not rounded to a maintenance sweep interval.

addBulk inserts all jobs in one batch, much faster than a loop of add:

const jobs = await queue.addBulk([
{ name: 'task-1', data: { id: 1 } },
{ name: 'task-2', data: { id: 2 }, opts: { priority: 10 } },
{ name: 'task-3', data: { id: 3 }, opts: { delay: 5000 } },
]);

On memory/SQLite brokers, addBulk is ordered and uses accepted-prefix semantics, including over TCP. If a later entry is rejected (for example by a group maxSize limit), earlier accepted entries remain queued and later entries are not evaluated. The rejected entry is never left as a hidden in-memory job. PostgreSQL brokers commit addBulk in one transaction: an admission error rolls back the batch. Use FlowProducer when the complete graph must commit atomically across every backend.

// Every 5 seconds
await queue.add('heartbeat', {}, { repeat: { every: 5000 } });
// Every 24 hours, at most 30 times
await queue.add('daily-report', {}, { repeat: { every: 86400000, limit: 30 } });
// Cron pattern
await queue.add('weekly', {}, { repeat: { pattern: '0 9 * * MON' } });

You can change the data for future runs at any point in the lifecycle with updateData(), even after the current run completes (the update follows the repeat chain to the next scheduled execution):

const job = await queue.add('sync', { endpoint: '/api/v1' }, { repeat: { every: 60000 } });
await job.updateData({ endpoint: '/api/v2' }); // Next run uses /api/v2

For named, managed schedules, see Job Schedulers and the Cron guide.

Durable jobs (no SQLite buffer-loss window)

Section titled “Durable jobs (no SQLite buffer-loss window)”

By default SQLite mode batches writes to disk for up to 10 ms. A crash inside that window can lose the not-yet-flushed jobs. For jobs where that is unacceptable, durable: true bypasses bunqueue’s buffer and commits before add() returns. Host, filesystem, and physical-media durability still apply:

await queue.add(
'process-payment',
{ orderId: '123', amount: 99.99 },
{
durable: true,
}
);

SQLite durable acceptance is fail closed. add() resolves only after SQLite commits the job and any related custom-ID retirement, dedup replacement, dependency pin, or parent link. If SQLite rejects the write—for example because the disk is full—the call rejects and that candidate is not queryable, counted, or available to a Worker. Reusing a completed or DLQ jobId is also atomic: a failed replacement preserves the previous generation and its result across a broker restart. The same SQLite contract applies in Embedded and TCP mode and to durable entries in addBulk. PostgreSQL admission is transactional whether or not the flag is set. Memory-only mode remains ephemeral: durable: true cannot make it survive a process restart.

SQLite modePublished native workload medianData loss windowUse for
Default186,384 jobs/s, public on-disk addBulkUp to 10 msRe-creatable work
Durable60,835 ops/s, sequential Embedded addsNo SQLite buffer-loss window after add() resolvesPayments, orders, audit records

Those figures label different workloads and are not a direct per-operation speedup ratio. See Benchmarks for distributions and the TCP rows. PostgreSQL admissions are already transactional and do not use this SQLite buffer.

GuideWhat it covers
Queue APICreate a queue in embedded or TCP mode
Deduplication and Idempotent Job AddsIdempotent adds, dedup keys, custom job ids
Querying JobsFetch jobs, states, counts and results
Queue Control and MaintenancePause, drain, obliterate, clean and repair
Progress, Job Logs and DependenciesProgress, per-job logs and dependencies
Queue Rate Limiting and Global ConcurrencyRate limits and global concurrency caps
Job Schedulers from the QueueNamed repeatable schedules from the queue
DLQ Operations from the Queue ObjectFailed-job operations from the Queue object
Workers, Stats and Metrics from the QueueRegistered workers, stats and metrics windows
Namespaces, Auto-Batching and Store-and-ForwardNamespaces, auto-batching, store-and-forward
JobOptions ReferenceEvery JobOptions field, with defaults