---
title: BullMQ Worker Not Processing Jobs? Fix It (2026)
description: BullMQ worker not processing jobs? Work through queue name and prefix, maxRetriesPerRequest, stalled-job locks, and the v6 ioredis peer dependency change.
url: https://nerdleveltech.com/bullmq-worker-not-processing-jobs
language: en
date: '2026-08-03'
category: backend
tags:
  - bullmq
  - redis
  - job queue
  - nodejs
  - backend
  - stalled jobs
  - ioredis
markdown_url: https://nerdleveltech.com/bullmq-worker-not-processing-jobs.md
attribution: 'Source: NerdLevelTech — https://nerdleveltech.com/bullmq-worker-not-processing-jobs'
terms: https://nerdleveltech.com/llms.txt
---

# BullMQ Worker Not Processing Jobs? Fix It (2026)

A BullMQ worker that never picks up jobs usually fails one of four checks: it is not connected to Redis, it points at a different queue name or prefix than the producer, the queue is paused or rate limited, or jobs are stalling. Check them in that order.

**TL;DR:** Start by proving the worker is alive and connected, then prove it shares the producer's exact queue name *and* prefix, then check `isPaused()` and `getJobCounts()` before you touch any timing options. Only after those come the stalled-job settings — `lockDuration` and `stalledInterval` both default to 30,000 ms in `bullmq@6.0.5`.[^1] If the symptom started right after an upgrade, BullMQ v6 shipped on 30 July 2026 and moved `ioredis` to an *optional* peer dependency, so `npm install bullmq@6` on its own installs no Redis client at all.[^2][^3]

## What you'll learn

- The ordered checklist that isolates the cause in four steps instead of guessing
- Why jobs sit in `waiting` while workers look perfectly healthy
- How a queue name or `prefix` mismatch silently splits producer from consumer
- What `maxRetriesPerRequest must be null` actually means, and why it throws in one case and only warns in another
- Why a missing `error` listener can stop a worker dead
- The two failure modes introduced by BullMQ v6: the `ioredis` peer dependency and legacy repeatable-job metadata
- What stalled jobs really are, and which defaults to change (and which to leave alone)
- Whether a paused queue, a rate limiter, or global concurrency is throttling you
- Whether you still need `QueueScheduler` for delayed jobs

## Why is my BullMQ worker not processing jobs?

Because the worker and the jobs are not meeting. That happens in one of four places, and checking them in order is faster than reading your processor code: **connection** (the worker never reached Redis), **identity** (queue name or `prefix` differs from the producer's), **state** (the queue is paused, rate limited, or concurrency-capped), and **lifecycle** (jobs enter `active`, lose their lock, and get pushed back to `waiting`).

The reason ordering matters is that the stalled-job path — the category most troubleshooting advice reaches for first — is the only one of the four that requires a job to have reached `active` at all. If your `active` count is flat at zero, no amount of `lockDuration` tuning will help you, because nothing is ever acquiring a lock in the first place.

## How do I debug a BullMQ worker that is not picking up jobs?

Ask the queue what it thinks is happening before you change any code. These four calls answer the "connection, identity, state" questions in one pass:

```typescript
import { Queue } from 'bullmq';

const queue = new Queue('emails', { connection, prefix: 'bull' });

console.log('paused:', await queue.isPaused());
console.log('counts:', await queue.getJobCounts());
console.log('workers:', await queue.getWorkersCount());
console.log('rate-limit ttl:', await queue.getRateLimitTtl(100));
```

Read the output like this:

| Observation | What it points to |
|---|---|
| `getWorkersCount()` is `0` | The worker is not connected, or is registered under a different name/prefix |
| `paused: true` | The queue is globally paused — nothing will be picked up[^4] |
| `waiting` climbing, `active` stays `0` | Identity mismatch, pause, rate limit, or global concurrency |
| `active` non-zero but jobs never finish | Stalled path: locks, event-loop blocking, or a crashing processor |
| `rate-limit ttl` greater than `0` | The limiter is holding jobs in `waiting`[^5] |

`getWorkersCount()` returning zero while your process is clearly running is the highest-yield line here, because it collapses "is it connected?" and "is it the same queue?" into one answer. Called with no arguments, `getJobCounts()` reports `active`, `completed`, `delayed`, `failed`, `prioritized`, `waiting` and `waiting-children` — the default set in `bullmq@6.0.5`.[^1]

One caveat on that first line, because it changes how much you should trust it. `getWorkersCount()` is built on Redis's `CLIENT LIST`, and BullMQ's own type definitions warn that "GCP does not support SETNAME, so this call will not work." On a managed Redis that rejects the `CLIENT` command, BullMQ catches the error and substitutes a single placeholder entry, so **the count comes back as `1` whether or not a worker is actually attached**.[^1] If you are on Memorystore or a similarly locked-down provider, treat a count of exactly `1` as "unknown" rather than as proof, and fall back to checking whether `active` ever moves.

## Why are my BullMQ jobs stuck in the waiting state?

`waiting` is where jobs pile up for several *different* reasons, which is why it is such a confusing symptom. A rate-limited job is not a broken job — BullMQ's docs are explicit that "jobs that get rate limited will actually stay in the waiting state."[^5] A stalled job is also returned to `waiting`, not to a state of its own.[^6]

So `waiting` growing tells you a job has not been claimed *yet*. It does not tell you why. The three benign explanations are a paused queue, an active rate-limit window, and a global concurrency cap that is already saturated. The two broken explanations are no connected worker and a queue-identity mismatch. Distinguish them with `getWorkersCount()` and `isPaused()` before assuming a bug.

One more thing worth knowing about the limiter: it is global across your fleet, not per-process. The docs put it plainly — with `max: 10, duration: 1000`, "if you have for example 10 workers for one queue with the above settings, still only 10 jobs will be processed by second."[^5] Scaling out worker replicas will not move a rate-limited queue any faster.

## Do the queue and the worker need the same name and prefix?

Yes — exactly the same, on both. A `Queue` and a `Worker` only find each other through the Redis keys they compute, and those keys are derived from the queue name plus the `prefix` option, which "defaults to `bull`" in `bullmq@6.0.5`.[^1] If the producer sets `prefix: 'myapp'` and the worker leaves it unset, they are operating on two entirely separate key spaces, and neither will report an error.

```typescript
// Producer and consumer must agree on BOTH arguments.
const queue = new Queue('emails', { connection, prefix: 'myapp' });
const worker = new Worker('emails', handler, { connection, prefix: 'myapp' });
```

This is a common casualty of environment-variable drift: one service reads `QUEUE_PREFIX` and another was deployed before that variable existed. BullMQ's troubleshooting page flags the related hazard of passing undefined or empty-string values into queue names, which surfaces as an opaque Lua error rather than a clear message: `ERR Error running script ... Lua redis() command arguments must be strings or integers`.[^7] Validate those variables at startup rather than letting them reach Redis.

Note that `prefix` is a Redis-specific concept in v6. The shipped type definitions explain that it is deliberately not part of the shared queue options "because it is a Redis-specific concept: other backends namespace differently (e.g. the PostgreSQL backend uses a schema) and ignore it."[^1]

## Why does BullMQ throw "maxRetriesPerRequest must be null"?

Because a worker holds a blocking connection to Redis, and if ioredis is allowed to give up on a command after a fixed number of retries, that blocking call can fail and take the worker's fetch loop down with it. BullMQ's guidance is that for workers this option must be `null` so that "the workers will keep processing forever as long as there is a working connection."[^8]

What is not obvious — and what changes how you debug this — is that BullMQ reacts in two different ways depending on *how* you pass the connection. Running both cases against `bullmq@6.0.5` with `ioredis@5.11.1` produces:

| How you pass the connection | Result |
|---|---|
| An existing ioredis **instance** with a non-null `maxRetriesPerRequest` | Constructor **throws**: `BullMQ: Your redis options maxRetriesPerRequest must be null.` |
| A plain options **object** with `maxRetriesPerRequest: 20` | Logs `BullMQ: WARNING! Your redis options maxRetriesPerRequest must be null and will be overridden by BullMQ.` and continues, with BullMQ setting it to `null` |

The second row is the one to watch. Because it only warns, the message can be lost in a container log while the value you set is silently replaced — so if you deliberately configured a retry ceiling for a worker, BullMQ has already discarded it. ioredis defaults this setting to 20 when you construct a client yourself,[^8] so a hand-rolled shared client will hit one of these two paths. Two limits are worth knowing: the check only fires for *blocking* connections, so the same client handed to a `Queue` triggers neither branch, and it is a truthiness test, so `maxRetriesPerRequest: 0` also slips past both. Worth noting too that the production guide describes both cases as producing a warning;[^10] the throw above is what `bullmq@6.0.5` actually does when handed a client instance.

```typescript
import IORedis from 'ioredis';

// Correct for a Worker: no retry ceiling on the blocking connection.
const connection = new IORedis({ maxRetriesPerRequest: null });
const worker = new Worker('emails', handler, { connection });
```

While you are in that constructor: do not set ioredis's `keyPrefix`. Passing an instance built with `keyPrefix` throws `BullMQ: ioredis does not support ioredis prefixes, use the prefix option instead.` — verified against the same version pair. Use BullMQ's own `prefix` option instead, as covered above.

A `Queue` used by a request handler wants the opposite behaviour. The docs recommend leaving `maxRetriesPerRequest` at its default (or setting it to 1) for producers so an HTTP caller fails fast,[^8] and — on the production guide — disabling ioredis's `enableOfflineQueue` on the `Queue` while leaving it enabled on the `Worker`.[^10]

## Why did my BullMQ worker stop processing jobs after an error?

Because an `EventEmitter` that emits `error` with no listener attached raises in Node.js, and BullMQ's own documentation carries this as a danger notice: "If the error handler is missing, your worker may stop processing jobs when an error is emitted!"[^9] It is a two-line fix, and an easy one to omit, because nothing in the happy path requires it.

```typescript
worker.on('error', err => {
  logger.error({ err }, 'bullmq worker error');
});

queue.on('error', err => {
  logger.error({ err }, 'bullmq queue error');
});
```

The related gotcha is `autorun`. A worker "launches the processor immediately" when constructed, unless you pass `autorun: false` — in which case nothing is processed until you call `worker.run()` yourself.[^9] The default is `true` in `bullmq@6.0.5`,[^1] so this only bites people who set the option deliberately in tests or in a staged-startup sequence and then forgot the matching `run()`.

## Do I need to install ioredis separately for BullMQ v6?

Yes, if you want the ioredis backend — and this changed in v6. BullMQ v5 listed `ioredis` as a hard dependency (pinned at `5.11.1` in `5.81.3`). BullMQ v6.0.5 does not list it at all; `ioredis`, `redis`, `pg` and `bullmq-otel` are declared as **peer dependencies marked optional**.[^2] Optional peer dependencies are not auto-installed, so a clean install produces a package tree with no Redis client in it.

Installing `bullmq@6.0.5` on its own into an empty project (npm 10.9.8, Node 22.22.3) produces eight top-level entries in `node_modules` — `bullmq` and its direct dependencies, plus the `luxon` and msgpackr-extract packages those pull in. None of them is a Redis client. Requiring the package then fails immediately:

```text
Error: Cannot find module 'ioredis/built/utils'
```

The fix is one line:

```bash
npm install bullmq ioredis
```

This is a consequence of the headline v6 change. The release is described as "release BullMQ v6 with pluggable queue backends,"[^3] and the connection layer now supports node-redis and Bun's Redis client alongside ioredis, with the production guide giving separate reconnect advice for each.[^10] Making every client optional is what lets you install only the one you use. It also means an upgrade that looks like a patch bump to a lockfile can leave a service with no Redis driver.

## Why did my worker break after upgrading to BullMQ v6?

Beyond the missing client, the other upgrade trap is repeatable jobs. BullMQ v6 removed the legacy repeatable-job API entirely, and the migration guide is blunt about what happens if old data is left behind: "Legacy repeatable jobs stored by BullMQ v5 are not supported in BullMQ v6. If v6 encounters legacy repeatable-job metadata, it raises an error instead of trying to keep running with partially compatible behavior."[^11]

The message reads:

```text
Legacy repeatable job metadata is not supported in BullMQ v6 (key: "..."). Migrate legacy repeatable jobs to Job Schedulers before upgrading. See https://docs.bullmq.io/guide/migrations/migrate-from-v5-to-v6
```

Where you see it depends on what touched the metadata. Listing schedulers with `queue.getJobSchedulers()` raises it directly. A worker that finishes a job carrying a legacy repeat key takes a different route: it cannot schedule the next iteration, so it **emits** the message on its own `error` event, wrapped as `Failed to add repeatable job for next iteration: <message>`.[^1] That detail matters here — if you skipped the `error` listener from the previous section, this arrives silently, and all you observe is that a recurring job quietly stopped recurring.

These v5 APIs are gone in v6: `Queue.add(..., { repeat })`, `Queue.addBulk(..., { repeat })`, `Queue.getRepeatableJobs()`, `Queue.removeRepeatable()`, `Queue.removeRepeatableByKey()`, and the `Repeat` class. The replacements are `queue.upsertJobScheduler(...)`, `queue.getJobSchedulers()` and `queue.removeJobScheduler(...)`.[^11]

The migration is meant to happen while you are still on v5: enumerate the old definitions with `getRepeatableJobs()`, recreate each one as a Job Scheduler, verify with `getJobSchedulers()`, then delete the legacy entries — and only deploy v6 once every producer and worker is on schedulers.[^11] Two smaller breaks are worth grepping for at the same time: `repeat.utc` is replaced by `upsertJobScheduler(..., { tz: 'UTC' })`, and **`Worker.resume()` is now asynchronous** — its signature changed from `resume(): void` in `5.81.3` to `resume(): Promise<void>` in `6.0.5`, so an un-awaited call now leaves a floating promise. Note that the migration guide files this change under the heading "`Queue.resume()` is asynchronous," but `Queue.resume()` already returned `Promise<void>` in v5; the type definitions in both packages show the change is on `Worker`.[^11][^1]

## What are stalled jobs in BullMQ and how do I stop them?

A stalled job is one that reached `active`, then failed to renew its lock in time, so BullMQ assumes the worker died and returns the job to `waiting`. There is no stalled *state* — the docs note that there is "only a 'stalled' event emitted when a job is automatically moved from *active* to *waiting* state."[^6] If a job stalls more than `maxStalledCount` times it is failed permanently with the reason *job stalled more than allowable limit*.[^6]

The defaults, read from the type definitions shipped in `bullmq@6.0.5`:[^1]

| Option | Default | What it controls |
|---|---|---|
| `lockDuration` | 30000 ms | How long a worker's claim on a job survives without renewal |
| `lockRenewTime` | half `lockDuration` | Renewal cadence; the docs advise against changing it |
| `stalledInterval` | 30000 ms | How often the stalled check runs |
| `maxStalledCount` | 1 | Recoveries from stalled before the job is failed |
| `concurrency` | 1 | Jobs one worker instance runs in parallel |
| `drainDelay` | 5 (seconds) | Long-poll window when the queue is empty |
| `maximumRateLimitDelay` | 30000 ms | Idle ceiling while rate limited |

The important framing is that a stalled job is usually a *symptom of blocked code*, not of a badly tuned lock. Node.js is single-threaded, and BullMQ's guidance is that "if the CPU is very busy (due to the process being very CPU intensive), the worker may not have time to renew the lock."[^12] Raising `lockDuration` on a processor that blocks the event loop for a minute buys you a longer silence, not a fix. The two real fixes are returning control to the event loop more often, and moving genuinely CPU-bound work into a sandboxed processor, which by default runs the job in a separate Node.js process (`useWorkerThreads` switches it to a worker thread instead).[^6][^1]

Four non-obvious causes of lock loss are worth checking before you touch any numbers, all listed on BullMQ's troubleshooting page under "Missing Locks": CPU starvation, lost communication with Redis, the job being force-removed by one of the removal APIs, and a wrong Redis `maxmemory` policy.[^7] That last one deserves its own emphasis — the production guide states that configuring `maxmemory-policy` to `noeviction` "is the **only** setting that guarantees the correct behavior of the queues."[^10] A Redis instance provisioned from a cache template will happily evict BullMQ's keys under memory pressure.

Deploys are the other routine source of stalls. A worker killed mid-job leaves that job to be recovered by the stalled check "with a waiting time of about 30 seconds by default," which is why the production guide recommends handling `SIGINT` and `SIGTERM` and awaiting `worker.close()` before exit.[^10]

## Is my queue paused, rate limited, or concurrency capped?

All three look identical from the outside — jobs accumulate in `waiting` and workers sit idle — and all three are answerable with one call each.

**Paused.** A globally paused queue means "no workers will pick up any jobs from the queue."[^4] Workers already mid-job finish that job and then idle. Check `await queue.isPaused()` and resume with `await queue.resume()`. A *worker*-level pause is a separate mechanism: the docs describe `myWorker.pause()` as stopping that instance from taking new jobs while it finishes its current ones,[^4] and because it is scoped to the instance rather than to the queue's shared state, it will not make `queue.isPaused()` return `true`.

**Rate limited.** `await queue.getRateLimitTtl(maxJobs)` returns a value greater than zero while the limiter window is open, and `await queue.removeRateLimitKey()` clears it and lets workers pick jobs again.[^5] Remember the limiter is global, and that group-key rate limiting was removed in BullMQ 3.0.[^5]

**Concurrency capped.** `setGlobalConcurrency(n)` caps parallel processing across every worker instance, and per-worker `concurrency` cannot exceed it — the docs note that a worker's own setting "will not override the global one."[^13] Read the current value with `await queue.getGlobalConcurrency()` and clear it with `await queue.removeGlobalConcurrency()`.[^13]

## Do I still need QueueScheduler for delayed jobs?

No. `QueueScheduler` was required before BullMQ 2.0 for delayed and retried jobs to be promoted, and its absence was once a genuine cause of jobs never running. The current docs repeat the same line in three places: "From BullMQ 2.0 and onwards, the `QueueScheduler` is not needed anymore."[^6][^14][^5]

This matters for diagnosis because guidance written against BullMQ 1.x instructs you to add a `QueueScheduler`, and that advice is obsolete on any currently supported version. If delayed jobs are not firing on v5 or v6, look at the causes above instead, particularly the connection and identity checks.

One genuine version floor does exist, though. `bullmq@6.0.5` refuses to run against Redis older than **5.0.0**, throwing `Redis version needs to be greater or equal than 5.0.0`, and logs a recommendation to run at least **6.2.0**.[^1] Some blocking-call optimisations are additionally gated on Redis 6.0.0 and 7.0.8.[^1]

## Frequently asked questions

**Why is my BullMQ worker not processing jobs?**
Check four things in order: that the worker is connected to Redis, that it uses the same queue name and `prefix` as the producer, that the queue is not paused or rate limited, and only then whether jobs are stalling. `getWorkersCount()` returning zero settles the first two at once.

**Why are my jobs stuck in the waiting state?**
`waiting` is the holding area for unclaimed jobs and for rate-limited and stalled ones alike. Rate-limited jobs explicitly stay in `waiting`,[^5] and a stalled job is moved back there rather than into a state of its own.[^6]

**Do the queue and the worker need the same prefix?**
Yes. `prefix` defaults to `bull` and namespaces every Redis key for that queue.[^1] A producer and a consumer with different prefixes operate on separate key spaces and never report an error.

**What does "maxRetriesPerRequest must be null" mean?**
That a worker's blocking Redis connection has a retry ceiling it should not have. Passing an ioredis *instance* with a non-null value throws; passing an options *object* only logs a warning and BullMQ overrides the value to `null`.[^8]

**How do I debug a worker that is not picking up jobs?**
Ask the queue rather than reading your processor: `isPaused()`, `getJobCounts()`, `getWorkersCount()` and `getRateLimitTtl()` cover connection, identity and state in one pass. Treat `getWorkersCount()` of exactly `1` with suspicion on managed Redis that blocks the `CLIENT` command.[^1]

**Why did my worker stop processing jobs after an error?**
Most likely no `error` listener is attached. BullMQ's docs warn that "if the error handler is missing, your worker may stop processing jobs when an error is emitted."[^9] Attach one to both the worker and the queue.

**What are stalled jobs?**
Jobs that reached `active` and then failed to renew their lock, so BullMQ assumes the worker died and returns them to `waiting`. There is no stalled state, only a stalled event.[^6] After `maxStalledCount` recoveries (default 1) the job is failed permanently.[^1][^6]

**Is my queue paused, rate limited, or concurrency capped?**
All three look identical from outside. Check `await queue.isPaused()`,[^4] `await queue.getRateLimitTtl(maxJobs)`,[^5] and `await queue.getGlobalConcurrency()`[^13] — one call each, and only the first is a true error state.

**Does BullMQ v6 install ioredis for me?**
No. In v6, `ioredis` is an optional peer dependency rather than a direct one, so `npm install bullmq@6` on its own leaves you without a Redis client and `require('bullmq')` fails with `Cannot find module 'ioredis/built/utils'`.[^2] Install `bullmq` and `ioredis` together.

**Why did my worker break after upgrading to v6?**
Most likely the missing Redis client, or leftover repeatable-job metadata. v6 removed the legacy repeatable APIs and raises an error when it encounters v5 repeatable metadata rather than degrading.[^11]

**Should I raise lockDuration to stop stalled jobs?**
Only if your jobs legitimately run longer than the lock. If the processor blocks the Node.js event loop, a longer lock delays the failure rather than preventing it — return control to the event loop or use a sandboxed processor instead.[^12][^6]

**Do I still need QueueScheduler?**
No — it has been unnecessary since BullMQ 2.0.[^6] Any guide still recommending it for delayed jobs predates that release.

**Can Redis eviction cause this?**
Yes. Set `maxmemory-policy` to `noeviction`; the production guide calls it the only setting that guarantees correct queue behaviour, and a wrong policy is listed as a cause of lost job locks.[^10][^7]

## Bottom line

Resist the urge to start with `lockDuration`. The fastest path through this problem is to prove connection, then identity, then state, and only then look at the stalled-job path — because three of those four produce the same "worker idle, jobs waiting" symptom and only the last one is a timing problem. Since the v6 release on 30 July 2026 there is one extra step: if the symptom appeared right after a dependency bump, check whether you landed on v6 and lost your Redis client along the way.

If you are re-evaluating the queue itself rather than debugging it, it is worth knowing what the alternatives cost. A [Postgres-backed job queue with pg-boss](https://nerdleveltech.com/pg-boss-postgres-job-queue-node-typescript-production-tutorial) removes Redis from the picture entirely, [NATS JetStream with durable workers and a DLQ](https://nerdleveltech.com/nats-jetstream-typescript-durable-worker-retries-dlq-tutorial) trades it for a different operational model, and the broader [Kafka vs RabbitMQ vs SQS vs NATS comparison](https://nerdleveltech.com/message-queue-showdown-kafka-vs-rabbitmq-vs-sqs-vs-nats) covers where each one fits. If you are staying on Redis, the same `noeviction` and connection-reuse concerns show up across [Redis caching patterns](https://nerdleveltech.com/redis-caching-patterns-the-complete-guide-for-scalable-systems) too.

---

[^1]: Type definitions and compiled source shipped in `bullmq@6.0.5`, installed and read on 2026-08-03 — `interfaces/worker-options.d.ts` (`@defaultValue` tags for `autorun`, `concurrency`, `lockDuration`, `stalledInterval`, `maxStalledCount`, `drainDelay`, `maximumRateLimitDelay` and `useWorkerThreads`; `lockRenewTime`'s "half the lockDuration" default is stated in prose rather than in a tag), `interfaces/queue-options.d.ts` (`KeyPrefixOptions.prefix`, "Defaults to `bull`"), `classes/queue-getters.js` and `.d.ts` (the `sanitizeJobTypes` default type list; `baseGetClients` substituting a single placeholder entry when Redis rejects the `CLIENT` command; the "GCP does not support SETNAME, so this call will not work" note on `getWorkers`), `classes/worker.js` (the `Failed to add repeatable job for next iteration:` wrapper emitted on the worker's `error` event) and `classes/redis-connection.js` (`minimumVersion = '5.0.0'`, `recommendedMinimumVersion = '6.2.0'`, capability gates at 6.0.0 and 7.0.8). `Worker.resume()` compared across the installed `bullmq@5.81.3` (`resume(): void`) and `bullmq@6.0.5` (`resume(): Promise<void>`). https://www.npmjs.com/package/bullmq
[^2]: npm registry metadata for `bullmq`, read 2026-08-03: `6.0.5` lists `pg`, `redis`, `ioredis` and `bullmq-otel` under `peerDependencies` with all four marked `optional: true` in `peerDependenciesMeta`, and does not list `ioredis` under `dependencies`; `5.81.3` lists `ioredis: 5.11.1` as a direct dependency. https://registry.npmjs.org/bullmq
[^3]: BullMQ v6.0.0 release, published 30 July 2026: "feat!: release BullMQ v6 with pluggable queue backends." https://github.com/taskforcesh/bullmq/releases/tag/v6.0.0
[^4]: BullMQ documentation — Pausing queues. https://docs.bullmq.io/guide/workers/pausing-queues
[^5]: BullMQ documentation — Rate limiting. https://docs.bullmq.io/guide/rate-limiting
[^6]: BullMQ documentation — Stalled (jobs). https://docs.bullmq.io/guide/jobs/stalled
[^7]: BullMQ documentation — Troubleshooting. https://docs.bullmq.io/guide/troubleshooting
[^8]: BullMQ documentation — Connections. https://docs.bullmq.io/guide/connections
[^9]: BullMQ documentation — Workers. https://docs.bullmq.io/guide/workers
[^10]: BullMQ documentation — Going to production. https://docs.bullmq.io/guide/going-to-production
[^11]: BullMQ documentation — Migrate from v5 to v6. https://docs.bullmq.io/guide/migrations/migrate-from-v5-to-v6
[^12]: BullMQ documentation — Stalled Jobs (workers). https://docs.bullmq.io/guide/workers/stalled-jobs
[^13]: BullMQ documentation — Global Concurrency. https://docs.bullmq.io/guide/queues/global-concurrency
[^14]: BullMQ documentation — Queues. https://docs.bullmq.io/guide/queues
