ジョブキュー比較:BullMQ vs Agenda vs pg-boss でバックグラウンドタスクを管理する
オープンソースラボ編集部 ・ 2026年6月14日
ジョブキュー比較:BullMQ vs Agenda vs pg-boss でバックグラウンドタスクを管理する
メール送信・画像リサイズ・バッチ処理・定期実行など時間のかかる処理をバックグラウンドで非同期実行するジョブキューはWebアプリの基盤インフラです。BullMQ(Redis製・高速・豊富な機能)・Agenda(MongoDB製・cronスケジュール)・pg-boss(PostgreSQL製・追加インフラ不要)の3つが2026年のOSSジョブキューデファクトスタンダードです。
ジョブキューを使う理由
- UXの向上: メール送信・PDFレポート生成などをAPIレスポンスから切り離してユーザーを待たせない
- 信頼性: ジョブを永続化してサーバー再起動・クラッシュ時のタスク消失を防止してリトライを自動化
- スケーリング: ワーカーを独立したプロセス・コンテナとして水平スケールしてスループットを向上
- スケジュール実行: cronライクな定期実行(毎日レポート生成・週次バッチ集計)を管理
主要ツールの概要
BullMQ
2021年公開、TypeScript製のOSSです。GitHubスター6k+(Bullから継続)。Redisを使った高速・高機能なNode.jsジョブキューで、優先度キュー・遅延ジョブ・フロー(ジョブの依存関係グラフ)・リピーティングジョブ・ダッシュボード(Bull Board)が充実しています。
// BullMQ: Node.js + TypeScriptでのジョブキュー実装
import { Queue, Worker, QueueEvents, FlowProducer } from 'bullmq'
import { Redis } from 'ioredis'
const connection = new Redis({
host: process.env.REDIS_HOST || 'localhost',
port: 6379,
maxRetriesPerRequest: null, // BullMQの要件
})
// ジョブタイプの型定義
interface EmailJobData {
to: string
subject: string
body: string
userId: string
}
interface PdfJobData {
reportId: string
userId: string
dateRange: { start: string; end: string }
}
// キューの作成
const emailQueue = new Queue<EmailJobData>('email', {
connection,
defaultJobOptions: {
attempts: 3, // 失敗時に最大3回リトライ
backoff: { type: 'exponential', delay: 2000 }, // 指数バックオフ
removeOnComplete: { count: 1000 }, // 完了ジョブを1000件保持
removeOnFail: { count: 5000 }, // 失敗ジョブを5000件保持
},
})
const pdfQueue = new Queue<PdfJobData>('pdf-generation', { connection })
// ジョブを追加
export async function sendEmailAsync(data: EmailJobData) {
const job = await emailQueue.add('send-email', data, {
priority: data.to.endsWith('@vip.com') ? 1 : 10, // VIPは優先度高
delay: 0,
})
return job.id
}
// 5分後に実行する遅延ジョブ
export async function scheduleReminder(userId: string, message: string) {
await emailQueue.add('reminder', { to: `${userId}@example.com`, subject: 'リマインダー', body: message, userId }, {
delay: 5 * 60 * 1000, // 5分後
})
}
// 毎日深夜0時に実行する繰り返しジョブ
await emailQueue.add(
'daily-digest',
{ to: 'all@example.com', subject: 'デイリーダイジェスト', body: '...', userId: 'system' },
{ repeat: { cron: '0 0 * * *', tz: 'Asia/Tokyo' } }
)
// ワーカー(実際の処理を行う)
const emailWorker = new Worker<EmailJobData>(
'email',
async (job) => {
console.log(`メール送信: ${job.data.to} - ${job.data.subject} (試行 ${job.attemptsMade + 1}回目)`)
await sendEmail({
to: job.data.to,
subject: job.data.subject,
html: job.data.body,
})
// 進捗報告
await job.updateProgress(100)
return { sentAt: new Date().toISOString() }
},
{
connection,
concurrency: 10, // 同時並列処理数
limiter: { max: 100, duration: 60000 }, // レートリミット: 毎分100件
}
)
emailWorker.on('completed', (job, result) => {
console.log(`ジョブ ${job.id} 完了: ${JSON.stringify(result)}`)
})
emailWorker.on('failed', (job, err) => {
console.error(`ジョブ ${job?.id} 失敗:`, err.message)
})
// フロー: ジョブ間の依存関係(PDFを作成してからメール送信)
const flowProducer = new FlowProducer({ connection })
await flowProducer.add({
name: 'send-report-email',
queueName: 'email',
data: { to: 'user@example.com', subject: 'レポートができました', body: '...', userId: '123' },
children: [
{
name: 'generate-pdf',
queueName: 'pdf-generation',
data: { reportId: 'rpt-001', userId: '123', dateRange: { start: '2026-01-01', end: '2026-01-31' } },
},
],
})
// Bull Board: ダッシュボードUI(Express)
import { createBullBoard } from '@bull-board/api'
import { BullMQAdapter } from '@bull-board/api/bullMQAdapter'
import { ExpressAdapter } from '@bull-board/express'
import express from 'express'
const serverAdapter = new ExpressAdapter()
serverAdapter.setBasePath('/admin/queues')
createBullBoard({
queues: [new BullMQAdapter(emailQueue), new BullMQAdapter(pdfQueue)],
serverAdapter,
})
const app = express()
app.use('/admin/queues', serverAdapter.getRouter())
app.listen(3001, () => console.log('Bull Board: http://localhost:3001/admin/queues'))
pg-boss
2017年公開、TypeScript製のOSSです。GitHubスター2k+。PostgreSQLをバックエンドとしてRedis等の追加インフラが不要なジョブキューです。Next.js・Supabaseなど既にPostgreSQLを使っているプロジェクトにゼロインフラ追加でジョブキューを導入できます。
// pg-boss: PostgreSQLだけでジョブキューを実装
import PgBoss from 'pg-boss'
const boss = new PgBoss({
connectionString: process.env.DATABASE_URL,
schema: 'pgboss', // pgbossスキーマにテーブルを作成
retentionDays: 7, // ジョブ履歴を7日保持
})
await boss.start()
// ジョブを送信
interface NotificationJob {
userId: string
message: string
type: 'push' | 'email' | 'sms'
}
await boss.send('send-notification', {
userId: 'user-123',
message: '注文が確定しました',
type: 'push',
} satisfies NotificationJob, {
retryLimit: 3,
retryDelay: 30, // 30秒後にリトライ
expireInHours: 24, // 24時間でタイムアウト
})
// スケジュールジョブ(毎日9時に実行)
await boss.schedule('morning-report', '0 9 * * *', {}, {
tz: 'Asia/Tokyo',
})
// ワーカー
boss.work<NotificationJob>('send-notification', { teamSize: 5, teamConcurrency: 2 }, async (jobs) => {
for (const job of jobs) {
await sendNotification(job.data)
}
})
// Next.js App Router での統合
// app/api/process/route.ts
export async function POST(request: Request) {
const body = await request.json()
const jobId = await boss.send('process-upload', body)
return Response.json({ jobId })
}
Agenda
2013年公開、TypeScript製のOSSです。GitHubスター9k+。MongoDBをバックエンドとするcron風ジョブスケジューラーで、人間が読みやすいスケジュール定義('every 5 minutes'・'in 20 minutes')が特徴です。
// Agenda: MongoDBベースのジョブスケジューラー
import Agenda from 'agenda'
const agenda = new Agenda({ db: { address: process.env.MONGODB_URL!, collection: 'agendaJobs' } })
agenda.define('send weekly report', async (job) => {
const { userId } = job.attrs.data
await generateAndSendReport(userId)
})
await agenda.start()
// 人間が読みやすいスケジュール
await agenda.every('1 week', 'send weekly report', { userId: '123' })
await agenda.schedule('in 30 minutes', 'send welcome email', { email: 'new@user.com' })
await agenda.now('process payment', { orderId: 'ord-001' })
機能比較表
| 比較項目 | BullMQ | pg-boss | Agenda |
|---|---|---|---|
| バックエンド | Redis | PostgreSQL | MongoDB |
| 追加インフラ | Redis必要 | ✅ 不要(PG利用) | MongoDB必要 |
| 優先度キュー | ✅ | ✅ | △ |
| フロー(依存) | ✅ | ❌ | ❌ |
| ダッシュボード | ✅ Bull Board | △ | △ |
| GitHub Stars | 6k+ | 2k+ | 9k+ |
ジョブキューはDevOpsカテゴリ/categories/devopsのRedis・PostgreSQLと組み合わせてNext.jsアプリのバックグラウンド処理を実装します。LLM Toolsカテゴリ/categories/llm-toolsのClaude API・Whisperとの統合で「音声ファイルアップロード→BullMQジョブ→Whisper文字起こし→Claude要約→結果通知」の非同期AIパイプラインを構築できます。
FAQ
Q. BullMQでRedisのダウン時にジョブを失わない対策は?
A. Redis Sentinel(自動フェイルオーバー)またはRedis Cluster(分散)の構成が必要です。Redis Sentinel: new Redis({sentinels: [{host:'sentinel1', port:26379}], name:'mymaster'})でBullMQを接続するとマスター障害時に自動でスタンバイに切り替わります。Redis Cluster: BullMQはRedis Clusterをサポートしておりnew Redis.Cluster([{host:'node1', port:6379}])で接続できます。ジョブのAt-least-once保証: BullMQはRedisのACK(XACK)ベースでジョブ完了を管理し、ワーカークラッシュ時はlockDuration(デフォルト30秒)後に自動的にジョブが「スタック」状態から回収されてリトライします。
Q. BullMQとpg-bossの選択基準は?
A. Redisを既に使っている・高スループット(数千ジョブ/秒)ならBullMQ、PostgreSQLのみ・低〜中スループット・Supabase環境ならpg-bossが向いています。スループット比較: BullMQ(Redis)→数千〜数万ジョブ/秒、pg-boss(PostgreSQL)→数十〜数百ジョブ/秒。pg-bossの追加インフラ不要のメリット: SupabaseプロジェクトはPostgreSQLを既に持っているためpg-bossをインストールするだけでジョブキューが機能し、Redis月額費用が不要。Next.jsでのサーバーレス注意: Vercel等のサーバーレス環境ではワーカープロセスを常駐させられないため、Vercel Cronから/api/workerを定期呼び出してジョブを処理するパターンが必要です。
Q. ジョブの失敗を監視してアラートを送るには?
A. BullMQのWorkerイベント + Slack Webhookでアラート通知を実装します。①worker.on('failed', async (job, err) => { ... })で失敗イベントを受信②失敗回数がjob.attemptsMadeでリトライ上限に達したら(job.attemptsMade >= job.opts.attempts - 1)③Slack Webhookにエラー詳細・ジョブID・入力データを送信④SentryのSentry.captureException(err, {extra: {jobId: job.id, data: job.data}})でエラートラッキングに記録。Bull BoardのUI: ジョブ一覧・失敗ジョブのスタックトレース・手動リトライをWebUIから操作できます。
Q. ジョブキューをDockerで本番運用するには?
A. APIサーバーとワーカーを別コンテナに分離するのがベストプラクティスです。Dockerfile.worker: FROM node:22-alpine→依存インストール→CMD ['node', 'dist/worker.js']。docker-compose.yml: services: api: ..., worker: {image: same-image, command: node dist/worker.js, deploy: {replicas: 3}}でワーカーを3並列で起動。スケーリング: docker-compose scale worker=10で瞬時にワーカー数を増減できます。Kubernetes: HPA(Horizontal Pod Autoscaler)でキューの深さ(BullMQ Prometheusメトリクス)に基づいてワーカーPodsを自動スケールするパターンが本番で使われます。
まとめ
| ユースケース | 推奨ツール |
|---|---|
| Redis環境・高スループット・フロー・UI | BullMQ |
| PostgreSQLのみ・Supabase・追加インフラ不要 | pg-boss |
| MongoDB環境・cron風・人間可読スケジュール | Agenda |