BullMQ 1.3.0

dotnet add package BullMQ --version 1.3.0
                    
NuGet\Install-Package BullMQ -Version 1.3.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="BullMQ" Version="1.3.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="BullMQ" Version="1.3.0" />
                    
Directory.Packages.props
<PackageReference Include="BullMQ" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add BullMQ --version 1.3.0
                    
#r "nuget: BullMQ, 1.3.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package BullMQ@1.3.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=BullMQ&version=1.3.0
                    
Install as a Cake Addin
#tool nuget:?package=BullMQ&version=1.3.0
                    
Install as a Cake Tool

BullMQ for .NET

The official .NET port of BullMQ, the fast and robust Redis- and PostgreSQL-based distributed queue.

This binding is glue code on top of the exact same Lua (Redis) and SQL (PostgreSQL) scripts used by every other BullMQ runtime (Node.js, Python, PHP, Elixir, Rust). Sharing the scripts is what guarantees identical, battle-tested atomicity and semantics across languages: a queue produced by the Node.js library can be consumed by a .NET worker and vice versa.

Status: the database-agnostic queue-backend contract is fully implemented for both Redis and PostgreSQL and validated by a shared conformance suite (the exact same tests run against both adapters). The high-level Queue, Worker and Job classes run on either backend — select PostgreSQL by setting Postgres on the options. FlowProducer, QueueEvents and JobScheduler are also implemented.

Requirements

  • .NET 8.0 or newer
  • A Redis 6.2+ server (or a compatible server such as Valkey/Dragonfly), or a PostgreSQL 13+ server when using the PostgreSQL backend

Installation

dotnet add package BullMQ

Quick start

using BullMQ;

// Create a queue and add a job.
await using var queue = new Queue("emails", new QueueOptions
{
    Connection = ConnectionOptions.FromString("localhost:6379"),
});

await queue.AddAsync("welcome", new { to = "user@example.com" });

// Process jobs with a worker.
await using var worker = new Worker("emails", async (job, ct) =>
{
    Console.WriteLine($"Processing {job.Name} #{job.Id}");
    // ... do the work ...
    return "sent";
}, new WorkerOptions
{
    Connection = ConnectionOptions.FromString("localhost:6379"),
    Concurrency = 5,
});

worker.Completed += (job, result) =>
    Console.WriteLine($"Job {job.Id} completed with '{result}'");

Sharing a connection

Pass an existing IConnectionMultiplexer to reuse a connection across queues and workers (BullMQ will not dispose a connection it does not own):

var mux = await StackExchange.Redis.ConnectionMultiplexer.ConnectAsync("localhost:6379");
var options = new QueueOptions
{
    Connection = new ConnectionOptions { Multiplexer = mux },
};

Using PostgreSQL

Set Postgres on the options to run the exact same Queue/Worker/Job API against PostgreSQL instead of Redis. The schema (default bullmq) namespaces all queues and the required tables/functions are migrated automatically on first use.

using BullMQ;
using BullMQ.Postgres;

var pg = new PostgresOptions
{
    ConnectionString = "Host=localhost;Database=bullmq;Username=postgres;Password=postgres",
    // Schema = "bullmq", // optional, this is the default
};

await using var queue = new Queue("emails", new QueueOptions { Postgres = pg });
await queue.AddAsync("welcome", new { to = "user@example.com" });

await using var worker = new Worker("emails", async (job, ct) => "sent",
    new WorkerOptions { Postgres = pg, Concurrency = 5 });

Deduplication

Set JobsOptions.Deduplication to suppress jobs with the same identifier. The same options work with Redis and PostgreSQL, single and bulk adds, and QueueOptions.DefaultJobOptions.

await queue.AddAsync("refresh", new { version = 1 }, new JobsOptions
{
    Delay = 5000,
    Deduplication = new DeduplicationOptions
    {
        Id = "refresh",
        Reschedule = true,
        Ttl = 10000,
        Extend = true,
    },
});
  • Simple: only Id; suppress duplicates until completion or failure.
  • Throttle: set Ttl in milliseconds to suppress duplicates for that window.
  • Extend: refresh the TTL on duplicate adds.
  • Replace: replace the existing delayed job with the incoming ID and payload.
  • Reschedule: retain the delayed job's ID, name and payload, but apply the incoming delay relative to its timestamp. Omitted/zero delay makes it due. Non-delayed jobs are unchanged. TTL extension is independent; Replace takes precedence if both flags are enabled.
  • KeepLastIfActive: retain only the latest successor payload while the winner is active; create it when that winner completes or fails. TTL is ignored and the key stays live for the job lifecycle.

A per-job deduplication object replaces the entire queue-default object. Duplicate adds return the winning ID, but their returned object still describes the incoming payload; use queue.GetJobAsync(id) to inspect stored job data. This matches the reference BullMQ API.

var winner = await queue.GetDeduplicationJobIdAsync("refresh");
await queue.RemoveDeduplicationKeyAsync("refresh"); // Unconditional removal.
if (winner is not null && await queue.GetJobAsync(winner) is { } job)
{
    await job.RemoveDeduplicationKeyAsync(); // Removes only if still the owner.
}

QueueEvents.Deduplicated exposes DeduplicationId, JobId and DeduplicatedJobId. The event is also delivered to EventReceived. Rescheduling additionally emits Delayed; replacement retains the scripts' Removed events.

Flow validation happens before any writes. Deduplication is forbidden on all nodes with children and on every child node, including nested leaves. Deduplication remains supported on standalone roots without children; Replace must be omitted in flows. A dedup winner may belong to another parent, and jobs cannot belong to multiple parents. Standalone root leaves support the other modes, including KeepLastIfActive. This stricter validation applies to the .NET flow API; other ports retain their existing behavior.

Adding deduplication management extends IQueueBackend with winner lookup, unconditional deletion and conditional owner deletion; custom backend implementations must supply these operations.

Development

The shared Lua scripts live at the repository root under rawScripts/ and are copied into dotnet/src/BullMQ/Commands/ (embedded into the assembly) and, for the PostgreSQL backend, src/postgres/** is copied into dotnet/src/BullMQ/Postgres/. These copies are generated and git-ignored.

From the repository root:

# Populate the generated script copies (requires `yarn install` once).
yarn copy:lua:dotnet
yarn copy:sql:dotnet

# Build and test.
cd dotnet
dotnet build
dotnet test          # requires a Redis server on localhost:6379

Or just run the helper, which copies the shared scripts if needed, sets the default test connections, and runs the suite (any arguments are passed straight through to dotnet test):

cd dotnet
./scripts/test.sh                      # whole suite (Redis + PostgreSQL)
./scripts/test.sh --filter Name~Flow   # a subset

Set BULLMQ_TEST_REDIS and/or BULLMQ_TEST_POSTGRES to point the integration tests at different servers.

License

MIT — see LICENSE.

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.3.0 54 11/4/2026
1.2.3 116 10/28/2026
1.2.2 247 10/14/2026
1.2.1 139 9/18/2026
1.2.0 117 9/12/2026
1.1.1 137 8/25/2026
1.1.0 271 8/12/2026
1.0.0 873 8/5/2026