Skip to content

Migrate from BullMQ to Bunqueue: Step-by-Step Guide

This guide helps you migrate from BullMQ to bunqueue with minimal code changes.

bunqueue provides a BullMQ-compatible API, making migration straightforward for most use cases.

BullMQbunqueueNotes
new Queue()new Queue()✅ Same API
new Worker()new Worker()✅ Same API
QueueEventsQueueEvents✅ Same API
FlowProducerFlowProducer✅ Same API
jobId deduplicationjobId deduplication✅ Same behavior (idempotent)
Redis connectionNot needed✅ Simpler
Terminal window
# Remove BullMQ and Redis
bun remove bullmq ioredis
# Install bunqueue
bun add bunqueue
// Before (BullMQ)
import { Queue, Worker, QueueEvents } from 'bullmq';
import Redis from 'ioredis';
const connection = new Redis();
const queue = new Queue('my-queue', { connection });
// After (bunqueue)
import { Queue, Worker, QueueEvents } from 'bunqueue/client';
const queue = new Queue('my-queue', { embedded: true });
// No connection needed - uses in-process SQLite!
// Before (BullMQ)
const queue = new Queue('emails', {
connection: {
host: 'localhost',
port: 6379,
password: 'secret',
},
defaultJobOptions: {
attempts: 3,
backoff: { type: 'exponential', delay: 1000 },
},
});
// After (bunqueue)
const queue = new Queue('emails', {
embedded: true,
defaultJobOptions: {
attempts: 3,
backoff: 1000, // Base delay for exponential backoff
},
});
// Before (BullMQ)
const worker = new Worker('emails', async (job) => {
await sendEmail(job.data);
return { sent: true };
}, {
connection,
concurrency: 5,
limiter: { max: 100, duration: 1000 },
});
// After (bunqueue)
const worker = new Worker('emails', async (job) => {
await sendEmail(job.data);
return { sent: true };
}, {
embedded: true,
concurrency: 5,
// Rate limiting is set on queue via server mode, not worker
});
// Rate limiting is configured via CLI or TCP server
// bunqueue rate-limit set emails 100

Events work the same way:

// Same in both BullMQ and bunqueue
worker.on('completed', (job, result) => {
console.log(`Job ${job.id} completed`);
});
worker.on('failed', (job, err) => {
console.error(`Job ${job.id} failed:`, err.message);
});
worker.on('progress', (job, progress) => {
console.log(`Job ${job.id}: ${progress}%`);
});
// Before (BullMQ)
await queue.add('task', data, {
priority: 1,
delay: 5000,
attempts: 3,
backoff: { type: 'exponential', delay: 1000 },
removeOnComplete: true,
removeOnFail: false,
jobId: 'custom-id',
});
// After (bunqueue) - Almost identical
// Queue created with: new Queue('tasks', { embedded: true })
await queue.add('task', data, {
priority: 1,
delay: 5000,
attempts: 3,
backoff: 1000, // Base delay (exponential: 1s, 2s, 4s, 8s...)
removeOnComplete: true,
removeOnFail: false,
jobId: 'custom-id',
});
// BullMQ supports both types
backoff: { type: 'exponential', delay: 1000 }
backoff: { type: 'fixed', delay: 5000 }
// bunqueue supports both fixed and exponential backoff
backoff: { type: 'exponential', delay: 1000 } // Same as BullMQ
backoff: { type: 'fixed', delay: 5000 } // Same as BullMQ
backoff: 1000 // Shorthand: base delay with exponential backoff
// BullMQ (on worker)
new Worker('queue', processor, {
limiter: { max: 100, duration: 1000 }
});
// bunqueue (server mode only - via CLI or TCP)
// Rate limiting is not available in embedded mode
bunqueue rate-limit set my-queue 100
// BullMQ sandboxed processors
new Worker('queue', './processor.js', { connection });
// bunqueue — recommended: inline Worker (production-ready)
import { Worker } from 'bunqueue/client';
const worker = new Worker('queue', async (job) => {
// same logic from your processor.js
return result;
}, { embedded: true, concurrency: 4 });
// bunqueue — alternative: SandboxedWorker (experimental, Bun Workers)
import { SandboxedWorker } from 'bunqueue/client';
const worker = new SandboxedWorker('queue', {
processor: './processor.ts',
concurrency: 4,
timeout: 30000,
});
worker.start();
// BullMQ
await queue.add('task', data, {
repeat: { cron: '0 * * * *' }
});
// bunqueue (queue created with embedded: true)
await queue.add('task', data, {
repeat: { pattern: '0 * * * *' }
});
// Or use interval
await queue.add('task', data, {
repeat: { every: 3600000 }
});
FeatureBullMQbunqueueNotes
Sandboxed processorsUse SandboxedWorker (experimental — Bun Workers)
Redis ClusterSingle instance
Redis StreamsSQLite storage
Rate limit per workerQueue-level rate limit
  • Remove bullmq and ioredis packages
  • Install bunqueue
  • Update imports to bunqueue/client
  • Remove all Redis connection configuration
  • Update backoff configuration (simplified)
  • Move rate limiting from worker to queue
  • Update sandboxed processors to use SandboxedWorker
  • Update repeat config (cronpattern)
  • Test all job processing
  • Remove Redis server from infrastructure

If you encounter issues during migration: