タスクスケジューラOSS比較:BullMQ vs Temporal vs Inngest でバックグラウンドジョブを管理する
オープンソースラボ編集部 ・ 2026年6月13日
タスクスケジューラOSS比較:BullMQ vs Temporal vs Inngest でバックグラウンドジョブを管理する
cronジョブのタイムアウト・失敗時のリトライ・長時間ワークフローの管理に悩んでいませんか?BullMQ・Temporal・Inngestはジョブキュー・ワークフローエンジン・イベント駆動型バックグラウンド処理をOSSで実現します。
バックグラウンドジョブ管理が必要な場面
- 重いAPI処理のオフロード: 画像変換・PDF生成・AI推論など時間のかかる処理をキューに入れてAPIを即座にレスポンスしたい
- 失敗時の自動リトライ: メール送信・外部APIコールが失敗したら指数バックオフで自動再試行したい
- 長時間ワークフロー: 注文→決済→発送→通知の複数ステップを管理して途中失敗でも再開したい
- cronジョブ管理: 毎日0時のバッチ処理を確実に1回だけ実行して失敗時に通知したい
- イベント駆動処理: ユーザー登録・支払い完了などのイベントに反応してバックグラウンド処理を起動したい
主要ツールの概要
BullMQ
Node.js + Redisベースのジョブキューライブラリです。GitHubスター6k+で最も広く使われているNode.jsのジョブキュー。シンプルなキューから優先度付きジョブ・遅延実行・cronジョブ・フロー(DAG)まで対応します。
// BullMQのセットアップ(Next.js APIルート + Worker)
// lib/queue.ts
import { Queue, Worker, QueueEvents } from "bullmq";
import { Redis } from "ioredis";
const connection = new Redis(process.env.REDIS_URL!, {
maxRetriesPerRequest: null,
});
// キューの定義
export const emailQueue = new Queue("email", { connection });
export const imageQueue = new Queue("image-processing", { connection });
// ジョブの追加
export async function scheduleEmail(payload: {
to: string;
subject: string;
body: string;
}) {
await emailQueue.add("send-welcome", payload, {
attempts: 3, // 最大3回リトライ
backoff: {
type: "exponential",
delay: 1000, // 1秒→2秒→4秒のバックオフ
},
removeOnComplete: 100, // 完了済みジョブを100件まで保持
removeOnFail: 200, // 失敗ジョブを200件まで保持
});
}
// cronジョブ(毎日9時にデイリーサマリー送信)
await emailQueue.add(
"daily-summary",
{ type: "daily" },
{
repeat: { cron: "0 9 * * *", tz: "Asia/Tokyo" },
attempts: 3,
}
);
// app/api/send-email/route.ts - APIルートからジョブを追加
import { NextRequest, NextResponse } from "next/server";
import { scheduleEmail } from "@/lib/queue";
export async function POST(req: NextRequest) {
const { to, subject, body } = await req.json();
// ジョブをキューに追加してすぐにレスポンスを返す
await scheduleEmail({ to, subject, body });
return NextResponse.json({ queued: true }, { status: 202 });
}
// workers/email.worker.ts - バックグラウンドワーカー
import { Worker, Job } from "bullmq";
import { Redis } from "ioredis";
import { Resend } from "resend";
const connection = new Redis(process.env.REDIS_URL!);
const resend = new Resend(process.env.RESEND_API_KEY);
const emailWorker = new Worker(
"email",
async (job: Job) => {
const { to, subject, body } = job.data;
console.log(`Processing job ${job.id}: sending to ${to}`);
await resend.emails.send({
from: "no-reply@yoursite.com",
to,
subject,
html: body,
});
console.log(`Job ${job.id} completed`);
return { sent: true, to };
},
{
connection,
concurrency: 10, // 同時処理数
}
);
emailWorker.on("completed", (job) => {
console.log(`Email sent: ${job.id}`);
});
emailWorker.on("failed", (job, err) => {
console.error(`Email failed: ${job?.id}`, err);
});
# BullMQ用Redisのdocker-compose設定
version: "3"
services:
redis:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
- redis_data:/data
command: redis-server --appendonly yes
# BullMQ Boardで管理UIを追加
bull-board:
image: deadly0/bull-board:latest
ports:
- "3001:3000"
environment:
REDIS_HOST: redis
REDIS_PORT: 6379
volumes:
redis_data:
Temporal
Goで書かれた分散ワークフローエンジンです。GitHubスター12k+で、長時間・複数ステップのワークフロー管理に強力です。UberとCadenceチームが作り、Netflixや Stripe等が本番で使っています。ワークフローの状態を自動的に永続化し、サーバー再起動後も正確に再開できます。
// Temporal TypeScript SDK でワークフローを定義
// workflows/order-workflow.ts
import { proxyActivities, sleep, defineSignal, setHandler } from "@temporalio/workflow";
import type * as activities from "../activities/order-activities";
const { processPayment, sendConfirmationEmail, scheduleShipping, notifyWarehouse } =
proxyActivities<typeof activities>({
startToCloseTimeout: "5 minutes",
retry: {
maximumAttempts: 3,
initialInterval: "1s",
backoffCoefficient: 2,
},
});
// シグナル定義(外部からワークフローに信号を送れる)
export const cancelSignal = defineSignal<[reason: string]>("cancel");
export async function orderWorkflow(orderId: string): Promise<string> {
let cancelled = false;
let cancelReason = "";
setHandler(cancelSignal, (reason) => {
cancelled = true;
cancelReason = reason;
});
if (cancelled) {
return `Order ${orderId} cancelled: ${cancelReason}`;
}
// ステップ1: 決済処理
const paymentResult = await processPayment(orderId);
if (!paymentResult.success) {
throw new Error("Payment failed");
}
// ステップ2: 確認メール送信
await sendConfirmationEmail(orderId, paymentResult.transactionId);
// ステップ3: 24時間待機(倉庫処理時間)
await sleep("24 hours");
if (cancelled) {
return `Order ${orderId} cancelled after payment: ${cancelReason}`;
}
// ステップ4: 倉庫に通知
await notifyWarehouse(orderId);
// ステップ5: 配送スケジュール
const trackingNumber = await scheduleShipping(orderId);
return `Order ${orderId} shipped: ${trackingNumber}`;
}
// activities/order-activities.ts - アクティビティ(実際の処理)
export async function processPayment(orderId: string) {
// Stripe等の決済APIを呼ぶ
const result = await stripe.paymentIntents.confirm(orderId);
return { success: result.status === "succeeded", transactionId: result.id };
}
export async function sendConfirmationEmail(orderId: string, transactionId: string) {
await resend.emails.send({
from: "orders@yoursite.com",
to: await getOrderEmail(orderId),
subject: "ご注文を受け付けました",
html: `注文番号: ${orderId}<br>取引ID: ${transactionId}`,
});
}
// ワーカーの起動
// workers/temporal.worker.ts
import { Worker } from "@temporalio/worker";
import * as activities from "./activities/order-activities";
const worker = await Worker.create({
workflowsPath: require.resolve("./workflows/order-workflow"),
activities,
taskQueue: "order-processing",
});
await worker.run();
Inngest
サーバーレス環境(Vercel・Netlify)でバックグラウンドジョブとワークフローを実行できるプラットフォームです。Next.jsとの統合が特に優れており、自社サーバーを持たずにキューとワークフローを使えます。OSSのInngest Dev Serverでローカル開発もできます。
// Inngestのセットアップ(Next.jsと統合)
// lib/inngest.ts
import { Inngest } from "inngest";
export const inngest = new Inngest({ id: "my-app" });
// イベントの型定義
type Events = {
"user/signed-up": { data: { userId: string; email: string } };
"order/placed": { data: { orderId: string; amount: number } };
"article/published": { data: { slug: string; title: string } };
};
export const inngestTyped = new Inngest<Events>({ id: "my-app" });
// functions/onUserSignUp.ts - ユーザー登録時のワークフロー
import { inngestTyped } from "@/lib/inngest";
export const onUserSignUp = inngestTyped.createFunction(
{
id: "on-user-signed-up",
retries: 3,
},
{ event: "user/signed-up" },
async ({ event, step }) => {
const { userId, email } = event.data;
// ステップ1: ウェルカムメール送信
await step.run("send-welcome-email", async () => {
await resend.emails.send({
from: "welcome@yoursite.com",
to: email,
subject: "ようこそ!",
html: "アカウントが作成されました。",
});
});
// ステップ2: 3日後にフォローアップメール
await step.sleep("wait-3-days", "3 days");
const user = await step.run("check-user-activity", async () => {
return await db.users.findUnique({ where: { id: userId } });
});
if (!user?.lastActiveAt) {
await step.run("send-followup-email", async () => {
await resend.emails.send({
from: "support@yoursite.com",
to: email,
subject: "何かお困りですか?",
html: "サポートが必要でしたらご連絡ください。",
});
});
}
}
);
// app/api/inngest/route.ts
import { serve } from "inngest/next";
import { inngest } from "@/lib/inngest";
import { onUserSignUp } from "@/functions/onUserSignUp";
export const { GET, POST, PUT } = serve({
client: inngest,
functions: [onUserSignUp],
});
機能比較表
| 比較項目 | BullMQ | Temporal | Inngest |
|---|---|---|---|
| ライセンス | MIT | MIT | Apache 2.0 |
| インフラ要件 | Redis | Temporal Server + DB | クラウドサービス or Dev Server |
| 実装言語 | TypeScript/Node.js | Go(多言語SDK) | TypeScript/Python/Go |
| ジョブキュー | ✅ | ✅ | ✅ |
| cronジョブ | ✅ | ✅ | ✅ |
| 長時間ワークフロー | ❌ | ✅ 得意 | ✅ |
| ステップ実行・再開 | ❌ | ✅ | ✅ |
| サーバーレス対応 | ❌(Redisが必要) | ❌(サーバー必要) | ✅ Vercel等で動く |
| シグナル・クエリ | ❌ | ✅ | ✅ |
| 管理UI | ✅ Bull Board | ✅ Web UI | ✅ Inngest Cloud |
| セットアップの簡単さ | ★★★★☆ | ★★☆☆☆ | ★★★★★ |
| スケーラビリティ | 中規模 | 大規模 | クラウド依存 |
| GitHub Stars | 6k+ | 12k+ | 4k+ |
バックグラウンドジョブとDevOpsの関連ツールはdevopsカテゴリ(/categories/devops)にまとめています。CI/CDパイプラインとの統合についてはCI/CDセルフホスト比較(/categories/devops)も参照してください。
FAQ
Q. BullMQとBullの違いは何ですか?
A. BullMQはBull(前世代のRedisジョブキュー)の後継ライブラリです。主な違い: ①BullMQはTypeScriptネイティブで型安全、②Redisの最新機能(Redis Streams)を活用して信頼性が向上、③フロー機能(親ジョブ→子ジョブのDAG)が追加、④ワーカーの並行処理モデルが改善されてメモリリークが減少。現時点ではBullMQを使うことを推奨します。BullからBullMQへの移行は比較的簡単で、APIが似ているためコードの変更量が少なく済みます。
Q. TemporalをVercelやサーバーレス環境で使えますか?
A. Temporalはステートフルなサーバー(Temporal Server)が必要なため、純粋なサーバーレス環境では動きません。対策: ①Temporal Cloudを使う(マネージドサービス、$25/月〜)、②Fly.ioやRenderにTemporal Serverをデプロイする(Dockerfile提供あり)、③Vercelで使う場合は「Inngest」や「Trigger.dev」(類似ツール)を検討する。サーバーレスでTemporalライクな体験を求めるならInngestが最も近いです。
Q. BullMQのRedisはどのくらいのリソースが必要ですか?
A. ジョブ量に依存しますが、中小規模のWebアプリなら512MB〜1GBのRedisで十分です。ジョブデータはRedisのメモリに保存されるため、大量のジョブや大きなペイロードは注意が必要です。対策: ①removeOnCompleteとremoveOnFailオプションで完了・失敗ジョブを定期削除する(設定しないとメモリが溢れる)、②ジョブのペイロードは最小限に(大きなデータはS3/Supabase Storageに保存してIDだけキューに入れる)、③Upstash Redis(サーバーレスRedis)を使えばVercel等でも手軽にBullMQが使えます(データ量課金)。
Q. Inngestのセルフホストは可能ですか?
A. Inngest Dev Serverは無料のローカル開発用ツールとして提供されています(npx inngest-cli@latest devで起動)。本番環境ではInngest Cloudを使うのが基本ですが、2024年以降にセルフホスト版(Inngest Community Edition)の提供も進んでいます。セルフホストには PostgreSQL が必要です。InngestはVercelのような外部からWebhookを受信できる環境が前提のため、プライベートネットワーク内のみの環境では動作しません。完全にオンプレミスでクラウドに依存しない構成が必要ならBullMQ + Redisが適切です。
まとめ
| ユースケース | 推奨ツール |
|---|---|
| シンプルなジョブキュー + Redis | BullMQ |
| 長時間ワークフロー + エンタープライズ | Temporal |
| Vercel/Next.js + サーバーレス | Inngest |
| cronジョブ管理 + 可視化UI | BullMQ + Bull Board |