·QueueHub Team·6 min read

BullMQ Job Priorities: When to Use priority() and Why It Matters

BullMQRedisqueue managementjob prioritiesworker design

Most BullMQ users pass an options object to add() that covers delay, attempts, and backoff. The priority field gets skipped more often than not. That is a missed opportunity — and occasionally a hidden problem.

BullMQ's priority system lets you steer which jobs get processed first when multiple jobs are waiting in the same queue. It is not free: it changes how jobs are stored in Redis, it interacts with delayed jobs in surprising ways, and overuse can starve lower-priority traffic. This post explains the internals, the right use cases, and the trade-offs so you can decide whether priority belongs in your worker design.

How BullMQ Implements Priorities

Under the hood, BullMQ stores waiting jobs in a Redis sorted set keyed by score. When you call queue.add('send-email', payload, { priority: 1 }), BullMQ assigns the job a score derived from its priority value and enqueues it in that sorted set.

When a worker calls getNextJob(), it pops the job with the lowest score — meaning the highest priority number gets processed first. This is counter-intuitive at first glance: priority: 1 is more urgent than priority: 5.

import { Queue } from 'bullmq';

const emailQueue = new Queue('emails', { connection: { port: 6379 } });

// Urgent transactional email — processed before marketing blasts
await emailQueue.add('send', {
  to: 'user@example.com',
  template: 'password-reset',
}, { priority: 1 });

// Low-priority newsletter — runs when workers are idle
await emailQueue.add('send', {
  to: 'newsletter-list',
  template: 'weekly-digest',
}, { priority: 10 });

The valid priority range is 1 (most urgent) through MAX_INT (least urgent). BullMQ does not enforce a hard ceiling, but in practice values above a few hundred create a very flat distribution and make tuning harder.

When Priorities Actually Help

Priority queues shine when you have a mixed workload with clearly distinct urgency tiers. Here are the patterns that justify the added complexity:

  • Transactional vs. batch jobs. Password-reset emails, payment confirmations, and account alerts should jump ahead of newsletter sends and analytics aggregation. Assigning transactional jobs priority: 1 and batch jobs priority: 5 keeps user-facing latency low without dedicating separate queues or workers.
  • SLA tiers in a multi-tenant system. If your platform serves Free, Team, and Enterprise customers on the same infrastructure, priority queues let Enterprise jobs naturally outrank Free-tier jobs — no custom routing logic required.
  • Abuse throttling without dropping work. When a spike in low-value jobs (webhook retries, log ingestion) threatens to drown out critical work, lowering their priority keeps the queue functional instead of resorting to rate-limiting or dropping jobs entirely.

If your queue only ever processes one type of job, or if all jobs have similar latency requirements, priorities add complexity with no benefit. Skip them.

The Head-of-Line Blocking Problem

Priority queues have a well-known downside: head-of-line blocking. If your high-priority lane is saturated — say, a flood of password-reset requests during a security incident — lower-priority jobs will not get processed until the urgent queue drains.

In BullMQ specifically, this manifests as:

  • Starved workers: all available workers pull from the high-priority bucket, ignoring lower-priority jobs entirely.
  • Growing backlog: low-priority jobs accumulate with increasing wait times, but you only notice when you inspect the queue count.
  • Metric distortion: a dashboard showing "all workers active" masks the fact that lower-priority work is not progressing.

Mitigations are straightforward:

  • Reserve worker slots. Run a small dedicated pool of workers that only pull from lower-priority ranges.
  • Cap priority range. Keep it to 2–3 distinct levels. Ten levels of priority is harder to reason about and easier to misconfigure.
  • Monitor per-priority depth. A queue dashboard that breaks down waiting counts by priority band (e.g. priority: 1–3, priority: 4–7) makes starvation visible immediately. Tools like QueueHub surface per-queue depth and job distributions so you can spot a blocked lane before it becomes a backlog crisis. For a broader look at available monitoring options, see our guide to the best BullMQ dashboard alternatives in 2026.

Priorities and Delayed Jobs Don't Mix

BullMQ processes delayed jobs separately from the waiting sorted set — they live in a different Redis key until their timestamp arrives. As a result, a delayed high-priority job will not jump ahead of waiting lower-priority jobs when it becomes ready.

If you need both scheduling and urgency steering, consider one of these patterns:

  1. Repeatable jobs instead of delayed jobs for recurring scheduled work. Repeatable jobs live in the waiting set and participate in priority ordering.
  2. Enqueue at priority time. When a delayed job is due, re-add it with the appropriate priority rather than relying on BullMQ's delayed-job promotion logic.
  3. Separate queues by urgency tier. A dedicated emails:urgent queue with its own workers is more explicit and easier to monitor than a single queue with mixed priorities. For understanding how a real-time queue dashboard surfaces these distinctions, see QueueHub vs RedisInsight.

Observing Priority Behavior

Once you have priorities in production, you need to verify that the system behaves as expected. The key questions to answer:

  • Are urgent jobs actually getting picked up first?
  • Is any priority band experiencing unexpected wait times?
  • How does worker throughput distribute across priority levels?

Without a queue dashboard, answering these questions means adding ad-hoc logging or running Redis CLI queries against internal BullMQ sorted sets — both fragile approaches.

A BullMQ-aware dashboard gives you direct visibility into job counts by status and queue. If you want a deeper look at how monitoring fits into a production BullMQ setup, see our guide to the best BullMQ dashboard alternatives in 2026 and our comparison of QueueHub vs RedisInsight for understanding the difference between a queue-aware tool and a general Redis GUI.

Summary

BullMQ job priorities are a simple concept — pass a number to priority() — but the implications touch your data model, your worker design, and your monitoring strategy. They are genuinely useful for separating urgent transactional work from background batch processing, but they introduce head-of-line blocking risk and interact poorly with delayed jobs.

Start small: two priority levels, clear naming, and a dashboard that surfaces per-queue depth. If you outgrow that, dedicated queues with separate worker pools are a cleaner architectural step than adding more priority tiers.

Related Articles