
cron式の完全解説 ― 5つの星が生まれた歴史、書き方、そしてハマりどころ
「* * * * *」— たった5つのフィールドで「いつ実行するか」を表現する。1979年のUNIX V7で誕生したこの形式が、40年以上経った今もクラウドネイティブ時代のスケジューリング標準であり続けている理由を解き明かす。
cronとは何か — 1分の粒度で世界を動かすデーモン
cronはUNIX系OSにおける時刻ベースのジョブスケジューラデーモンです。システム起動時にバックグラウンドで起動し、毎分1回、登録されたスケジュール(crontab)を確認して、条件に合致するコマンドを実行します。
基本的な動作モデル
- cronデーモンは毎分目覚めて現在時刻を取得する
- 登録された全エントリのスケジュールと照合する
- 合致するエントリのコマンドを子プロセスとして実行する
- 実行結果(stdout/stderr)は設定に従いメール送信またはログ記録
2026年でもcronが現役な理由
コンテナ、サーバーレス、CI/CDが当たり前の時代になっても、cronの概念は至るところに生きています。
- Kubernetes CronJob — Podをcron式スケジュールで起動
- GitHub Actions —
scheduleトリガーでワークフローを定期実行 - AWS EventBridge Scheduler — サーバーレスでcron式を解釈
- systemd timer — cronの代替だがcron式パーサーも内蔵
どのプラットフォームも「時刻指定の文法」としてcron式を採用しています。一度覚えれば、あらゆる環境で通用するユニバーサルな知識です。
歴史 — UNIX V7からVixie Cronへ
1979年: UNIX V7 — Ken Thompsonのcron
cronはUNIX V7(1979年)で初めて登場しました。Ken Thompsonが実装したこの初代cronは、極めてシンプルな設計でした。
- システム全体で1つの設定ファイル(
/usr/lib/crontab)のみ - ユーザーごとのcrontabは存在しない(root管理)
- 毎分ウェイクアップしてスケジュールを確認する基本アーキテクチャはこの時点で確立
なぜ「秒」フィールドがないのか
初代cronが毎分1回だけ起動する設計だったため、最小粒度は1分でした。当時のCPUリソースを考えると、毎秒のチェックは過剰な負荷だったのです。この設計判断が40年以上引き継がれ、標準的なcron式は今も秒フィールドを持ちません。
1987年: Paul VixieのVixie Cron
Paul Vixieが書き直したcron(Vixie Cron)は、事実上の業界標準となりました。主な改善点:
- ユーザーごとのcrontab —
crontab -eで各ユーザーが自分のジョブを管理 - MAILTO変数 — 実行結果を指定アドレスにメール送信
- 環境変数の設定 — crontab内でSHELL, PATH等を指定可能
- ISCライセンス — 自由な再配布が可能
Linux(Debian, Ubuntu, RHEL系)やFreeBSDで採用され、現在のほとんどのシステムのcronはVixie Cronの派生です。
曜日番号の不統一 — 歴史的経緯
曜日フィールドで「0 = 日曜日」なのはPOSIXの規定に従っています。しかし ISO 8601 では「1 = 月曜日」が標準です。Vixie Cronでは互換性のため0と7の両方を日曜日として受け付けるという折衷策が採られました。この不統一は現在も混乱の原因になっています。
5フィールドの文法 — 基本構文の完全リファレンス
標準的なcron式は、スペース区切りの5つのフィールドで構成されます。
┌───────────── 分 (0-59)
│ ┌─────────── 時 (0-23)
│ │ ┌───────── 日 (1-31)
│ │ │ ┌─────── 月 (1-12)
│ │ │ │ ┌───── 曜日 (0-7, 0と7=日曜)
│ │ │ │ │
* * * * *
各フィールドの詳細
| フィールド | 範囲 | 使用可能な特殊文字 | 備考 |
|---|---|---|---|
| 分 | 0-59 | * , - / | |
| 時 | 0-23 | * , - / | 24時間制 |
| 日 | 1-31 | * , - / | 存在しない日は無視 |
| 月 | 1-12 | * , - / | JAN-DECも可(環境依存) |
| 曜日 | 0-7 | * , - / | 0=日, 1=月, ..., 6=土, 7=日 |
特殊文字の意味
| 文字 | 意味 | 例 |
|---|---|---|
* | 全ての値 | * * * * * = 毎分 |
, | リスト(複数指定) | 1,15 = 1日と15日 |
- | 範囲 | 1-5 = 月〜金 |
/ | ステップ(間隔) | */15 = 15ごと |
具体例で理解する
| cron式 | 意味 |
|---|---|
0 9 * * 1-5 | 平日(月〜金)の9:00 |
*/15 * * * * | 15分ごと(毎時0分, 15分, 30分, 45分) |
0 0 1,15 * * | 毎月1日と15日の0:00 |
30 4 * * 0 | 毎週日曜の4:30 |
0 */2 * * * | 2時間ごと(0:00, 2:00, 4:00...) |
よく使うcron式パターン集
実務で頻出するパターンをまとめました。コピー&ペーストして活用してください。
| やりたいこと | cron式 | 補足 |
|---|---|---|
| 毎分実行 | * * * * * | テスト・監視向け |
| 5分ごと | */5 * * * * | ヘルスチェック等 |
| 10分ごと | */10 * * * * | |
| 15分ごと | */15 * * * * | |
| 30分ごと | 0,30 * * * * | */30でも同じ |
| 毎時0分 | 0 * * * * | |
| 毎日深夜0時 | 0 0 * * * | 日次バッチの定番 |
| 毎日朝9時 | 0 9 * * * | 業務開始タイミング |
| 深夜2時(バックアップ) | 0 2 * * * | 負荷の低い時間帯 |
| 平日9:00-18:00の毎正時 | 0 9-18 * * 1-5 | 業務時間内の定期処理 |
| 毎週月曜9:00 | 0 9 * * 1 | 週次レポート等 |
| 毎週日曜3:00 | 0 3 * * 0 | 週次メンテナンス |
| 毎月1日の0:00 | 0 0 1 * * | 月次処理 |
| 毎月1日と15日の9:00 | 0 9 1,15 * * | 隔週的な処理 |
| 四半期初日(1,4,7,10月の1日) | 0 0 1 1,4,7,10 * | 四半期レポート |
| 年に1回(1月1日 0:00) | 0 0 1 1 * | 年次処理 |
| 平日の昼12時 | 0 12 * * 1-5 | ランチタイム通知等 |
| 毎月末日 | (標準cronでは不可) | 後述の「罠」参照 |
「毎月末日」の問題: 標準的なcron式には「月の最終日」を意味する表記がありません。0 0 28-31 * *とすると28〜31日全てで実行されてしまいます。解決策としては、実行コマンド側で「翌日が1日かどうか」を判定するか、AWS EventBridgeのL記法など拡張構文を使う方法があります。
6フィールド・7フィールド — 方言と拡張
標準の5フィールド形式だけでなく、環境によって拡張された構文が使われています。
Vixie Cronのショートカット記法
| 記法 | 等価なcron式 | 意味 |
|---|---|---|
@yearly | 0 0 1 1 * | 年1回(1/1 0:00) |
@monthly | 0 0 1 * * | 月1回(1日 0:00) |
@weekly | 0 0 * * 0 | 週1回(日曜 0:00) |
@daily | 0 0 * * * | 日1回(0:00) |
@hourly | 0 * * * * | 毎時0分 |
@reboot | — | システム起動時に1回 |
6フィールド形式(秒あり)
Java系のスケジューラでは先頭に「秒」フィールドが追加されます。
秒 分 時 日 月 曜日
0 0 9 * * MON-FRI (Spring @Scheduled / Quartz)
7フィールド形式(秒+年)
Quartzの完全形式では末尾に「年」フィールドが付きます。
秒 分 時 日 月 曜日 年
0 0 9 * * MON-FRI 2026
各環境の方言比較
| 環境 | フィールド数 | 秒 | 年 | 拡張記号 |
|---|---|---|---|---|
| 標準cron(POSIX) | 5 | なし | なし | なし |
| Vixie Cron | 5 | なし | なし | @記法 |
| GitHub Actions | 5 | なし | なし | なし |
| Kubernetes CronJob | 5 | なし | なし | なし |
| Spring Scheduler | 6 | あり | なし | L, W, # |
| Quartz | 6-7 | あり | 任意 | L, W, #, ? |
| AWS EventBridge | 6 | あり(分単位) | あり | ? L W |
注意: AWS EventBridgeの「6フィールド」は分 時 日 月 曜日 年の構成で、秒ではなく年が追加されます。?は「値を指定しない」を意味し、日と曜日のどちらか一方に必ず使います。
日と曜日の「OR結合」問題 — 最大の罠
cron式で最も多くの人が誤解する仕様がこれです。
仕様: 日と曜日が両方指定されると「OR」で結合される
Vixie Cronの仕様では、日フィールドと曜日フィールドの両方に*以外の値が指定された場合、どちらかの条件に合致すれば実行されます。
# 意図: 「13日の金曜日」にだけ実行したい
0 9 13 * 5
# 実際の動作: 「毎月13日」OR「毎週金曜日」に実行される!
つまり、上記の式は以下の日に全て実行されます:
- 1月13日(月曜) — 13日なので実行
- 1月3日(金曜) — 金曜なので実行
- 1月10日(金曜) — 金曜なので実行
- 1月13日(月曜) — 13日なので実行
- 1月17日(金曜) — 金曜なので実行
- ... 以下全ての金曜日と13日
なぜOR結合なのか
歴史的には「日指定と曜日指定の両方を活用できるようにする」という利便性の設計判断でした。AND結合だと、例えば「1日が月曜のときだけ」のように条件が厳しくなりすぎるためです。
「13日の金曜日」だけに実行する方法
cron式だけでは実現できません。以下のようにスクリプト側で曜日を判定します:
# crontab: 毎月13日の9:00に実行
0 9 13 * * [ "$(date +\%u)" = "5" ] && /path/to/script.sh
date +%uで曜日(1=月曜, 5=金曜)を取得し、金曜のときだけ本処理を実行します。
タイムゾーンの罠 — DST・UTC・サーバー時刻
cronはデフォルトでシステムのローカルタイムゾーンで動作します。日本(JST, UTC+9)で運用する場合は比較的安全ですが、国際的なサービスでは注意が必要です。
夏時間(DST)による実行スキップ・二重実行
夏時間を採用する地域(米国、欧州など)では深刻な問題が発生します:
| イベント | 影響 | 例 |
|---|---|---|
| 春: 時計が進む | ジョブがスキップされる | 2:00→3:00に飛ぶため、2:30のジョブは実行されない |
| 秋: 時計が戻る | ジョブが2回実行される | 2:30が2回存在するため、ジョブも2回発火 |
解決策
- CRON_TZ変数: Vixie Cron派生ではcrontab内で
CRON_TZ=UTCを指定可能 - サーバー時刻をUTCに設定: 最もシンプルで推奨される方法
- DST遷移時間帯を避ける: 2:00-3:00台のジョブを避ける
# crontab内でタイムゾーンを明示
CRON_TZ=Asia/Tokyo
0 9 * * * /path/to/job.sh
# または
CRON_TZ=UTC
0 0 * * * /path/to/midnight-utc-job.sh
日本(JST)の注意点
日本は夏時間を採用していないため、DST問題は発生しません。ただし以下のケースでは注意が必要です:
- 海外リージョンのクラウドサーバー(UTC設定が多い)で日本時間ベースのジョブを動かす場合
- GitHub ActionsのscheduleはUTC固定のため、JST 9:00に実行したければ
0 0 * * *(UTC 0:00 = JST 9:00)と書く - 複数タイムゾーンのユーザー向けサービスでcronを使う場合
実行環境の注意点 — PATHとシェル
「ターミナルでは動くのにcronでは動かない」— 最もよくある初心者のハマりポイントです。
cronの実行環境はログインシェルと異なる
| 項目 | ログインシェル | cron |
|---|---|---|
| PATH | 多数のディレクトリ | /usr/bin:/bin のみ |
| SHELL | 通常 /bin/bash | /bin/sh |
| 環境変数 | .bashrc等で設定済み | ほぼ未設定 |
| ホームディレクトリ | 設定済み | 設定されているが注意 |
対策
1. 絶対パスを使う
# NG: コマンドが見つからない可能性
0 9 * * * python script.py
# OK: 絶対パスを使用
0 9 * * * /usr/bin/python3 /home/user/scripts/script.py
2. crontab内でPATHを設定
PATH=/usr/local/bin:/usr/bin:/bin
SHELL=/bin/bash
0 9 * * * python3 /home/user/scripts/script.py
3. MAILTOで結果を受け取る
# 空にすると出力メールを抑制
MAILTO=""
# アドレスを設定するとエラー時に通知
MAILTO=admin@example.com
4. 出力のリダイレクト
# stdout/stderrをログに記録
0 2 * * * /path/to/backup.sh >> /var/log/backup.log 2>&1
# 出力を完全に破棄
0 * * * * /path/to/job.sh > /dev/null 2>&1
ログの確認方法
- syslog:
grep CRON /var/log/syslog - systemd:
journalctl -u cron - crontab確認:
crontab -lで現在の登録内容を表示
クラウド時代のcron — マネージドサービスとの比較
従来のcronデーモンに代わり、クラウドではマネージドスケジューラが主流になりつつあります。しかし、どれもcron式を「スケジュール指定の言語」として採用しています。
主要サービスの比較
| サービス | 提供元 | cron式サポート | 特徴 |
|---|---|---|---|
| EventBridge Scheduler | AWS | 6フィールド(年あり) | サーバーレス、リトライ、DLQ |
| Cloud Scheduler | GCP | 標準5フィールド | Pub/Sub/HTTP/App Engine連携 |
| Logic Apps Timer | Azure | 6フィールド(秒あり) | ビジュアルワークフロー |
| GitHub Actions schedule | GitHub | 標準5フィールド | CI/CDパイプラインと統合 |
| CronJob | Kubernetes | 標準5フィールド | Pod単位の実行、並行制御 |
マネージドサービスの利点
- 単一障害点の排除: サーバーダウンでもジョブが停止しない
- 監視・アラート内蔵: 実行失敗を自動検知
- リトライポリシー: 失敗時の自動再実行
- スケーラビリティ: 同時実行数の制御
- 監査ログ: 実行履歴の自動記録
従来のcronが適切なケース
- 単一サーバーの簡易タスク(ログローテーション、tmpクリーニング)
- 外部依存を最小限にしたい場合
- セットアップの容易さを優先する場合
- コストを抑えたい小規模環境
人間が間違えやすいパターンTOP5
cronを使い始めた多くのエンジニアが一度はハマる、典型的な間違いパターンを紹介します。
第1位: 「毎日実行」のつもりで毎分実行
# 間違い: これは「毎分」実行される!
* * * * *
# 正しい: 毎日0:00に実行
0 0 * * *
*は「全ての値」を意味します。全フィールドが*だと「毎分の、毎時の、毎日の、毎月の、全曜日」= 毎分実行です。
第2位: 曜日の「7」は日曜?
# 0 0 * * 7 — 日曜日のはず...だが
# POSIX: 0=日曜、7は範囲外(未定義)
# Vixie Cron: 0も7も日曜として受け付ける
# 安全策: 日曜は「0」を使う
環境によって動作が異なるため、日曜は常に0で書くのが安全です。
第3位: 「第一月曜日」のつもりがOR結合
# 間違い: 「1〜7日の月曜」のつもり
0 9 1-7 * 1
# 実際: 「1〜7日」OR「全ての月曜日」に実行される
# 結果: 月曜は毎週実行 + 1〜7日は曜日に関係なく実行
# 正しい方法: スクリプトで判定
0 9 1-7 * * [ "$(date +\%u)" = "1" ] && /path/to/script.sh
第4位: ステップ指定の境界
# */30 9-17 * * * の動作
# 実行される時刻: 9:00, 9:30, 10:00, 10:30, ..., 17:00, 17:30
# 注意: 17:30も実行される(17時台の*/30は0分と30分)
# もし17:00までにしたい場合:
0,30 9-16 * * *
0 17 * * *
第5位: 「毎月末」は標準cronで表現不可
# 間違い: 31日がない月はスキップされる
0 0 31 * *
# → 2月、4月、6月、9月、11月は実行されない!
# 間違い: 28-31日全てで実行される
0 0 28-31 * *
# 正しい方法: 翌日が1日かどうかを判定
0 0 28-31 * * [ "$(date -d tomorrow +\%d)" = "01" ] && /path/to/script.sh
crontab管理のベストプラクティス
運用で問題を起こさないために、以下のプラクティスを推奨します。
1. バージョン管理する
# crontabをファイルとして管理
crontab -l > ~/crontab.bak
# gitで管理
git add crontab.txt
git commit -m "Add daily backup job"
# 復元
crontab crontab.txt
2. コメントを必ず書く
# [2026-01-15] DBバックアップ (担当: 田中)
0 2 * * * /opt/scripts/db-backup.sh >> /var/log/db-backup.log 2>&1
# [2026-03-01] 日次レポート生成 (担当: 鈴木)
0 8 * * 1-5 /opt/scripts/daily-report.sh >> /var/log/report.log 2>&1
3. 出力は必ずリダイレクトする
リダイレクトしないと、出力がメールとして蓄積されディスクを圧迫します。
4. flockで二重起動を防止する
# 前回の実行が終わっていなければスキップ
* * * * * /usr/bin/flock -n /tmp/myjob.lock /path/to/long-job.sh
flock -nはロック取得できなければ即座に終了します。長時間実行ジョブが重複しないことを保証します。
5. Dead Man's Switchで「実行されなかったこと」を検知
cronが実行された証拠を外部に送り、送信が途絶えたらアラートを出す仕組みです:
- Healthchecks.io — 無料枠あり、ジョブ完了時にURLにpingを送る
- Cronitor — エンタープライズ向け監視
- 自前の実装: ジョブ末尾で監視APIにPOST、一定時間POSTがなければアラート
6. 本サイトのCron式ジェネレーターで視覚確認
式を手書きしたら、Cron式ジェネレーターツールに入力して「次回の実行予定時刻」を確認しましょう。人間の解釈とコンピューターの解釈が一致していることを必ず確かめてください。
まとめ — 40年の設計思想が教えてくれること
cronの5フィールド形式は、1979年の誕生から40年以上、ほぼ変わらない文法で使われ続けています。これはUNIX哲学「一つのことをうまくやれ」の体現です。
cron式が長寿命な理由
- シンプルさ: 5つのフィールドで95%のスケジューリング要件をカバー
- 可読性: 慣れれば人間が一目で意味を把握できる
- ポータビリティ: Linux, macOS, クラウド、CI/CDどこでも同じ文法
- 拡張性: 残り5%は外部ロジック(スクリプト内の条件分岐)で対応
実践のポイント
- 日と曜日の「OR結合」を常に意識する
- タイムゾーンは明示的に設定する(できればUTC)
- PATHと実行環境の違いを忘れない
- 式を書いたら必ずツールで次回実行時刻を確認する
- ログ、排他制御、監視の三点セットで運用する
cron式を正確に理解し、罠を回避できれば、あらゆるプラットフォームでの定期実行タスクを自信を持って設定できます。まずは本サイトのCron式ジェネレーターで、実際に式を組み立てながら感覚を掴んでみてください。
記事作成に関する注記
本記事は AI(大規模言語モデル)を編集補助として活用して作成しています。 公開前に編集者が内容を確認していますが、事実誤認・仕様の解釈ミス・最新情報との齟齬が含まれる可能性があります。 重要な判断を行う際は、本文中の一次ソースや公式ドキュメントを必ずご自身でご確認ください。 誤りにお気づきの場合は、お問い合わせフォームよりご連絡いただけると助かります。


