N2
NanToo
cron式の完全解説 ― 5つの星が生まれた歴史、書き方、そしてハマりどころ
DDEVELOPER
開発10 分で読める

cron式の完全解説 ― 5つの星が生まれた歴史、書き方、そしてハマりどころ

* * * * *」— たった5つのフィールドで「いつ実行するか」を表現する。1979年のUNIX V7で誕生したこの形式が、40年以上経った今もクラウドネイティブ時代のスケジューリング標準であり続けている理由を解き明かす。

#cron#crontab#スケジューラ#Linux#UNIX#定期実行#GitHub Actions#Kubernetes
AD

cronとは何か — 1分の粒度で世界を動かすデーモン

cronはUNIX系OSにおける時刻ベースのジョブスケジューラデーモンです。システム起動時にバックグラウンドで起動し、毎分1回、登録されたスケジュール(crontab)を確認して、条件に合致するコマンドを実行します。

基本的な動作モデル

  • cronデーモンは毎分目覚めて現在時刻を取得する
  • 登録された全エントリのスケジュールと照合する
  • 合致するエントリのコマンドを子プロセスとして実行する
  • 実行結果(stdout/stderr)は設定に従いメール送信またはログ記録

2026年でもcronが現役な理由

コンテナ、サーバーレス、CI/CDが当たり前の時代になっても、cronの概念は至るところに生きています。

  • Kubernetes CronJob — Podをcron式スケジュールで起動
  • GitHub Actionsscheduleトリガーでワークフローを定期実行
  • 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)は、事実上の業界標準となりました。主な改善点:

  • ユーザーごとのcrontabcrontab -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:000 9 * * 1週次レポート等
毎週日曜3:000 3 * * 0週次メンテナンス
毎月1日の0:000 0 1 * *月次処理
毎月1日と15日の9:000 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式意味
@yearly0 0 1 1 *年1回(1/1 0:00)
@monthly0 0 1 * *月1回(1日 0:00)
@weekly0 0 * * 0週1回(日曜 0:00)
@daily0 0 * * *日1回(0:00)
@hourly0 * * * *毎時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 Cron5なしなし@記法
GitHub Actions5なしなしなし
Kubernetes CronJob5なしなしなし
Spring Scheduler6ありなしL, W, #
Quartz6-7あり任意L, W, #, ?
AWS EventBridge6あり(分単位)あり? 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 SchedulerAWS6フィールド(年あり)サーバーレス、リトライ、DLQ
Cloud SchedulerGCP標準5フィールドPub/Sub/HTTP/App Engine連携
Logic Apps TimerAzure6フィールド(秒あり)ビジュアルワークフロー
GitHub Actions scheduleGitHub標準5フィールドCI/CDパイプラインと統合
CronJobKubernetes標準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(大規模言語モデル)を編集補助として活用して作成しています。 公開前に編集者が内容を確認していますが、事実誤認・仕様の解釈ミス・最新情報との齟齬が含まれる可能性があります。 重要な判断を行う際は、本文中の一次ソースや公式ドキュメントを必ずご自身でご確認ください。 誤りにお気づきの場合は、お問い合わせフォームよりご連絡いただけると助かります。

🔧 関連ツール

📚 関連記事

AD