npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

bunqueue

v2.9.5

Published

High-performance job queue for Bun and AI agents. Memory or one-file SQLite, optional PostgreSQL 15–18 multi-broker persistence, cron, retries, DLQ, and MCP.

Readme


Quickstart

bun add bunqueue
import { Bunqueue } from 'bunqueue/client';

const app = new Bunqueue('emails', {
  embedded: true,
  dataPath: './data/emails.db', // omit to run in-memory (lost on restart)
  processor: async (job) => {
    console.log(`Sending to ${job.data.to}`);
    return { sent: true };
  },
});

await app.add('send', { to: '[email protected]' });

That's it. Queue + Worker in one object, persisted to a single SQLite file. No Redis, no config, no setup. msgpackr is the only runtime dependency; cron, SQLite, S3, HTTP and WebSocket use Bun's built-ins.

Not on Bun? Run the server, connect from anywhere

The queue also runs as a standalone server. Memory is the zero-configuration default; SQLite is the zero-infrastructure persistent option:

# in-memory without --data-path; pass it to persist jobs to SQLite
bunx bunqueue start --data-path ./data/bunq.db   # TCP :6789, HTTP :6790

# or, with no runtime at all (the volume persists /app/data):
docker run -d -p 6789:6789 -p 6790:6790 \
  -v bunqueue-data:/app/data \
  egeominotti/bunqueue:latest

For multiple active brokers, the repository includes a topology pinned to PostgreSQL 18.6:

POSTGRES_PASSWORD='replace-me' \
  BUNQUEUE_POSTGRES_URL='postgres://bunqueue:replace-me@postgres:5432/bunqueue' \
  docker compose -f docker-compose.postgres.yml up --build -d

It starts two brokers against one database/namespace. PostgreSQL is server-only; embedded mode keeps using memory/SQLite. Supply both Compose values when the password changes, percent-encoding reserved characters in the URL only. MySQL is not supported. CI validates PostgreSQL 15, 16, 17, and the pinned/recommended 18.6 release. See the storage guide.

Starting with 2.9.5, completed releases publish to both Docker Hub (egeominotti/bunqueue) and GHCR (ghcr.io/egeominotti/bunqueue), with matching version, latest, commit SHA, and timestamp tags. Each image supports linux/amd64 and linux/arm64; Docker selects the matching architecture. Pin a version or digest for reproducible deployments. Confirm a tag exists with docker buildx imagetools inspect egeominotti/bunqueue:<tag> before using it.

Choose a Linux distribution with the same tags on either registry:

| Variant | Version tag | Moving tag | Runtime base | |---|---|---|---| | Alpine (default) | 2.9.5-alpine | alpine, latest | Alpine 3.22, musl | | Debian | 2.9.5-debian | debian | Debian 13 | | Debian slim | 2.9.5-slim | slim | Debian 13 slim | | Distroless | 2.9.5-distroless | distroless | Debian 13, no shell or package manager |

Every variant supports both architectures, runs as UID/GID 1001:1001, and stores SQLite data in /app/data. Unsuffixed tags such as 2.9.5 stay on Alpine. Production images contain the compiled server and required system libraries; development dependencies and a separate Bun installation stay out of the image. The built-in health check uses /app/bunqueue healthcheck, including on distroless. See the deployment guide for custom probes.

Prefer a standalone executable? GitHub releases include the Bun runtime, so no Bun or Node.js installation is needed. From 2.9.5, the eight downloads are:

| System | Architecture | Archive | |---|---|---| | Linux (glibc) | x64 | bunqueue-linux-x64.tar.gz | | Linux (glibc) | arm64 | bunqueue-linux-arm64.tar.gz | | Linux (musl / Alpine) | x64 | bunqueue-linux-x64-musl.tar.gz | | Linux (musl / Alpine) | arm64 | bunqueue-linux-arm64-musl.tar.gz | | macOS | x64 / Intel | bunqueue-darwin-x64.tar.gz | | macOS | arm64 / Apple Silicon | bunqueue-darwin-arm64.tar.gz | | Windows | x64 | bunqueue-windows-x64.zip | | Windows | arm64 | bunqueue-windows-arm64.zip |

Extract the archive for your operating system and architecture, and verify it against the release's SHA256SUMS. See the installation guide.

Then produce and process from the language you already use:

npm install bunqueue-client    # Node.js ≥ 20, Deno ≥ 2, Bun, Cloudflare Workers
import { Queue, Worker } from 'bunqueue-client';

const queue = new Queue('emails'); // localhost:6789 by default
await queue.add('welcome', { to: '[email protected]' });

new Worker('emails', async (job) => ({ sent: true }), { concurrency: 10 });

Python, PHP, Go, Rust and Elixir clients speak the same protocol — see One Queue, Any Language.

Only the server and embedded mode are Bun-only (bun >= 1.4.0, bun.sh); producers and workers can run anywhere.

Quick Start guide →

Why bunqueue?

| Library | Requires | AI-native | | ------------ | ------------------------------------------ | --------- | | BullMQ | Redis | No | | Agenda | MongoDB | No | | pg-boss | PostgreSQL | No | | bunqueue | Nothing (memory/SQLite) · PostgreSQL optional | Yes |

  • Zero external infrastructure by default — memory by default; one SQLite file when local persistence is configured
  • PostgreSQL 15–18 multi-broker mode — PostgreSQL 18.6 is recommended; database-authoritative claims, fenced leases, shared limits, cron, workers, job-state/lifecycle metrics, and failover state (tested three-broker Docker example)
  • BullMQ and BullMQ Pro-compatible APIQueue, Worker, QueueEvents, FlowProducer, plus QueuePro/WorkerPro aliases, fair job groups, native processor batches, cooperative cancellation, and Observable results; migrating takes minutes
  • MCP server included — 73 tools; AI agents get full queue control out of the box
  • Everything server-side — retries with backoff, priorities, cron, rate limits, dead letter queue
  • Measured, operation-specific performance — 729K jobs/sec internal in-memory batch push, 186K jobs/sec public on-disk Embedded addBulk, and 159K jobs/sec TCP PUSHB; methodology and distributions

Great for: embedded and single-server deployments, PostgreSQL-backed broker fleets, AI agents that need a scheduler, edge/serverless spooling, and teams that don't want to operate Redis.

Not ideal for: multi-region consensus or deployments that require MySQL as the queue store. If you already run Redis and BullMQ works for you, keep it.

When to choose bunqueue →

Two Modes

| | Embedded | Server (TCP) | | ---------------- | -------------------------------------------------------------- | -------------------------------------------------------- | | How it works | Queue runs inside your process | Standalone server, clients connect via TCP | | Setup | bun add bunqueue | docker run or bunqueue start | | Performance | 186K jobs/sec on-disk addBulk; 729K internal in-memory batch | SQLite: 159K TCP PUSHB; 17K jobs/sec worker drain | | Best for | Single-process apps, CLIs, serverless | Multiple workers, separate producer/consumer | | Scaling | Same process only | Multiple clients; multiple brokers with PostgreSQL 15–18 |

Embedded

Everything in your process. Without a data path the queue is in-memory: pass dataPath (or set BUNQUEUE_DATA_PATH) to persist jobs.

import { Queue, Worker } from 'bunqueue/client';

const queue = new Queue('emails', { embedded: true, dataPath: './data/app.db' });

const worker = new Worker(
  'emails',
  async (job) => {
    return { sent: true };
  },
  { embedded: true }
);

await queue.add('welcome', { to: '[email protected]' });

Server (TCP)

docker run -d -p 6789:6789 -p 6790:6790 \
  -v bunqueue-data:/app/data \
  egeominotti/bunqueue:latest
import { Queue, Worker } from 'bunqueue/client';

const queue = new Queue('tasks', { connection: { host: 'localhost', port: 6789 } });

const worker = new Worker(
  'tasks',
  async (job) => {
    return { done: true };
  },
  { connection: { host: 'localhost', port: 6789 } }
);

await queue.add('process', { data: 'hello' });

Running the server → · Deployment guide →

BullMQ Pro-compatible groups and batches

The Bun client exposes Pro-style aliases backed by the same native queue and worker implementations. No separate runtime or license is required:

import { QueuePro, WorkerPro } from 'bunqueue/client';

const queue = new QueuePro('webhooks', { embedded: true, dataPath: './data/app.db' });

await queue.add('deliver', payload, {
  group: { id: 'tenant-a', priority: 1, maxSize: 10_000 },
});

const worker = new WorkerPro(
  'webhooks',
  async (job, { signal } = { signal: new AbortController().signal }) => {
    const batch = job.getBatch?.() ?? [job];
    return await deliver(batch, signal);
  },
  {
    embedded: true,
    batch: { size: 100, minSize: 10, timeout: 250, groupAffinity: true },
    group: { concurrency: 2, limit: { max: 20, duration: 1000 } },
  }
);

Groups support atomic maxSize admission, priority within a group, pause and resume, manual rate limits, per-group concurrency/rate defaults, pending-job queries, and fair round-robin claims. Native batches support minimum size, timeout, group affinity, and selective job.setAsFailed(error). Processor timeouts and explicit cancellation abort the second-argument AbortSignal; Promise handlers remain cooperative, while structural Observable handlers are unsubscribed. These contracts are identical with SQLite and PostgreSQL 15–18. BullMQ Pro telemetry and NestJS integration are intentionally not included.

Job Groups guide → · Worker options →

One Queue, Any Language (SDKs)

The server does all the heavy lifting. Official client SDKs share the protocol-conformant core Queue, Worker, and Flow surface, so producers and workers can live anywhere in your stack — add a job from TypeScript, process it from Python. Language-specific capabilities are tracked in the SDK guide.

| Where your code runs | Install | | --------------------------------------------------- | -------------------------------------------------------------------------------------- | | Node.js ≥ 20, Deno ≥ 2, Bun, Cloudflare Workers | npm install bunqueue-client | | Python ≥ 3.9 | pip install bunqueue-client | | PHP ≥ 8.1 | composer require bunqueue/client | | Go ≥ 1.26.5 | go get github.com/egeominotti/bunqueue/sdk/go | | Rust ≥ 1.85 | cargo add bunqueue-client | | Elixir ≥ 1.15 | Hex coming soon — today: use sdk/elixir as a path dependency |

// Node.js / Deno / Cloudflare Workers
import { Queue, Worker } from 'bunqueue-client';

const queue = new Queue('emails', { host: 'localhost', port: 6789 });
await queue.add('welcome', { to: '[email protected]' });

new Worker('emails', async (job) => ({ sent: true }), { concurrency: 10 });
# Python
from bunqueue import Queue, Worker

queue = Queue("emails", host="localhost", port=6789)
queue.add("welcome", {"to": "[email protected]"})

Worker("emails", lambda job: {"sent": True}, concurrency=10).run()

Every SDK is certified against the same public wire protocol and conformance suite.

Atomic flows, in every SDK

Every official FlowProducer resolves all job IDs and reciprocal dependency edges locally, then sends one PUSHF command. The broker validates the complete graph and commits it atomically, so a worker cannot observe a leaf from a partially-created flow.

import { FlowProducer } from 'bunqueue-client';

const flows = new FlowProducer({ host: 'localhost', port: 6789 });
const root = await flows.add({
  name: 'publish-release',
  queueName: 'release',
  data: { version: 'candidate-42' },
  children: [
    { name: 'unit-tests', queueName: 'checks', data: { suite: 'unit' } },
    { name: 'sdk-tests', queueName: 'checks', data: { suite: 'sdk' } },
  ],
});

console.log(
  root.job.id,
  root.children?.map(({ job }) => job.id)
);
await flows.close();

The repository records the contracts and the test strategy beside each implementation:

| SDK | Runtime invariants | Generated tests | Mutation engine | | ---------------------------------------- | ------------------------------------------ | --------------- | --------------- | | TypeScript | contract | fast-check | none¹ | | Python | contract | Hypothesis | mutmut | | PHP | contract | Eris | Infection | | Go | contract | Rapid | Gremlins | | Rust | contract | proptest | cargo-mutants | | Elixir | contract | StreamData | Muex |

¹ The TypeScript SDK has no mutation engine. StrykerJS was removed because its dependency graph produced every advisory the weekly audit had to answer for, none of it reachable from the published client; the planners keep their fast-check coverage.

Property campaigns run in the ordinary SDK gate with deterministic replay seeds. Mutation campaigns run separately against the pure planners and snapshot validators. Contributors can reproduce the complete isolated SDK gate with bun run test:sandbox:sdk; language-specific commands live in each SDK README and AGENTS.md.

SDK guide (all six languages) →

Simple Mode

Bunqueue bundles Queue + Worker + routes + middleware + cron in one object:

import { Bunqueue } from 'bunqueue/client';

const app = new Bunqueue('notifications', {
  embedded: true,
  routes: {
    'send-email': async (job) => ({ sent: true }),
    'send-sms': async (job) => ({ sent: true }),
  },
  concurrency: 10,
  retry: { maxAttempts: 5, strategy: 'jitter' },
  circuitBreaker: { threshold: 5, resetTimeout: 30000 },
});

// Onion middleware around every job
app.use(async (job, next) => {
  const start = Date.now();
  const result = await next();
  console.log(`${job.name}: ${Date.now() - start}ms`);
  return result;
});

await app.cron('daily-report', '0 9 * * *', { type: 'summary' });
await app.add('send-email', { to: '[email protected]' });

app.on('completed', (job, result) => console.log(result));
await app.close();

Also included: batch processing, event triggers (job A completes → create job B), job TTL, priority aging, deduplication, per-group rate limiting, DLQ with auto-retry, graceful cancellation via AbortController.

Simple Mode reference →

Workflow Engine

Multi-step orchestration with saga compensation, branching, parallel steps and human-in-the-loop signals — built on bunqueue, no new infrastructure:

import { Workflow, Engine } from 'bunqueue/workflow';

const orderFlow = new Workflow('order-pipeline')
  .step(
    'reserve-stock',
    async () => {
      await inventory.reserve();
      return { reserved: true };
    },
    {
      compensate: async () => await inventory.release(), // auto-rollback on failure
    }
  )
  .step(
    'charge',
    async () => {
      return { txId: await payments.charge() };
    },
    {
      compensate: async () => await payments.refund(),
    }
  )
  .waitFor('manager-approval', { timeout: 86_400_000 }) // human-in-the-loop
  .step('confirm', async (ctx) => {
    return { txId: (ctx.steps['charge'] as { txId: string }).txId };
  });

const engine = new Engine({ embedded: true });
engine.register(orderFlow);
const run = await engine.start('order-pipeline', { orderId: 'ORD-1' });
await engine.signal(run.id, 'manager-approval', { approved: true });

| | bunqueue | Temporal | Inngest | Trigger.dev | | --------------------- | --------------- | ----------------------- | --------------------- | ----------------------- | | Infrastructure | None (embedded) | PostgreSQL + 7 services | Cloud-only | Redis + PostgreSQL | | Saga compensation | Built-in | Manual | Manual | Manual | | Human-in-the-loop | .waitFor() | Signals API | step.waitForEvent() | Waitpoint tokens | | Self-hosted | Zero-config | Complex | No | Complex | | Pricing | Free (MIT) | Free / Cloud $$ | Per-execution | Free tier, then $50/mo+ |

Also included: nested workflows, doUntil/doWhile loops, forEach over dynamic lists, schema validation (Zod, ArkType, Valibot or any .parse()), step timeouts, typed events, SQLite-persisted execution state.

Workflow Engine guide →

Built for AI Agents (MCP Server)

bunqueue ships a native MCP server: 73 tools, 5 resources, 3 prompts. Agents schedule cron jobs, push and process jobs, retry failures, set rate limits, and read stats — no glue code. HTTP handlers let an agent register a URL and have an embedded worker call it for every job.

bun add bunqueue @modelcontextprotocol/sdk   # the MCP SDK is an optional peer
claude mcp add bunqueue -- bunx --package=bunqueue bunqueue-mcp
// Claude Desktop / Cursor / Windsurf
{
  "mcpServers": {
    "bunqueue": {
      "command": "bunx",
      "args": ["--package=bunqueue", "bunqueue-mcp"]
    }
  }
}

Then just ask: "Schedule a cleanup job every day at 3 AM" · "Show me all failed jobs and retry them" · "Set rate limit to 50/sec on api-calls".

MCP guide →

Dashboard

A web dashboard that fully drives your server — queues, jobs, DLQ, cron, webhooks, workers, live activity, SQLite inspector and an AI copilot. Open source, currently in beta:

bunx bunqueue-dashboard

https://github.com/user-attachments/assets/e8a8d38e-b4a6-4dc8-8360-876c0f24d116

Live demo · User guide · GitHub

Performance

Native Ryzen 9 9950X3D, Bun 1.3.14; medians from repeated fresh processes:

| Workload | Mode | Median | Persistence | | ------------------------------------ | -------------- | --------------------------: | ------------------------------- | | Internal batched push, 1M jobs | Embedded | 729,395 jobs/sec | In-memory, no dataPath | | Public sustained addBulk, 50K cell | Embedded | 186,384 jobs/sec | On-disk buffered SQLite | | PUSHB, fresh 50K sample | TCP | 158,779 jobs/sec | On-disk buffered SQLite | | No-work worker drain, concurrency 50 | TCP | 17,256 jobs/sec | Full pull/process/ACK | | Linear Workflow Engine | Embedded / TCP | 2,700 / 3,187 workflows/sec | Workflow SQLite + 3 queue nodes |

These operations do different work; the internal in-memory result is not an SQLite or public-API claim. Run bun run bench, bun run bench:tcp, or bun run bench:workflow on your hardware. Benchmark methodology → · full engineering report

Documentation

bunqueue.dev →

License

MIT