cron式は、コンピューターに「このスケジュールでこのタスクを実行して」と伝える最も簡潔な方法だ。5つのフィールド、すべて数字と特殊文字で、構文を学べば曖昧さはない。問題は、実際に必要な一般的なパターンが約15個あり、残りの時間はcrontab -eプロンプトを見つめて日曜が0なのか7なのかを思い出そうとしていることだ。
この投稿は、5年前に欲しかったチートシートだ。すべての一般的なパターンに、コピペ可能な式、説明、そして人々を引っ掛けるgotchaの注記をつけて紹介する。
5つのフィールド
* * * * *
│ │ │ │ │
│ │ │ │ └── 曜日 (0-6, 日曜=0)
│ │ │ └──── 月 (1-12)
│ │ └────── 日 (1-31)
│ └──────── 時 (0-23)
└────────── 分 (0-59)
各フィールドは以下が可能だ:
- 特定の値:
5 - ワイルドカード:
*(すべての値) - 範囲:
1-5 - ステップ:
*/15(15ごと) - リスト:
1,15,30 - ステップ付き範囲:
1-30/5(1から30まで5ごと)
これだけだ。あとはこれらのプリミティブを組み合わせるだけだ。
パターン
毎分
* * * * *
使用例:常に実行されるべきタスクで、スケジューラーが再起動している場合。ほとんどの場合、あなたが望むものではない。
N分ごと
*/5 * * * * # 5分ごと
*/15 * * * * # 15分ごと
*/30 * * * * # 30分ごと
*/5は「0から始まり、5分ごと」を意味する。つまり0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55だ。
Gotcha:*/5の最初の実行は0分であり、crontabを保存した時点ではない。12:03に保存した場合、最初の実行は12:05であり、12:08ではない。
毎時0分
0 * * * *
Gotcha:これは「毎時の0分」であり、1:00, 2:00, 3:00などだ。「毎時30分」なら30 * * * *を使う。
毎日深夜0時
0 0 * * *
Gotcha:サーバーのタイムゾーン。cronはシステムタイムゾーンを使用する。サーバーがUTCで、米国東海岸の深夜0時を望む場合、変換が必要だ。最もシンプルな修正:システムタイムゾーンを望むものに設定するか、CRON_TZ環境変数を設定する(ほとんどのモダンcron実装でサポート)。
毎日の特定時刻
0 9 * * * # 午前9:00
30 14 * * * # 午後2:30
0 0 * * * # 深夜0時
15 3 * * * # 午前3:15
平日の朝9時
0 9 * * 1-5
1-5は曜日で、月曜から金曜までだ。
Gotcha:0と7はどちらも日曜を意味する。POSIX仕様は両方を許可している。ほとんどの実装は誤って7を使っても許容する。それに頼るな。
毎週月曜日
0 0 * * 1
毎週月曜日の深夜0時。
週2回(月曜と木曜)
0 9 * * 1,4
月曜と木曜の午前9時。
毎月1日
0 0 1 * *
1日の深夜0時。
月末日
0 0 L * *
LはVixie cron / Quartzの拡張だ。標準cronにはない。標準cronを使っている場合は、「最終日」の近似として0 0 28-31 * *(28日、29日、30日、31日に実行され、2月は28日に実行される)に頑張るしかない。
四半期ごと
0 0 1 */3 *
1月、4月、7月、10月の1日の深夜0時。
毎週日曜の正午
0 12 * * 0
6時間ごと
0 */6 * * *
0:00, 6:00, 12:00, 18:00。
平日の午前9時から午後5時まで毎分
* 9-17 * * 1-5
月曜から金曜まで、9:00:00から17:59:59まで毎分実行されるタスク。おそらく過激すぎる — しかし可能だ。
毎月1日の午前2時30分
30 2 1 * *
毎月最後の金曜日の午後11時
0 23 * * 5#5
5#5は「月の5番目の金曜日」(Quartz構文)だ。#とLは非標準の拡張 — 標準cronには「月の最後の金曜日」の式はない。標準cronで必要な場合、日付をチェックするラッパースクリプトが必要だ。
30秒ごと
標準cronは1分以下には対応していない。サブミニットスケジューリングの場合:
- systemdタイマー:
OnUnitActiveSec=30s - Kubernetes CronJobでは:不可(最低1分)。ループ内の
sleep 30を持つ通常のJobを使う。 - Node.jsアプリ内:
setInterval(fn, 30_000)をプロセス内で使う。
夏時間
cronは夏時間を、タスクを2回実行する(春の前進)かスキップする(秋の後退)かのいずれかで処理する。タスクが時間に敏感な場合(請求実行、レポート)、夏時間のサプライズを避けるため、1-3AMウィンドウの外にスケジュールする。
QuartzスケジューラーはDSTフラグでこれを処理する。標準cronにはない。
1分粒度の制限
cronは基本的に1分解像度のツールだ。サブミニットスケジューリングが必要なら、「別のツールを使え」という答えになる。特にKubernetesの場合:
apiVersion: batch/v1
kind: CronJob
metadata:
name: every-30-seconds
spec:
schedule: "* * * * *"
startingDeadlineSeconds: 10
jobTemplate:
spec:
template:
spec:
containers:
- name: worker
image: myworker:latest
command: ["/bin/sh", "-c", "for i in 1 2; do do_work; sleep 30; done"]
これは毎分2つのサブジョブを発行してタスクを30秒ごとに実行する。乱暴だが動作する。
Quarkus、EventBridge、Kubernetes CronJobでは?
異なるスケジューラーは少し異なるcron構文を使う。5フィールド形式が最も一般的だ。その他:
- Quartz: 6フィールド(秒付き)、
?ワイルドカードは「特定の値なし」。 - AWS EventBridge: 6フィールド、
Lと#拡張をサポート。 - Kubernetes CronJob: 5フィールド、
Lや#のサポートなし。 - Spring
@Scheduled: 6フィールド(秒付き)、?とLのサポート。 - GitHub Actions: 5フィールド、拡張なし。
標準と一致しないツールを使っている場合、ドキュメントを確認する。このチートシートのほとんどのパターンは直接転用可能だ。非標準の拡張(L、#、W)は転用不可だ。
構文を暗記せずにcron式を生成する
構文を暗記したくない場合、DevSpeedToolsのcronジェネレーターで5つのフィールドをクリックし、次回の5回の実行を表示できる。「0 9 * * 1-5は本当に平日の朝9時か?」を確認する最速の方法だ — 答えはyesだが、次回の実行日を見ることでパターンが具体的になる。
一度限りの式には、crontab.guruが標準リファレンスだ。cronジェネレーターは同じ仕事をし、完全にクライアントサイドなので、貼り付けた式はブラウザから出ない。
まとめ
cron式は少数のルールを持つ小さなDSLだ。5つのフィールド、*(任意)、-(範囲)、/(ステップ)、,(リスト)を学べば、必要なスケジュールの95%を表現できる。残りの5%はスケジューラー固有の拡張か別のツールが必要だ。迷ったら、次回の実行を表示するツールで式を生成・プレビューしよう — デバッグのための心算よりはるかに速い。