BullMQ vs Quirrel vs pgBoss (2026)
Background jobs handle email sending, image processing, webhooks, and scheduled tasks. Here's how the three main Node.js job queue libraries compare.
Quick Comparison
| Feature | BullMQ | Quirrel | pgBoss |
|---|---|---|---|
| Backend | Redis | Hosted/self-hosted | PostgreSQL |
| Language | TypeScript | TypeScript | TypeScript |
| Scheduling | ✅ Cron, delayed | ✅ Cron, delayed | ✅ Cron, delayed |
| Retries | ✅ Configurable | ✅ Configurable | ✅ Configurable |
| Priority | ✅ | ❌ | ✅ |
| Rate Limiting | ✅ | ❌ | ❌ |
| Dashboard | Bull Board | Built-in | ❌ |
| Extra Infra | Redis required | Optional (hosted) | Uses your Postgres |
| Best For | High-throughput | Serverless/Next.js | Postgres-only stacks |
BullMQ — The Industry Standard
Best for: High-throughput applications that already use Redis.
import { Queue, Worker } from 'bullmq'
const connection = { host: 'localhost', port: 6379 }
// Create a queue
const emailQueue = new Queue('emails', { connection })
// Add a job
await emailQueue.add('welcome', {
to: 'user@example.com',
subject: 'Welcome!',
})
// Add a delayed job (send in 1 hour)
await emailQueue.add('reminder', { to: 'user@example.com' }, {
delay: 60 * 60 * 1000,
})
// Add a recurring job (every day at 9am)
await emailQueue.add('daily-digest', {}, {
repeat: { pattern: '0 9 * * *' },
})
// Process jobs
const worker = new Worker('emails', async (job) => {
await sendEmail(job.data)
}, { connection, concurrency: 5 })
worker.on('completed', (job) => console.log(`Done: ${job.id}`))
worker.on('failed', (job, err) => console.error(`Failed: ${err.message}`))
Pros:
- Most battle-tested (millions of jobs/day in production)
- Rate limiting, priority queues, job dependencies
- Bull Board dashboard for monitoring
- Excellent TypeScript support
- Large community and ecosystem
Cons:
- Requires Redis (extra infrastructure)
- More complex setup than alternatives
- Redis costs on cloud providers
Quirrel — The Serverless Queue
Best for: Next.js and serverless apps that don't want to manage Redis.
// app/api/queues/email/route.ts
import { Queue } from 'quirrel/next'
export const emailQueue = Queue('api/queues/email', async (job) => {
await sendEmail(job.to, job.subject, job.body)
})
// Enqueue from anywhere
await emailQueue.enqueue(
{ to: 'user@example.com', subject: 'Welcome!' },
{ delay: '1h' }
)
// Recurring jobs
await emailQueue.enqueue(
{ type: 'daily-digest' },
{ repeat: { cron: '0 9 * * *' } }
)
Pros:
- Zero infrastructure (hosted version)
- Built for serverless (Next.js, Vercel)
- Simple API
- Built-in dashboard
Cons:
- Smaller community
- Less feature-rich than BullMQ
- Hosted service dependency (or self-host)
- No priority queues or rate limiting
pgBoss — The PostgreSQL Queue
Best for: Apps already using PostgreSQL that don't want to add Redis.
import PgBoss from 'pg-boss'
const boss = new PgBoss(process.env.DATABASE_URL)
await boss.start()
// Add a job
await boss.send('emails', {
to: 'user@example.com',
subject: 'Welcome!',
})
// Add a scheduled job
await boss.schedule('daily-report', '0 9 * * *')
// Process jobs
await boss.work('emails', { teamConcurrency: 5 }, async (job) => {
await sendEmail(job.data)
})
Pros:
- No extra infrastructure (uses your existing Postgres)
- ACID-compliant (transactional job creation)
- Simple setup
- Job archival and monitoring built-in
Cons:
- Slower than Redis-backed queues at high volume
- Less battle-tested at massive scale
- Adds load to your database
- Fewer features than BullMQ
Also Consider: Inngest & Trigger.dev
These managed platforms handle background jobs without self-managed queues:
Inngest: Event-driven functions with built-in retries, scheduling, and step functions. No queue infrastructure needed.
Trigger.dev: Similar to Inngest but with a focus on long-running tasks and integrations.
Both are excellent if you don't want to manage queue infrastructure at all.
Decision Framework
Already have Redis → BullMQ
Serverless / Next.js → Quirrel or Inngest
PostgreSQL only (no Redis) → pgBoss
Don't want to manage queues → Inngest or Trigger.dev
High throughput (>10K jobs/min) → BullMQ
FAQ
Do I need a job queue?
If you're sending emails, processing images, syncing data, or running scheduled tasks — yes. Don't do these in your API request handler.
Can PostgreSQL handle job queues?
Yes, for moderate volume (thousands of jobs/hour). For high throughput (millions/day), Redis-backed BullMQ is more appropriate.
BullMQ vs Inngest?
BullMQ is self-managed (you run Redis and workers). Inngest is managed (they handle the infrastructure). BullMQ for control, Inngest for simplicity.
Bottom Line
BullMQ is the default for Node.js job queues — battle-tested and feature-rich. pgBoss if you want to avoid Redis. Quirrel for serverless. Inngest/Trigger.dev if you don't want to manage queue infrastructure. Start with what matches your existing infrastructure.