- Published on
Cron とスケジュールタスク 2026 — systemd timers / Vercel Cron / AWS EventBridge Scheduler / k8s CronJobs / BullMQ / Sidekiq / Temporal 徹底ガイド
- Authors

- Name
- Youngju Kim
- @fjvbn20031
プロローグ — 「cron 回しとけばいいでしょ?」が危うくなった時代
2026 年のあるチームの設計レビュー。
新人「毎日 9 時にレポートメールを送りたいんです。cron でいいですよね?」 シニア「どのホストで?監視は?失敗したら?二回走ったら?マシンが落ちたら?」 新人「……あ。」
この短いやり取りに 2026 年のスケジューリングがすべて詰まっている。cron は 50 年間動き続けてきたが、クラウドネイティブ時代に入ってからは「決まった時間にコマンドを実行する」だけでは到底足りない。ホストの停止、二重実行の競合、失敗通知、リトライ、分散ロック、冪等性、タイムゾーン、サマータイム(DST)——すべてが運用上の罠になった。
2026 年現在、スケジュールタスクは大きく 4 つのカテゴリ に分かれる。システムレベル(cron、systemd timers、fcron、anacron)、クラウドマネージド(Vercel Cron、EventBridge Scheduler、Cloudflare Workers、GitHub Actions schedule)、分散バックグラウンドキュー(BullMQ、Sidekiq、Celery Beat)、ワークフローエンジン(Airflow、Dagster、Prefect、Temporal、Inngest、Trigger.dev、Hatchet)。そしてその上に監視(Healthchecks.io、Cronitor.io)が乗る。
本記事ではこの地形を一気に整理する。古典的な cron の 5 フィールドから、systemd timers、Vercel Cron、AWS EventBridge Scheduler(2022 年 11 月に CloudWatch Events Rules を置き換え)、k8s CronJobs、BullMQ、Sidekiq、Celery Beat、Temporal Schedules、Hatchet、Quartz、Hangfire、fcron、anacron、Nomad periodic jobs、監視 SaaS まで。韓国のトス・カカオ、日本のメルカリが何をどう使っているかも見る。
1. 2026 年の cron 地図 — システム / クラウド / キュー / ワークフロー
まず全体像。「スケジュールタスク」という一語の下に、まったく異なる道具が並んでいる。
| カテゴリ | 代表的なツール | 適したケース |
|---|---|---|
| システム cron | crond、systemd timers、fcron、anacron | 単一ホストのバックグラウンド整理、ログローテーション |
| クラウドマネージド | Vercel Cron、AWS EventBridge Scheduler、Cloudflare Workers Cron Triggers、GitHub Actions schedule、GitLab schedule pipelines | サーバーレス、CI/CD、インフラ自動化 |
| 分散バックグラウンドキュー | BullMQ、Sidekiq、Celery Beat、RQ、dramatiq、APScheduler、Quartz、Spring @Scheduled、Hangfire | アプリ内のジョブキュー + スケジュール |
| ワークフローエンジン | Airflow、Dagster、Prefect、Temporal Schedules、Inngest Crons、Trigger.dev v3、Hatchet | 多段階データパイプライン、信頼性重視のワークフロー |
| コンテナ | k8s CronJobs、Nomad periodic jobs | コンテナベースのバッチ |
| 監視 | Healthchecks.io、Cronitor.io | デッドマンスイッチ、未受信アラート |
2026 年に共有されているベストプラクティスはこうだ。
- 単一ホストの一コマンド なら systemd timers(cron ではなく)。
- サーバーレス環境 なら Vercel Cron、EventBridge Scheduler、Cloudflare Cron のいずれかをインフラに合わせて。
- アプリ内のバックグラウンド + 定期作業 なら BullMQ / Sidekiq / Celery Beat。
- 多段階ワークフロー なら Temporal / Inngest / Trigger.dev / Hatchet(モダン)か Airflow / Dagster / Prefect(伝統)。
- 何を選んでも Healthchecks.io / Cronitor.io で未受信アラート を仕掛ける。
このガイドに従えば、2026 年のケースの 99% は片付く。
2. 古典的な cron — 5 フィールド表記、/etc/crontab
cron は 1975 年に Brian Kernighan が書いた Unix のツールだ。50 年間生き残り、ほぼすべての Linux ホストに今もインストールされている。中心の 5 フィールド から。
# ┌───────── 分 (0-59)
# │ ┌─────── 時 (0-23)
# │ │ ┌───── 日 (1-31)
# │ │ │ ┌─── 月 (1-12)
# │ │ │ │ ┌─ 曜日 (0-7、0 と 7 は日曜)
# │ │ │ │ │
# * * * * * command-to-execute
よく使うパターン。
# 毎日 0 時
0 0 * * * /usr/local/bin/backup.sh
# 毎時 0 分
0 * * * * /usr/local/bin/sync.sh
# 5 分おき
*/5 * * * * /usr/local/bin/healthcheck.sh
# 平日 9 時
0 9 * * 1-5 /usr/local/bin/report.sh
# 毎月 1 日 3 時
0 3 1 * * /usr/local/bin/monthly-billing.sh
cron には 2 種類の crontab がある。
- ユーザー crontab:
crontab -eで編集、/var/spool/cron/に保存、ユーザー権限で実行。 - システム crontab:
/etc/crontabや/etc/cron.d/*に置き、実行ユーザーを 6 番目のフィールドとして明示する。
# /etc/crontab — システム用。user フィールドが追加される。
SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin
# m h dom mon dow user command
17 * * * * root cd / && run-parts --report /etc/cron.hourly
25 6 * * * root test -x /usr/sbin/anacron || ( cd / && run-parts /etc/cron.daily )
5 フィールド以外に、一部の cron 実装(Vixie cron、anacron、cronie)は 秒の 6 フィールド を扱える。ただし標準的な POSIX cron は 5 フィールドしか理解しないことを忘れずに。BullMQ・Quartz・Spring などのライブラリは 6 フィールドや 7 フィールド(年)まで受け付ける。
cron の限界。
- 監視がない: 失敗しても誰も気付かない。出力はメールで送られるが、2026 年に root のメールを誰が読んでいるのか。
- キャッチアップなし: ホストが落ちている間に予定された実行は素直に欠落(anacron がこの問題を解く)。
- 単一ホスト: 分散ロックがない → 同じ cron を 2 つのホストに入れると 2 回走る。
- タイムゾーン: システムの TZ で動く。UTC でなければ DST(サマータイム)で 1 時間消えるか 2 回走る事故が起きる。
- 表現力不足: 「毎月最終平日」のような表現が難しい。
それでも cron は生きている。シンプルさという価値があるからだ。1 行のジョブ、1 つのホスト、監視が明らかに不要なケース(ログローテーション、一時ファイルの掃除)であれば、cron は今でも最適だ。
3. systemd timers — Linux のモダンな代替
systemd が init システムを統一して以降(2010 年代半ば)、systemd timers が cron のモダンな代替として定着した。RHEL/CentOS Stream、Ubuntu、Debian、Fedora はすべて systemd がデフォルトだ。
systemd timer は 2 ファイル構成。
# /etc/systemd/system/backup.service
[Unit]
Description=Daily backup
[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.sh
User=backup
Nice=10
IOSchedulingClass=idle
# /etc/systemd/system/backup.timer
[Unit]
Description=Daily backup timer
[Timer]
OnCalendar=daily
Persistent=true
RandomizedDelaySec=10min
[Install]
WantedBy=timers.target
有効化。
sudo systemctl daemon-reload
sudo systemctl enable --now backup.timer
sudo systemctl list-timers
systemd timers が cron に勝る点。
- ジャーナルロギング統合:
journalctl -u backup.service一発で実行ログを確認。cron はメールか syslog。 - Persistent: ホストが落ちて欠落した実行を、立ち上がった直後に 1 回だけ実行。anacron 相当が組み込み。
- RandomizedDelaySec: 同じ時刻に全ホストが一斉に cron を走らせて負荷が跳ねる問題を自動で分散。
- OnCalendar の表現力:
Mon..Fri 09:00、*-*-1..7 09:00(毎月第 1 週の平日)、quarterly、yearlyなど人間に読める形式。 - リソース制限: Service unit が
CPUQuota、MemoryMax、IOSchedulingClassといった cgroup 制限を直接持つ。 - 依存関係:
Requires=、After=で「別サービスが起きているときだけ実行」のような条件を表現できる。
OnCalendar の例。
# 毎日 03:00
OnCalendar=daily
# 毎週日曜 04:00
OnCalendar=Sun 04:00
# 毎月 1 日と 15 日
OnCalendar=*-*-01,15 02:00
# 平日 10 分おき
OnCalendar=Mon..Fri *:0/10
# 6 時間おき (0, 6, 12, 18)
OnCalendar=*-*-* 0/6:00:00
systemd-analyze calendar 'Mon..Fri *:0/10' で表記を検証できる。次の発火時刻も出力される。
cron から systemd timer への移行は、2026 年の標準推奨と言ってよい。Ubuntu 24.04 LTS と RHEL 10 のいずれも cron はオプションパッケージで、systemd timer が標準メカニズムになっている。
4. Vercel Cron — サーバーレススケジュール
Vercel Cron は 2022 年に GA となり、2026 年現在は Next.js・SvelteKit などのフレームワークと統合された標準スケジューラだ。
設定は vercel.json 1 ファイル。
{
"crons": [
{
"path": "/api/cron/daily-report",
"schedule": "0 9 * * *"
},
{
"path": "/api/cron/cleanup",
"schedule": "*/15 * * * *"
}
]
}
ハンドラ(Next.js App Router)。
// app/api/cron/daily-report/route.ts
import { NextResponse } from 'next/server'
export async function GET(request: Request) {
// Vercel は Authorization ヘッダで自前のシークレットを送る
const authHeader = request.headers.get('authorization')
if (authHeader !== `Bearer ${process.env.CRON_SECRET}`) {
return new NextResponse('Unauthorized', { status: 401 })
}
// 実処理
await sendDailyReport()
return NextResponse.json({ ok: true })
}
Vercel Cron の特徴。
- Hobby/Free プラン: 1 日 cron 最大 2 個、毎分 1 回。学習・個人プロジェクト向け。
- Pro プラン: 40 個の cron、毎分複数回可。実運用に耐える。
- Enterprise: 事実上無制限。
- タイムアウト: 関数自体のタイムアウト(Pro 60 秒、Enterprise 900 秒)内に終える必要あり。
- HMAC 認証:
CRON_SECRET環境変数で外部からの任意呼び出しを防止。 - 監視: Vercel ダッシュボードで最後の実行とステータスを確認。
制約は明確だ。関数タイムアウト内に終わるものに限られるため、長時間処理(バッチ、大量データ処理)には不向き。長くなるなら ジョブキュー(Inngest、Trigger.dev、QStash)に投げて即座に終わるパターンが定番。Vercel Cron 自体は「適時に起こすアラーム」だ。
5. AWS EventBridge Scheduler(2022 年 11 月) — CloudWatch Events Rules の置き換え
AWS の cron は 2022 年 11 月に EventBridge Scheduler に世代交代した。以前の標準は CloudWatch Events Rules(CloudWatch Events rate / cron expression)だったが、EventBridge Scheduler は 1 アカウントあたり 1 億スケジュールまで扱えると発表されてからは推奨オプションとなった。
差分の核心。
| 項目 | CloudWatch Events Rules(旧) | EventBridge Scheduler(新) |
|---|---|---|
| 提供開始 | 2016 | 2022 年 11 月 |
| スケジュール数上限 | アカウントあたり 100~300(サービスクォータ) | アカウントあたり 1 億 |
| 一回限りスケジュール | なし | at(...) 表現対応 |
| タイムゾーン | UTC 固定 | IANA TZ 指定可 |
| フレキシブル時間ウィンドウ | なし | FlexibleTimeWindowMinutes |
| ターゲット統合 | EventBus 経由の間接 | Lambda、SQS、ECS、SageMaker、Step Functions など 200+ に直接 |
| 推奨 | 非推奨ガイダンス | AWS 公式推奨 |
EventBridge Scheduler を作る(Terraform)。
resource "aws_scheduler_schedule" "daily_report" {
name = "daily-report"
group_name = "default"
flexible_time_window {
mode = "OFF"
}
schedule_expression = "cron(0 9 ? * MON-FRI *)"
schedule_expression_timezone = "Asia/Tokyo"
target {
arn = aws_lambda_function.report.arn
role_arn = aws_iam_role.scheduler.arn
retry_policy {
maximum_retry_attempts = 3
maximum_event_age_in_seconds = 3600
}
}
}
AWS の cron 表記は 6 フィールド(分 時 日 月 曜日 年)で、? は「気にしない」を意味する。POSIX cron と微妙に異なるので要注意。
もうひとつの選択肢が Step Functions の Wait + EventBridge の組み合わせだ。ワークフロー内の「5 分待ってから次へ」のようなパターンは Step Functions 自身の Wait state で表現し、外部から起こすところを EventBridge Scheduler が担う。
2026 年の推奨は 新規プロジェクトは EventBridge Scheduler、既存の CloudWatch Events Rules は段階的に移行。AWS 自身がコンソールでマイグレーションツールを提供している。
6. Cloudflare Workers Cron Triggers
Cloudflare Workers はエッジ関数プラットフォームで、Cron Triggers という名前で cron 表記ベースのスケジューリングを提供する。2026 年現在、Workers の無料プランでも cron を 3 つまで持てる。
wrangler.toml の設定。
name = "my-worker"
main = "src/index.ts"
compatibility_date = "2026-05-01"
[triggers]
crons = [
"0 */6 * * *",
"0 0 * * 0"
]
ハンドラ。
// src/index.ts
export default {
async scheduled(event: ScheduledEvent, env: Env, ctx: ExecutionContext) {
console.log(`cron fired: ${event.cron} at ${event.scheduledTime}`)
// 実処理
await runCleanup(env)
},
async fetch(req: Request): Promise<Response> {
return new Response('Worker alive')
}
}
Cloudflare の差別化ポイント。
- エッジ全球実行: cron がどこで起きるかは Cloudflare が決める(ルーティング最適化)。ワークロードにタイムゾーン依存があるならコード側で処理する。
- 無料枠込み: 10 万リクエスト / 日の中に cron も含まれる。
- 30 秒 CPU 上限: 標準 Worker は 30 秒 CPU。長時間処理は Durable Objects か Queues と組み合わせる。
- イベントメタデータ:
event.cronでどの式が発火したかが分かるので、1 関数で複数スケジュールを分岐できる。
Workers Cron Triggers は Vercel Cron と似ているが、エッジ分散 と 手厚い無料枠 が強みだ。
7. GitHub Actions schedule / GitLab schedule pipelines
CI/CD プラットフォーム自体が cron になるパターンは 2020 年以降に爆発的に増えた。インフラ運用、データ同期、定期点検などを ワークフローファイル で管理できる。
GitHub Actions schedule の例。
# .github/workflows/nightly.yml
name: Nightly Maintenance
on:
schedule:
- cron: '0 17 * * *' # UTC 17:00 = JST 02:00
workflow_dispatch: # 手動実行も許可
jobs:
cleanup:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run cleanup
run: ./scripts/cleanup.sh
GitHub Actions schedule の注意点。
- UTC 固定: タイムゾーンオプションなし。自前で変換する必要がある。
- 低活動リポジトリの自動停止: 60 日 push がないと、schedule ワークフローは自動的に無効化される。あえてコミットする必要あり。
- 遅延あり: GitHub の負荷次第で、約束時刻から数分~数十分遅れて発火することはよくある。
- 無料枠: パブリックリポジトリは無料、プライベートは分単位の quota から消費。
GitLab schedule pipelines。
# .gitlab-ci.yml
nightly:
script:
- ./scripts/cleanup.sh
rules:
- if: $CI_PIPELINE_SOURCE == "schedule"
GitLab はコード以外に、CI/CD > Schedules の UI から時刻・タイムゾーン・変数を設定する。GitHub Actions と違ってタイムゾーン設定が効くのが強み。
CI スケジュールが向いているケース。
- 依存関係のセキュリティスキャン / SBOM 更新。
- 静的サイトの再生成(為替・天気など)。
- 毎日 / 毎週のレポートメール送信。
- Git リポジトリのミラーリング。
不向きなケース。
- 分単位の精度が必要な処理(CI は約束時刻より遅れがち)。
- リアルタイムなインフラ操作(EventBridge Scheduler か Vercel Cron 推奨)。
8. Kubernetes CronJobs — kind: CronJob
Kubernetes には 1.21 で CronJob が安定化した。一度きりのコンテナ実行リソースである Job の上に cron 表記を載せたもの。
apiVersion: batch/v1
kind: CronJob
metadata:
name: daily-cleanup
spec:
schedule: "0 2 * * *"
timeZone: "Asia/Tokyo"
concurrencyPolicy: Forbid
startingDeadlineSeconds: 600
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 1
jobTemplate:
spec:
backoffLimit: 2
template:
spec:
restartPolicy: OnFailure
containers:
- name: cleanup
image: registry.example.com/cleanup:1.4.2
command: ["/app/cleanup.sh"]
resources:
requests: { cpu: "100m", memory: "128Mi" }
limits: { cpu: "1", memory: "512Mi" }
Kubernetes CronJob の主なフィールド。
- schedule: 5 フィールドの cron 表現。
- timeZone: 1.25 から正式。IANA TZ(例:
Asia/Tokyo)。 - concurrencyPolicy:
Allow(既定) /Forbid(前のジョブが終わっていなければ起動しない) /Replace(新しいジョブが前のジョブを殺す)。 - startingDeadlineSeconds: この秒数以内に起こせなければ諦める(コントローラが少し遅れたとき向け)。
- successfulJobsHistoryLimit / failedJobsHistoryLimit: Job オブジェクトを何件残すか。
- backoffLimit: Pod 失敗時の再試行回数。
CronJob の落とし穴。
- clock drift: ノード間で時刻がずれると発火時刻が揺れる。NTP は必須。
- コントローラ遅延: kube-controller-manager に負荷がかかると発火が遅れる。平均で 1 秒〜数十秒。
- タイムアウト未設定: Job に
activeDeadlineSecondsを入れないと無限実行が起こり得る。 - タイムゾーン罠: 1.25 以前のクラスタは常に UTC。DST をまたぐ時刻で 2 回または 0 回発火する古典的バグがある。
運用 Tips。
- CronJob ごとに PodDisruptionBudget と PriorityClass を定義してノード障害に強くする。
- ロギング: stdout/stderr をコンテナログ → Loki / OpenSearch へ収集。
- 監視: Prometheus の
kube_cronjob_*メトリクス + Alertmanager で「最後に成功してから X 時間経過したらアラート」。
9. BullMQ(Node) / Sidekiq(Ruby) / Celery Beat(Python)
アプリ内のバックグラウンドジョブ + 定期作業のパターンは、Redis ベースのジョブキュー + スケジューラ が定番だ。2026 年の三大選択肢。
BullMQ — Node.js
BullMQ は OptimalBits の Bull の後継で、2026 年の Node.js 界の標準キュー。
import { Queue, Worker, QueueEvents } from 'bullmq'
const connection = { host: '127.0.0.1', port: 6379 }
// キュー
const reportsQueue = new Queue('reports', { connection })
// 反復ジョブ(cron)登録
await reportsQueue.add(
'daily-report',
{ type: 'pdf' },
{
repeat: { pattern: '0 9 * * 1-5', tz: 'Asia/Tokyo' },
jobId: 'daily-report',
}
)
// ワーカー
new Worker('reports', async (job) => {
console.log(`processing ${job.name}`)
await generateReport(job.data)
}, { connection })
BullMQ の強み。
- TypeScript first: 型安全。
- Repeatable jobs: cron か every(ミリ秒)。tz 指定可。
- Flow producer: 親子ジョブの依存関係(ワークフロー風)。
- Rate limiting / priority / retries: 1 行で。
BullMQ Pro は有料。グループ、一時停止、優先キューグループなどエンタープライズ機能を追加。BullMQ を運用する会社(Taskforce.sh)がメンテナで、無料 OSS も活発。
Sidekiq — Ruby
Sidekiq は Ruby 界で圧倒的 1 位。2012 年に Mike Perham が開始、2026 年も活発に開発が続いている。
# Gemfile
gem 'sidekiq'
gem 'sidekiq-scheduler' # または sidekiq-cron
# config/sidekiq.yml
:schedule:
daily_report:
cron: '0 9 * * 1-5'
class: DailyReportJob
queue: reports
# app/jobs/daily_report_job.rb
class DailyReportJob
include Sidekiq::Job
sidekiq_options queue: :reports, retry: 3
def perform
ReportMailer.daily.deliver_now
end
end
Sidekiq の派生。
- Sidekiq(OSS): 無料。Redis。Web UI 標準。
- Sidekiq Pro: 有料。信頼性強化(reliable fetch、batches)。
- Sidekiq Enterprise: さらに高価。ユニークジョブ、定期ジョブ内蔵(別 gem 不要)、マルチプロセス、マルチ DC。
Mike Perham のビジネスモデルは、OSS と有料版を単一メンテナが運営する好例としてよく引用される。
Celery Beat — Python
Celery は Python のバックグラウンドキューの標準。Celery Beat がそのスケジューラコンポーネント。
# celery_app.py
from celery import Celery
from celery.schedules import crontab
app = Celery('myapp', broker='redis://localhost:6379/0')
app.conf.beat_schedule = {
'daily-report': {
'task': 'tasks.generate_report',
'schedule': crontab(hour=9, minute=0, day_of_week='1-5'),
},
'every-15-min-cleanup': {
'task': 'tasks.cleanup',
'schedule': 900.0, # 秒単位
},
}
app.conf.timezone = 'Asia/Tokyo'
# tasks.py
@app.task
def generate_report():
...
実行。
# ワーカー
celery -A celery_app worker -l info
# Beat スケジューラ(単一インスタンスのみ)
celery -A celery_app beat -l info
Celery Beat の落とし穴 — Beat は単一ノード だけにすること。2 つのノードで立ち上げると、すべてのジョブがキューに 2 回入る。分散ロックが欲しければ celery-redbeat というバックエンドを使う(Redis 分散ロックで Beat が 2 つ生きていてもキューに 1 回しか入らない)。
10. RQ / dramatiq / APScheduler — Python の選択肢
Celery 以外にも、Python にはより軽い代替がいくつかある。
RQ(Redis Queue)
最もシンプルな Python ジョブキュー。Celery より圧倒的に簡単だが、その分機能も少ない。
from rq import Queue
from rq_scheduler import Scheduler
from redis import Redis
from datetime import datetime
scheduler = Scheduler(connection=Redis())
scheduler.cron(
'0 9 * * 1-5',
func=generate_report,
queue_name='reports',
)
rq-scheduler パッケージが cron 表記をサポート。RQ が向く場面: 単一ホスト、単一ワーカー、軽量なバックグラウンド処理。
dramatiq
RQ より頑健、Celery より単純な中間。RabbitMQ 推奨だが Redis も使える。
import dramatiq
from dramatiq.brokers.rabbitmq import RabbitmqBroker
from dramatiq_crontab import cron
dramatiq.set_broker(RabbitmqBroker())
@cron('0 9 * * 1-5')
@dramatiq.actor
def daily_report():
...
dramatiq の強み: リトライバックオフ、デッドレター、ミドルウェアシステムが綺麗。Celery が重すぎ、RQ が軽すぎると感じるチームの選択肢。
APScheduler
ワーカーが別途要らない アプリ内インプロセススケジューラ。Flask・FastAPI から直接呼ぶ。
from apscheduler.schedulers.asyncio import AsyncIOScheduler
from apscheduler.triggers.cron import CronTrigger
scheduler = AsyncIOScheduler(timezone='Asia/Tokyo')
scheduler.add_job(
daily_report,
CronTrigger.from_crontab('0 9 * * 1-5')
)
scheduler.start()
APScheduler が向く場面: 1〜2 プロセスの小規模アプリで、外部 Redis/RabbitMQ を置きたくない場合。限界: 分散ロックが弱い、プロセスが死ぬと欠落。SQLAlchemy / MongoDB / Redis のジョブストアがあるが、いずれも強整合性ではない。
選択ガイド。
| ツール | バックエンド | 分散 | 複雑度 | 適合 |
|---|---|---|---|---|
| Celery + Beat | Redis / RabbitMQ | Yes(Beat は 1 個のみ) | 高 | 大規模アプリ、多様なワークロード |
| RQ + rq-scheduler | Redis | 中 | 低 | 単一ホスト、軽量ジョブ |
| dramatiq | RabbitMQ / Redis | Yes | 中 | 信頼性 + 単純さ |
| APScheduler | インプロセス | No | 非常に低 | 1〜2 プロセスの小アプリ |
11. Airflow / Dagster / Prefect / Temporal / Inngest / Trigger.dev — ワークフロー
ワークフローエンジンは cron の自然な進化形。多段階 + リトライ + 状態追跡 + 可視化 が中心。このカテゴリは別記事で深掘りしたので、ここでは cron 視点に絞って整理する。
Apache Airflow
データエンジニアリングの標準。DAG(Directed Acyclic Graph)の中に schedule_interval を置く。
from airflow import DAG
from airflow.operators.python import PythonOperator
from datetime import datetime
with DAG(
'daily_etl',
schedule_interval='0 2 * * *',
start_date=datetime(2026, 1, 1),
catchup=False,
tags=['etl'],
) as dag:
extract = PythonOperator(task_id='extract', python_callable=do_extract)
transform = PythonOperator(task_id='transform', python_callable=do_transform)
load = PythonOperator(task_id='load', python_callable=do_load)
extract >> transform >> load
Airflow の強みは可視化と catchup(欠落時刻のバックフィル)。弱みは運用の複雑さと重いスケジューラ。
Dagster
アセットベースのモデル。cron だけでなく、データアセットが古くなったとき に発火させるパターンが強力。
from dagster import asset, ScheduleDefinition, define_asset_job
@asset
def daily_metrics():
...
daily_job = define_asset_job('daily_metrics_job', selection=[daily_metrics])
daily_schedule = ScheduleDefinition(
job=daily_job,
cron_schedule='0 2 * * *',
execution_timezone='Asia/Tokyo',
)
Prefect
Python の関数デコレータでワークフローを定義。cron 以外に interval、rrule(反復)も対応。
from prefect import flow, task
from prefect.deployments import Deployment
from prefect.client.schemas.schedules import CronSchedule
@task
def fetch(): ...
@flow
def daily_pipeline():
fetch()
Deployment.build_from_flow(
flow=daily_pipeline,
name='daily',
schedule=CronSchedule(cron='0 2 * * *', timezone='Asia/Tokyo'),
).apply()
Temporal Schedules
Temporal はワークフローエンジン界の新星。2023 年に Schedules API が正式に出て、cron + ワークフローの信頼性を統合した。
import { Client } from '@temporalio/client'
const client = new Client()
await client.schedule.create({
scheduleId: 'daily-report',
spec: {
cronExpressions: ['0 9 * * 1-5'],
timezoneName: 'Asia/Tokyo',
},
action: {
type: 'startWorkflow',
workflowType: 'DailyReportWorkflow',
workflowId: 'daily-report',
taskQueue: 'reports',
},
policies: {
overlap: 'SKIP', // 前のワークフローが終わっていなければスキップ
catchupWindow: '1h',
},
})
Temporal Schedules の強みは ワークフロー自体が永続的 であること。cron が発火してワークフローが始まれば、そのワークフローはホストが死んでも別のホストで継続実行される。本物の「fire and forget」。
Inngest Crons
サーバーレス関数 / イベント駆動ワークフロー。cron 表記のほかイベント駆動トリガーも持つ。
import { inngest } from './client'
export const dailyReport = inngest.createFunction(
{ id: 'daily-report' },
{ cron: 'TZ=Asia/Tokyo 0 9 * * 1-5' },
async ({ step }) => {
const data = await step.run('fetch', fetchData)
await step.run('send', () => sendReport(data))
}
)
Trigger.dev v3
React 風のワークフローコード。cron 以外に cron.list、schedules.create() のような動的スケジュール API も提供。
import { schedules } from '@trigger.dev/sdk/v3'
export const dailyReport = schedules.task({
id: 'daily-report',
cron: { pattern: '0 9 * * 1-5', timezone: 'Asia/Tokyo' },
run: async (payload) => {
// payload.timestamp で発火時刻を受け取る
}
})
12. Hatchet — 新星ワークフロー
Hatchet は 2024 年に登場したワークフローエンジン。PostgreSQL 上にジョブキュー + ワークフロー + スケジュールを統合したのが特徴。
import { Hatchet } from '@hatchet-dev/typescript-sdk'
const hatchet = Hatchet.init()
hatchet.workflow({
id: 'daily-report',
on: { cron: '0 9 * * 1-5' },
steps: [
{
name: 'fetch-data',
run: async (ctx) => fetchData(),
},
{
name: 'send-email',
parents: ['fetch-data'],
run: async (ctx) => {
const data = ctx.stepOutput('fetch-data')
await sendEmail(data)
},
}
]
})
Hatchet の差別化ポイント。
- PostgreSQL 単一依存: Redis・Kafka・NATS のような別依存は不要。Postgres 1 インスタンスでキュー + ジョブ保存 + ワークフロー状態のすべて。
- セルフホスト向き: 単一 Docker イメージでフルスタックを動かせる。
- OpenTelemetry 内蔵: すべてのジョブ / ワークフローがトレースとして出る。
- 有料 / 無料: OSS は無料、Hatchet Cloud がマネージド版。
2026 年で Hatchet が向くのは: PostgreSQL を既に運用している小〜中規模チームが Temporal より軽い選択肢を探すとき。
13. Quartz(Java) / Spring @Scheduled / Hangfire(.NET)
JVM と .NET 界の cron。
Quartz Scheduler
JVM 界の古典的スケジューリングライブラリ。1999 年からあり、2026 年も活発。
import org.quartz.*;
import static org.quartz.JobBuilder.*;
import static org.quartz.TriggerBuilder.*;
import static org.quartz.CronScheduleBuilder.*;
public class DailyReportJob implements Job {
public void execute(JobExecutionContext ctx) {
// 処理
}
}
JobDetail job = newJob(DailyReportJob.class)
.withIdentity("dailyReport", "reports")
.build();
CronTrigger trigger = (CronTrigger) newTrigger()
.withIdentity("dailyReportTrigger", "reports")
.withSchedule(cronSchedule("0 0 9 ? * MON-FRI").inTimeZone(TimeZone.getTimeZone("Asia/Tokyo")))
.build();
scheduler.scheduleJob(job, trigger);
Quartz は JDBC JobStore でジョブ状態を RDB に永続化し、クラスタモード(複数のスケジューラインスタンスが同じ DB を共有)にも対応。AWS EventBridge や BullMQ のようなマネージドより精緻な制御が必要なときに。
Spring @Scheduled
Spring Boot ならアノテーション 1 つで cron ジョブを作れる。
@Component
public class ScheduledTasks {
@Scheduled(cron = "0 0 9 * * MON-FRI", zone = "Asia/Tokyo")
public void dailyReport() {
// 処理
}
@Scheduled(fixedRate = 60000)
public void healthcheck() {
// 60 秒おき
}
}
main クラスに @EnableScheduling を付ければ有効になる。Spring の cron 表記は 6 フィールド(秒 分 時 日 月 曜日) に注意。POSIX cron とは違う。
分散環境では ShedLock ライブラリで分散ロックを足し、複数インスタンスで同じジョブが 2 回回らないようにする。
Hangfire — .NET
.NET 界のバックグラウンドジョブ + スケジュールライブラリ。
// Startup.cs
services.AddHangfire(c => c.UseSqlServerStorage(connStr));
services.AddHangfireServer();
// Program / Controller
RecurringJob.AddOrUpdate<IDailyReportService>(
"daily-report",
svc => svc.SendReport(),
"0 9 * * 1-5",
TimeZoneInfo.FindSystemTimeZoneById("Tokyo Standard Time")
);
Hangfire の強み。
- SQL Server / PostgreSQL / Redis バックエンドが選べる。
- ダッシュボード: ジョブ状態、失敗、リトライをブラウザで確認。
- 自動リトライ: 失敗時の指数バックオフ。
- OSS 無料 + Hangfire Pro 有料。
Hangfire は .NET 界の事実上の標準だ。
Castle Windsor のような IoC コンテナとの統合も綺麗で、依存性注入が整理しやすい。
14. fcron / anacron — Linux の代替
cron の代替が 2 つ、Linux 界には残っている。
anacron
ホストが常時起動とは限らない環境(ノート PC、デスクトップ)向けの cron 補完。ジョブの予定時刻にホストが落ちていれば、次に起きたときに 1 回だけ実行してくれる。
# /etc/anacrontab
# period delay job-identifier command
1 5 cron.daily run-parts /etc/cron.daily
7 25 cron.weekly run-parts /etc/cron.weekly
@monthly 45 cron.monthly run-parts /etc/cron.monthly
period は日数、delay は起動後の待機分数、job-identifier は最終実行を追跡する名前。systemd timers の Persistent=true が同じ機能をより綺麗にこなす。
fcron
cron と anacron の統合を目指したもの。1999 年から開発されており、「多様な環境を 1 ツールで」が目標。
# fcrontab の例
@daily,mailto=ops backup.sh
@reboot,nice=10 startup-checks.sh
%every 10 minutes after boot * * * * /usr/local/bin/poll.sh
fcron の差別化ポイント。
@daily、%hours、%minsなど人間に読みやすい独自表記。- 起動後 N 分のような相対時刻指定。
- 落ちていた間のジョブの追いつき。
- ジョブの同時実行制限のようなリソース管理。
2026 年現在、fcron はニッチなツール。systemd timers が実質同じ機能を提供しつつ、より深いシステム統合を持つから。それでも一部の Debian / Gentoo 環境ではまだ使われている。
15. Healthchecks.io / Cronitor.io — 監視
cron の「黙って失敗する」問題を解くデッドマンスイッチサービス。ジョブが「完了した」と ping を送らねばならず、送らないとアラート。別記事でより深く触れているので、ここでは使用パターンに絞る。
Healthchecks.io
OSS + マネージド。無料プランが手厚い(チェック 20 個まで)。
# crontab
0 9 * * 1-5 /usr/local/bin/report.sh && curl -fsS --retry 3 https://hc-ping.com/PROJECT_ID/daily-report > /dev/null
または開始 / 終了 / 失敗の 3 つに ping を分ける。
#!/bin/bash
URL="https://hc-ping.com/PROJECT_ID/daily-report"
curl -fsS --retry 3 "$URL/start"
if /usr/local/bin/report.sh; then
curl -fsS --retry 3 "$URL"
else
curl -fsS --retry 3 "$URL/fail"
fi
/start と終了 ping の間の時間で実行時間を測る。/fail で失敗アラート。Slack・メール・PagerDuty 統合あり。
Cronitor.io
商用 SaaS。より精緻なメトリクス、アラートルーティング。Healthchecks.io のマネージド競合。
# Cronitor も同じパターン
0 9 * * 1-5 cronitor exec daily-report /usr/local/bin/report.sh
cronitor CLI が開始 / 終了 / 失敗 ping を自動で送る。
運用推奨: 重要な cron すべてに監視を仕掛ける。2026 年の SRE 合意は「ping のない cron は運用負債」。
16. Nomad periodic jobs
HashiCorp Nomad の cron 相当。コンテナ / バイナリ / Java / Docker / QEMU など多様なワークロードタイプに対応するのが差別化。
job "daily-cleanup" {
type = "batch"
periodic {
cron = "0 2 * * *"
time_zone = "Asia/Tokyo"
prohibit_overlap = true
}
group "cleanup" {
task "run" {
driver = "docker"
config {
image = "registry.example.com/cleanup:1.4.2"
command = "/app/cleanup.sh"
}
resources {
cpu = 500
memory = 256
}
}
}
}
Nomad の強み。
- 単一バイナリ: HashiCorp 標準の運用モデル。
- 多様な driver: Docker だけでなく raw_exec、java、qemu、podman など。
- Consul / Vault 統合: サービスディスカバリとシークレット。
- k8s より単純: 小規模チームでも運用しやすい。
Kubernetes CronJob と比べると、Nomad は 小〜中規模環境 に向く。大規模 SaaS は k8s が優勢。
17. 韓国 / 日本 — トス、カカオ、メルカリ
トス — cron インフラ
トスは SLASH カンファレンスで「分散 cron インフラ」のセッションを何度か出している。要点。
- 自前 ScheduleJob プラットフォーム: トス内部の cron マネージャ。数千個の cron ジョブを中央管理。
- 分散ロック: 同一ジョブが 2 ホストで走らないように Redis または ZooKeeper の分散ロック。
- k8s CronJob の上の抽象化レイヤ: 開発者は YAML ではなく UI で登録。
- 監視自動化: ジョブ登録時に自動でデッドマンスイッチまで作る。
- タイムゾーン統合: すべてのジョブが KST。UTC 変換はプラットフォームが担当。
トスの核心: 「cron ジョブが数百を超えたら、ツールではなく プラットフォーム が要る」。
カカオ — タスクスケジューリング
カカオは if(kakao) カンファレンスでタスクスケジューリングのインフラを共有している。
- カカオトークプッシュ: 定時送信プッシュは独立スケジューラ。Quartz の上に自前で構築。
- データパイプライン: Airflow + 自前ラッパー。
- 広告精算: Sidekiq 風のジョブキュー + cron の組み合わせ。
カカオの教訓: 「1 つのツールではすべてをカバーできない。ワークロードごとに最も適したツールをモザイクのように組み合わせる」が現実解。
メルカリ — Scheduled Jobs
メルカリ日本は Microservices 運用の代表例として有名。
- Google Cloud Tasks + Cloud Scheduler: GCP 中心のインフラ。
- k8s CronJob: インフラ整理タスク。
- 各マイクロサービス内のインプロセススケジュール: 軽量なジョブはサービス自身が処理。
メルカリのエンジニアリングブログ(英語)が定期的に運用事例を公開している。
18. 誰が何を選ぶべきか — シンプル / 分散 / バックグラウンド / 信頼性
2026 年の cron 選定をデシジョンツリーにまとめると。
ジョブは 1 ホスト・1 コマンドで完結?
├── YES → systemd timers(cron ではなく)
│ + Healthchecks.io ping
└── NO →
インフラはサーバーレス?
├── Vercel ベース → Vercel Cron
├── AWS ベース → EventBridge Scheduler
├── Cloudflare → Workers Cron Triggers
└── コンテナ → k8s CronJob または Nomad periodic
ジョブはアプリ内バックグラウンドの一部?
├── Node → BullMQ
├── Ruby → Sidekiq
├── Python → Celery Beat(大)/ RQ(小)/ dramatiq(中)
├── Java → Quartz / Spring @Scheduled
└── .NET → Hangfire
ジョブは多段階ワークフロー?
├── データ → Airflow / Dagster / Prefect
├── 信頼性 → Temporal
├── サーバーレス → Inngest / Trigger.dev v3
└── Postgres のみ → Hatchet
CI 活動に紐づく?
└── GitHub Actions schedule / GitLab schedule pipelines
ルール化。
- 単純なジョブ + 1 ホスト → systemd timers。
- クラウドマネージド → Vercel / EventBridge / Cloudflare のうちインフラ依存に合うもの。
- アプリ統合 → 言語別の標準(BullMQ / Sidekiq / Celery Beat / Quartz / Hangfire)。
- ワークフロー信頼性 → Temporal / Hatchet。
- データパイプライン → Airflow / Dagster / Prefect。
- 何を使っても → Healthchecks.io か Cronitor.io で監視。
2026 年の cron 運用は「1 ツールですべて」ではなく、「ワークロードごとに最適なツールを選び、すべてに監視を付ける」 が標準だ。
参考 / References
- cron(8) — Unix manual page
- crontab(5) — format specification
- systemd.timer documentation
- systemd-analyze calendar
- Vercel Cron Jobs
- AWS EventBridge Scheduler announcement (Nov 2022)
- AWS EventBridge Scheduler documentation
- Cloudflare Workers Cron Triggers
- GitHub Actions schedule events
- GitLab Pipeline schedules
- Kubernetes CronJob
- BullMQ documentation
- BullMQ Pro
- Sidekiq documentation
- Sidekiq Pro / Enterprise
- Celery documentation
- Celery Beat periodic tasks
- RQ — Redis Queue
- dramatiq documentation
- APScheduler documentation
- Apache Airflow
- Dagster schedules
- Prefect schedules
- Temporal Schedules
- Inngest crons
- Trigger.dev v3 Scheduled tasks
- Hatchet documentation
- Quartz Scheduler
- Spring @Scheduled
- Hangfire documentation
- fcron homepage
- anacron documentation
- Healthchecks.io
- Cronitor
- HashiCorp Nomad periodic jobs
- Toss SLASH
- Kakao if(kakao) conference
- Mercari Engineering