DEV Community

Atlas Whoff
Atlas Whoff

Posted on

Redis Streams vs BullMQ: Which Job Queue Actually Fits Your AI SaaS in 2026

You're building an AI SaaS. Users submit requests — generate a report, process a document, run an agent task. These take 10-60 seconds. You need a job queue.

Two options dominate: BullMQ (opinionated, high-level) and Redis Streams (primitive, flexible). The wrong choice costs you weeks of rearchitecting.

The Core Difference

BullMQ is a job queue library built on Redis. It handles scheduling, retries, concurrency, priorities, and rate limiting out of the box. You think in jobs and queues.

Redis Streams is a Redis data structure — an append-only log with consumer groups. It's lower-level. You build queue semantics on top of it. You think in messages and consumers.

When BullMQ Wins

BullMQ is the right choice when you need job priorities, cron scheduling, retry logic, or a UI dashboard.

import { Queue, Worker } from 'bullmq';

const queue = new Queue('ai-tasks', { connection: redis });

// High-priority user request
await queue.add('generate', { prompt, userId }, { priority: 1 });

// Cron job
await queue.add('daily-report', {}, {
  repeat: { pattern: '0 6 * * *', tz: 'America/Denver' }
});

// Exponential backoff
await queue.add('claude-completion', { prompt }, {
  attempts: 5,
  backoff: { type: 'exponential', delay: 2000 },
});
Enter fullscreen mode Exit fullscreen mode

Bull Board gives you a free UI dashboard — Redis Streams have no scheduling primitive.

When Redis Streams Win

Streams are better when multiple independent services consume the same event:

# Three consumer groups, one stream
XGROUP CREATE agent-events analytics $ MKSTREAM
XGROUP CREATE agent-events billing $
XGROUP CREATE agent-events audit $
Enter fullscreen mode Exit fullscreen mode

At-least-once processing with acknowledgment:

const messages = await redis.xreadgroup(
  'GROUP', 'analytics', 'consumer-1',
  'COUNT', 10, 'BLOCK', 5000,
  'STREAMS', 'agent-events', '>'
);

// Only acknowledge after confirmed processing
await redis.xack('agent-events', 'analytics', messageId);

// On restart: pending messages replay automatically
Enter fullscreen mode Exit fullscreen mode

Message replay is also native — BullMQ jobs are consumed and removed:

# Replay from beginning for a new consumer
XREAD COUNT 10000 STREAMS agent-events 0-0
Enter fullscreen mode Exit fullscreen mode

The Hybrid Pattern (What Most AI SaaS Actually Uses)

User Request
    ↓
BullMQ Queue     ← scheduling, priority, retry, rate limiting
    ↓
Worker (Claude call)
    ↓
Redis Stream     ← fan-out to analytics, billing, audit, notifications
Enter fullscreen mode Exit fullscreen mode
const worker = new Worker('ai-tasks', async (job) => {
  const result = await runClaudeTask(job.data);

  // Publish result to stream for downstream consumers
  await redis.xadd('task-completed', '*',
    'jobId', job.id,
    'userId', job.data.userId,
    'tokens', String(result.usage.total_tokens),
    'cost', String(estimateCost(result.usage))
  );

  return result;
}, { connection: redis });
Enter fullscreen mode Exit fullscreen mode

The billing service reads task-completed to charge per-token. Analytics reads the same stream for dashboards. Neither is coupled to BullMQ internals.

Decision Matrix

Need BullMQ Redis Streams
Job scheduling/cron yes no
Job priorities yes manual
Retry with backoff yes manual
UI dashboard yes manual
Multi-consumer fan-out manual yes
Message replay no yes
High throughput (>10k/s) limited yes

Recommendation

Start with BullMQ pre-PMF. Ergonomics are better, dashboard is free, you don't have time to build retry logic from scratch.

Add Redis Streams when multiple downstream systems (billing, analytics, notifications) need to react to the same events.

Migrate core queue to Streams only if you hit throughput limits. Most AI SaaS products never reach this point.


Building AI SaaS infrastructure from scratch? The AI SaaS Starter Kit ships with BullMQ + Redis Streams wired together — queue processing, event fan-out, and per-user rate limiting included.

Top comments (0)