
cron式の書き方と落とし穴 ― 5フィールド、方言、タイムゾーン
「* * * * *」— たった5つのフィールドで「いつ実行するか」を表現する。長年使われてきた形式ですが、同じ式が全サービスで同じ意味になるわけではありません。この記事は、一般的な5フィールド形式とCronie系の拡張を区別し、実行前に確認したい条件を説明します。
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の代替。独自の OnCalendar 形式を使用(cron 式は systemd-cron 等の別パッケージで変換)
多くのプラットフォームがcron式を採用しています。systemdなど別の文法もあり、各サービスの仕様確認が必要です。
長く使われる5フィールド形式と実装の違い
cronはUNIX系システムで長年使われてきました。POSIXのcrontab仕様は、分・時・日・月・曜日の5つの日時フィールドと、その後ろのコマンドを定義しています。秒フィールドはありません。
Vixie Cronの系統を含む実装では、ユーザー別crontabや環境変数設定、追加記法が使われます。現在のCronieマニュアルで確認できる仕様を、全UNIXや全クラウドへ一般化しないことが大切です。
cron式とcrontabの1行は別物です。ユーザーのcrontabでは5つの日時フィールドの後ろにコマンドを書きます。Cronieのシステムcrontabでは、日時とコマンドの間に実行ユーザーも必要です。以下の表は日時部分だけを示し、コマンド付き例はユーザーcrontab用です。
5フィールドの文法 — POSIXとCronie系の拡張
5フィールドの順序は共通ですが、以下はステップ指定や曜日7を含むCronie系の範囲です。POSIXの曜日は0〜6で、/や月・曜日名はPOSIXの基本文法に含まれません。
┌───────────── 分 (0-59)
│ ┌─────────── 時 (0-23)
│ │ ┌───────── 日 (1-31)
│ │ │ ┌─────── 月 (1-12)
│ │ │ │ ┌───── 曜日 (0-7, 0と7=日曜)
│ │ │ │ │
* * * * *
各フィールドの詳細
表が収まらない場合は、左右にスクロールできます。
| フィールド | 範囲 | 使用可能な特殊文字 | 備考 |
|---|---|---|---|
| 分 | 0-59 | * , - / | |
| 時 | 0-23 | * , - / | 24時間制 |
| 日 | 1-31 | * , - / | 日条件だけでは存在しない日に一致しない(曜日とのOR条件に注意) |
| 月 | 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 * * | 月2回。14日間隔ではない |
| 四半期初日(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フィールド形式だけでなく、環境によって拡張された構文が使われています。
Cronieのショートカット記法
表が収まらない場合は、左右にスクロールできます。
| 記法 | 等価な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回。Cronieでは通常、cronサービスの再起動だけでは再実行しない |
Cronieは実行済みの起動ジョブをロックファイルで管理します。@rebootをサービス再起動のフックとして使わず、他のcron実装へ移す場合も起動条件を確認してください。
6フィールド形式(秒あり)
Java系のスケジューラでは先頭に「秒」フィールドが追加されます。
秒 分 時 日 月 曜日
0 0 9 * * MON-FRI (Spring @Scheduled)
7フィールド形式(秒+年)
Quartzの完全形式では末尾に「年」フィールドが付きます。
秒 分 時 日 月 曜日 年
0 0 9 ? * MON-FRI 2026
各環境の方言比較
表が収まらない場合は、左右にスクロールできます。
| 環境 | フィールド数 | 秒 | 年 | 拡張記号 |
|---|---|---|---|---|
| 標準cron(POSIX) | 5 | なし | なし | なし |
| Cronie系 | 5 | なし | なし | ステップ、@記法 |
| GitHub Actions | 5 | なし | なし | ステップ(@記法は非対応) |
| Kubernetes CronJob | 5 | なし | なし | ステップ、@monthly等、?は*相当 |
| Spring Scheduler | 6 | あり | なし | L, W, #, ? |
| Quartz | 6-7 | あり | 任意 | L, W, #, ? |
| AWS EventBridge Scheduler | 6 | なし | あり | ? L W # |
注意: AWS EventBridgeの「6フィールド」は分 時 日 月 曜日 年の構成で、秒ではなく年が追加されます。?は「値を指定しない」を意味し、日と曜日のどちらか一方に使います。曜日番号も異なり、POSIXは0〜6(日〜土)、Cronieは0と7が日曜、QuartzとEventBridge Schedulerは1〜7(日〜土)です。「日曜は0」は全方言共通の規則ではありません。
日と曜日の「OR結合」問題 — 最大の罠
5フィールドの式で特に注意したいのが、日と曜日の組み合わせです。
仕様: 日と曜日が両方指定されると「OR」で結合される
Vixie Cronの仕様では、日フィールドと曜日フィールドの両方を数値で制限し、どちらも*で始まらない場合、どちらかの条件に合致すれば実行されます。*/nを含め、どちらかが*で始まる場合は両方の一致を要求します。
# 意図: 「13日の金曜日」にだけ実行したい
0 9 13 * 5
# 実際の動作: 「毎月13日」OR「毎週金曜日」に実行される!
つまり、上記の式は以下の日に全て実行されます:
- 1月13日(月曜) — 13日なので実行(2025年の例)
- 1月3日(金曜) — 金曜なので実行
- 1月10日(金曜) — 金曜なので実行
- 1月17日(金曜) — 金曜なので実行
- ... 以下全ての金曜日と13日
なぜOR結合なのか
これはその実装の照合規則です。たとえば 1-31 と * は日フィールド単独では同じ範囲でも、曜日との組み合わせ方が変わります。設計者の意図を推測せず、採用する実装の仕様と照合してください。
「13日の金曜日」だけに実行する方法
以下はCronie系のユーザーcrontabで、毎月13日にコマンド側の曜日判定を加える方法です。スケジュールとコマンドのタイムゾーンを揃えて使います:
# crontab: 毎月13日の9:00に実行
0 9 13 * * [ "$(date +\%u)" = "5" ] && /path/to/script.sh
date +%uで曜日(1=月曜, 5=金曜)を取得します。crontabのコマンド欄では、引用符の中でも % をバックスラッシュでエスケープします。別のシェルスクリプトへ移す場合は、cron用のエスケープを外してください。
タイムゾーンの罠 — DST・UTC・サーバー時刻
cronはデフォルトでシステムのローカルタイムゾーンで動作します。JSTには現在夏時間がありませんが、サーバーと予定のタイムゾーンが違えば実行時刻もずれます。
夏時間(DST)による実行スキップ・二重実行
時計を進めたり戻したりすると、存在しない時刻や重複する時刻が生じます。次の表は壁時計の例で、実行結果は実装に依存します:
表が収まらない場合は、左右にスクロールできます。
| イベント | 影響 | 例 |
|---|---|---|
| 春: 時計が進む | 存在しない時刻が生じる | 2:00→3:00の移行では2:30が存在しない |
| 秋: 時計が戻る | 同じ時刻が重複する | 3:00→2:00の移行では2:30が2回ある。移行時刻は地域で異なる |
Cronieのcron(8)には、3時間未満の時計変更に対する固定時刻ジョブ等の補正が記載され、春の実行補完・秋の重複抑制があります。単純に「必ずスキップ/2回実行」とは言えません。EventBridge Schedulerは春の欠落時刻をスキップし、秋の重複時刻は1回だけ実行します。GitHub Actionsのtimezone指定では春の欠落時刻を次の有効時刻へ進めます。実装・バージョン・対象タイムゾーンで実際の動作を検証してください。
CRON_TZで個別のタイムゾーンを指定する場合: Cronieの実装には、そのzoneの夏時間移行で実行の欠落や重複が起こり得るという制限も記載されています。デーモンの標準タイムゾーンに対する補正を、別zoneにもそのまま期待しないでください。
解決策
- CRON_TZ変数: Cronieなど対応実装ではcrontab内で
CRON_TZ=UTCを指定可能 - サーバー時刻をUTCに設定: UTC基準の業務には有効。現地の毎朝9時を維持したい場合は現地zoneを使う
- DST遷移時間帯を避ける: 移行する時刻は地域ごとに確認する
# crontab内でタイムゾーンを明示
CRON_TZ=Asia/Tokyo
TZ=Asia/Tokyo
0 9 * * * /path/to/job.sh
# または
CRON_TZ=UTC
TZ=UTC
0 0 * * * /path/to/midnight-utc-job.sh
CRON_TZは予定の解釈、TZはdate等の子プロセスが使う時刻を揃えるための設定です。CRON_TZ非対応の実装では同じ効果を期待できません。Kubernetesでは式の中へ書かず、.spec.timeZoneを設定します。
日本(JST)の注意点
日本は夏時間を採用していないため、DST問題は発生しません。ただし以下のケースでは注意が必要です:
- 海外リージョンのクラウドサーバー(UTC設定が多い)で日本時間ベースのジョブを動かす場合
- GitHub Actionsのscheduleは既定でUTCです。
timezoneにIANAタイムゾーンも指定できます。UTCのままJST 9:00を指定する例は0 0 * * *です。最短間隔は5分で、負荷による遅延・実行省略もあります。GitHub公式仕様を確認してください。 - 複数タイムゾーンのユーザー向けサービスでcronを使う場合
実行環境の注意点 — PATHとシェル
「ターミナルでは動くのにcronでは動かない」— 最もよくある初心者のハマりポイントです。
cronの実行環境はログインシェルと異なる
表が収まらない場合は、左右にスクロールできます。
| 項目 | ログインシェル | cron |
|---|---|---|
| PATH | 多数のディレクトリ | 実装・設定に依存(例:/usr/bin:/bin) |
| SHELL | ユーザー設定による | /bin/sh |
| 環境変数 | シェルの起動設定による | HOME・LOGNAME等は設定されるが、対話用の設定は通常読まない |
| ホームディレクトリ | 設定済み | 設定されているが注意 |
対策
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
MAILTOは後続ジョブに適用されます。上の2行は設定候補で、両方書けば後の値が有効です。無出力で失敗するジョブはこれだけでは検知できません。
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連携 |
| GitHub Actions schedule | GitHub | 標準5フィールド | CI/CDパイプラインと統合 |
| CronJob | Kubernetes | 標準5フィールド | Pod単位の実行、並行制御 |
マネージドサービスの利点
- スケジューラの運用負担軽減: 実行先の障害や権限エラーまで解消するわけではない
- 監視・アラート: サービスごとにログ・メトリクス・通知の設定が必要
- 再試行・並行制御: 提供機能と設定はサービスごとに確認。再実行されても結果が壊れない冪等性を設計する
- 運用上限: 同時実行数・API制限・料金を確認する
- 監査ログ: 記録される範囲と保持期間を確認する
Kubernetes CronJobでも、状況によってJobが重複生成されたり生成されなかったりします。cron式は「ちょうど1回の成功」を保証しません。
従来のcronが適切なケース
- 単一サーバーの簡易タスク(ログローテーション、tmpクリーニング)
- 外部依存を最小限にしたい場合
- セットアップの容易さを優先する場合
- コストを抑えたい小規模環境
人間が間違えやすいパターンTOP5
cronを使い始めた多くのエンジニアが一度はハマる、典型的な間違いパターンを紹介します。
第1位: 「毎日実行」のつもりで毎分実行
# 間違い: これは「毎分」実行される!
* * * * *
# 正しい: 毎日0:00に実行
0 0 * * *
*は「全ての値」を意味します。全フィールドが*だと「毎分の、毎時の、毎日の、毎月の、全曜日」= 毎分実行です。
第2位: 曜日の「7」は日曜?
# 0 0 * * 7 — 日曜日のはず...だが
# POSIX: 0=日曜、7は範囲外(未定義)
# Vixie Cron: 0も7も日曜として受け付ける
# POSIX系5フィールドでは日曜に「0」を使う
POSIX系の5フィールドなら日曜は0を使えます。QuartzやEventBridge Schedulerでは日曜は1であり、移植時に番号を確認します。
第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 * * *
*/35 * * * *は毎時0分と35分に一致し、次の時までの間隔は25分です。「起動から35分ごと」ではありません。日フィールドの*/2も各月の1日から選ぶため、連続した48時間間隔にはなりません。
第5位: 「毎月末」は標準cronで表現不可
# 間違い: 31日がない月はスキップされる
0 0 31 * *
# → 2月、4月、6月、9月、11月は実行されない!
# 間違い: 28-31日全てで実行される
0 0 28-31 * *
# GNU dateを使い、予定とコマンドをUTCに統一した例
CRON_TZ=UTC
TZ=UTC
# 翌日が1日かを判定(date -dはmacOS/BSD共通の構文ではない)
0 0 28-31 * * [ "$(date -d tomorrow +\%d)" = "01" ] && /path/to/script.sh
crontab管理のベストプラクティス
運用で問題を起こさないために、以下のプラクティスを推奨します。
1. バージョン管理する
# crontabをファイルとして管理
crontab -l > crontab.txt
# 成功を確認し、既存設定・秘密情報を点検してから登録
# gitで管理
git add crontab.txt
git commit -m "Add daily backup job"
# 内容を確認して復元(現在のユーザーcrontab全体を置換する)
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 /home/user/.local/state/myjob/job.lock /path/to/long-job.sh
Linuxのutil-linux版flockを使う例です。先にジョブ実行ユーザー所有のディレクトリを作り、ロックファイルを途中で削除・置換しないでください。例のパスは実環境へ置き換えます。flock -nはロック取得できなければ即座に終了します。同じロックを共有するプロセス間で、ロックを保持している間の重複実行を防ぎます。ホストやファイルシステムを跨ぐ排他は別途確認が必要です。
5. Dead Man's Switchで「実行されなかったこと」を検知
cronが実行された証拠を外部に送り、送信が途絶えたらアラートを出す仕組みです:
- Healthchecks.io — ジョブ完了の通知を受け取る監視サービス
- Cronitor — ジョブ監視サービス
- 自前の実装: ジョブ末尾で監視APIにPOST、一定時間POSTがなければアラート
6. 本サイトのCron式ジェネレーターで視覚確認
式を手書きしたら、Cron式ジェネレーターツールに入力し「予定を更新」で確認しましょう。本ツールは数値5フィールドの対応範囲だけを扱い、ローカル時刻またはUTC、基準時刻より後の8年以内・最大5件を表示します。表示は自動更新せず、実際のジョブ実行も予約しません。月・曜日名、@記法、L/W/#/?、秒・年フィールドには対応しません。表示予定は本番デーモンのDST補正や再試行を再現するものではなく、最終確認は実行環境で行ってください。
まとめ — 式・タイムゾーン・実行環境を一組で確認する
5フィールド形式は多くの環境で基本概念を共有しますが、実行契約まで同じではありません。式、タイムゾーン、実行環境を一組で管理することが重要です。
cron式が長寿命な理由
- シンプルさ: 5つのフィールドで多くの基本的な日時条件を表現
- 可読性: 慣れれば人間が一目で意味を把握できる
- ポータビリティ: 基本概念を共有しつつ、フィールド数・番号・特殊記号は環境ごとに確認
- 拡張性: 複雑な条件は外部ロジックや実行環境の拡張構文で補完
実践のポイント
- 日と曜日の「OR結合」を常に意識する
- タイムゾーンは業務の基準に合わせ、予定とコマンドで揃える
- PATHと実行環境の違いを忘れない
- 式の予定をツールで確認し、本番実装の仕様とも照合する
- ログ、排他制御、監視の三点セットで運用する
cron式を正確に理解し、罠を回避できれば、移植時の誤設定を減らせます。まずは本サイトのCron式ジェネレーターで、実際に式を組み立てながら感覚を掴んでみてください。
参考文献・ソース
- Cronie implementation: startup and per-job time zones ↗
- Google Cloud Scheduler: cron format ↗
- POSIX crontab ↗
- Cronie crontab(5) ↗
- Cronie cron(8): clock changes ↗
- GitHub Actions: schedule ↗
- Kubernetes CronJob ↗
- EventBridge Scheduler schedule types ↗
- Spring scheduling ↗
- Quartz CronTrigger ↗
- GNU date: relative items ↗
- util-linux flock ↗
記事作成に関する注記
本記事は AI(大規模言語モデル)を編集補助として活用して作成しています。 公開前に編集者が内容を確認していますが、事実誤認・仕様の解釈ミス・最新情報との齟齬が含まれる可能性があります。 重要な判断を行う際は、本文中の一次ソースや公式ドキュメントを必ずご自身でご確認ください。 誤りにお気づきの場合は、お問い合わせフォームよりご連絡いただけると助かります。




