Linuxサーバーで「毎日深夜2時にバックアップを実行する」「毎週月曜朝9時にレポートを送信する」といった定期処理を設定するとき、欠かせないのが CronCron式 です。

本記事では、Cron式の5フィールドの意味から、よく使うパターン、GitHub ActionsやAWS Lambda・Laravelでの活用方法、デバッグのコツまでを実践的に解説します。

Cronとは?

Cron(クロン)はUnix/Linux系OSに標準搭載された時間ベースのジョブスケジューラーです。バックグラウンドで常駐する cron デーモンが、crontab(cron table)と呼ばれる設定ファイルを定期的に読み込み、指定時刻になるとコマンドを自動実行します。

  • バックアップ処理:毎日深夜にDBをダンプしてS3へ転送
  • メール送信:毎朝9時に日次レポートをメール配信
  • ログローテーション:週次でログを圧縮・アーカイブ
  • キャッシュ更新:30分ごとに集計データを再生成
  • サイト監視:5分ごとにヘルスチェックエンドポイントをping

Cron式の構文

Cron式は空白区切りの 5つのフィールド で構成されます:

分(0-59)  時(0-23)  日(1-31)  月(1-12)  曜日(0-7)
  *          *          *          *         *
フィールド範囲備考
0〜590が毎時0分(正時)
0〜230が深夜0時
1〜31月によって最大日が異なる
1〜12(またはJan〜Dec)
曜日0〜7(0と7が日曜)1=月曜, 5=金曜, 6=土曜

特殊記号

記号意味
*すべての値* * * * *(毎分)
*/nn間隔ごと*/5 * * * *(5分ごと)
a-ba〜bの範囲1-5(月〜金)
a,b,cリスト指定0,15,30,45(15分ごと)
a-b/n範囲をn間隔で0-12/2(0〜12時を2時間ごと)

よく使うCron式パターン集

基本パターン

# 毎分実行
* * * * *

# 毎5分ごと
*/5 * * * *

# 毎15分ごと
0,15,30,45 * * * *   # または */15 * * * *

# 毎時0分(1時間に1回)
0 * * * *

# 毎日深夜0時
0 0 * * *

# 毎日午前2時(バックアップ等)
0 2 * * *

# 毎週月曜日の朝9時
0 9 * * 1

# 毎月1日の0時(月次バッチ)
0 0 1 * *

# 毎年1月1日の0時
0 0 1 1 *

ビジネスシーン向けパターン

# 平日(月〜金)の朝9時
0 9 * * 1-5

# 平日の業務時間内(9時〜18時)、毎時実行
0 9-18 * * 1-5

# 平日深夜2時のバッチ処理
0 2 * * 1-5

# 金曜日の18時(週次レポート)
0 18 * * 5

# 毎月第1営業日っぽい代替(毎月1〜5日の月曜)
# ※Cronのみでは第1月曜は直接指定できない
0 9 1-7 * 1

注意が必要なパターン

# 「日」と「曜日」を両方指定するとOR動作(ANDにはならない)
# ↓ 毎月15日 OR 毎週日曜(どちらかに一致すれば実行)
0 0 15 * 0

多くのCronシステムでは、日フィールドと曜日フィールドの両方に * 以外を指定すると、ANDではなくORで動作します。「毎月15日かつ日曜のみ」とはならないので注意してください。

crontabの使い方

基本コマンド

# crontabを編集(エディタが開く)
crontab -e

# 現在のcrontabを表示
crontab -l

# crontabを削除(全削除なので注意)
crontab -r

# 特定ユーザーのcrontabを編集(root権限が必要)
crontab -u username -e

crontabの書き方

# 形式:Cron式 + 実行コマンド(フルパス推奨)
0 2 * * * /usr/bin/python3 /home/user/backup.py

# 出力をログファイルに保存
0 2 * * * /usr/bin/backup.sh >> /var/log/backup.log 2>&1

# 環境変数の設定(先頭行で定義)
MAILTO=admin@example.com
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin
0 2 * * * /usr/local/bin/myapp --backup

フルパスを使う理由:crontabの実行環境は通常のシェルよりも少ない環境変数しか持ちません。python3 だけでなく /usr/bin/python3 のようにフルパスを指定することで「コマンドが見つからない」エラーを防げます。

システム全体のcron(/etc/cron.d)

Linuxには /etc/cron.daily//etc/cron.weekly//etc/cron.hourly/ のようなディレクトリがあり、スクリプトを置くだけで自動実行されます。/etc/cron.d/ には実行ユーザーを明示する必要があります:

# /etc/cron.d/myapp の形式(ユーザー名フィールドが必要)
0 2 * * * root /usr/local/bin/myapp-backup

クラウド・各種サービスでのCron設定

GitHub Actions

GitHub Actionsのワークフローで on.schedule にCron式を記述します。タイムゾーンはUTC固定です。

on:
  schedule:
    - cron: '0 0 * * *'   # UTC 0:00 = JST 9:00

jobs:
  nightly:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: ./scripts/nightly.sh

日本時間で動かしたい場合は、JSTからUTCに9時間引いた値を設定してください。JST 9:00 → UTC 0:00、JST 18:00 → UTC 9:00 です。

AWS EventBridge(旧CloudWatch Events)

AWS EventBridgeのスケジュールルールでは2種類の記法があります:

# Rate式(シンプルな繰り返し)
rate(5 minutes)
rate(1 hour)
rate(7 days)

# Cron式(6フィールド形式 — 年フィールドが追加)
# cron(分 時 日 月 曜日 年)
cron(0 2 * * ? *)     # 毎日UTC 2:00(?は必須、日と曜日のどちらかに指定)
cron(0 9 ? * MON-FRI *) # 平日UTC 9:00

AWSのCron式は標準の5フィールドと異なり、6フィールド(年追加)で日と曜日に ?(未指定)が使えます。

Laravel タスクスケジューラー

LaravelではCrondが1分ごとに php artisan schedule:run を呼び出し、その中でスケジュール定義を評価します。

# まずcrontabに1行だけ追加
* * * * * cd /path/to/project && php artisan schedule:run >> /dev/null 2>&1
// app/Console/Kernel.php
protected function schedule(Schedule $schedule)
{
    $schedule->command('backup:run')->dailyAt('02:00');
    $schedule->command('report:weekly')->weeklyOn(5, '18:00'); // 金曜18時
    $schedule->job(new ProcessQueue)->everyFiveMinutes();
    $schedule->cron('0 9 * * 1-5')->command('send:digest'); // カスタムCron式
}

Kubernetes CronJob

apiVersion: batch/v1
kind: CronJob
metadata:
  name: backup-job
spec:
  schedule: "0 2 * * *"
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: backup
            image: my-backup:latest
          restartPolicy: OnFailure

タイムゾーンの注意点

Cronが実行される時刻はサーバーのタイムゾーン設定に依存します。クラウドサーバーはUTCで動作していることが多く、意図した時刻に実行されないミスがよく起きます。

# タイムゾーンを確認
date
timedatectl

# crontabでタイムゾーンを指定(GNU cronの場合)
CRON_TZ=Asia/Tokyo
0 9 * * * /usr/bin/send-digest.sh

本番環境では、Cronの実行時刻と想定タイムゾーンをチームで明記しておくことを強くお勧めします。特にGitHub Actions(UTC固定)やAWS Lambda(UTC固定)は日本時間と9時間ずれます。

デバッグのコツ

Cronが実行されない原因チェックリスト

  • パスの問題:コマンドのフルパスを使っているか確認。which python3 でパスを調べる
  • 実行権限:スクリプトに実行権限があるか(chmod +x script.sh
  • 改行コード:Windowsで作成したスクリプトの \r\n 改行が原因で失敗することがある(dos2unix で変換)
  • ログ確認:/var/log/syslog または /var/log/cron にcronの実行ログが残る
  • メール確認:MAILTOを設定するとエラー出力がメールで届く
  • 環境変数:cronの環境は最小限なのでスクリプト内で source ~/.bashrc や必要な変数を明示的に読み込む

ログに出力して確認

# 標準出力と標準エラーをログに追記
0 2 * * * /usr/bin/backup.sh >> /var/log/backup.log 2>&1

# システムのcronログを確認
grep CRON /var/log/syslog | tail -20

# journalctlでcronのログを確認(systemd環境)
journalctl -u cron --since today

よくある質問(FAQ)

Cron式で月末最終日を指定できますか?
標準の5フィールドCronでは月末最終日を直接指定できません。代替として「28〜31日」で設定し、スクリプト側で最終日判定するか、last 記号に対応したVixie Cron拡張版を使う方法があります。
*/1 と * の違いは何ですか?
どちらも同じ意味で「すべての値」を指します。*/1 は「1間隔ごとのすべて」なので * と等価です。可読性のために * を使う方が一般的です。
Cronで30秒ごとの実行は可能ですか?
標準のCronは最小単位が1分です。30秒ごとに実行したい場合は、1分ごとのCronで実行し、スクリプト内で sleep 30 && command を呼ぶ方法か、Systemd timersやより高頻度のスケジューラーを使う方法があります。
crontab -r を誤って実行してしまいました。復元できますか?
残念ながら、crontab -r で削除したcrontabはバックアップがない限り復元できません。定期的に crontab -l > ~/crontab.bak でバックアップしておくことをお勧めします。
GitHub Actionsのcronは最小どのくらいの頻度で動かせますか?
GitHub Actionsのスケジュールは最短5分間隔(*/5 * * * *)が目安です。ただしGitHub側の負荷により、指定した時刻より数分遅れて実行されることがあります。高頻度・精密な実行が必要な場合は自前のサーバーでcronを使う方が確実です。

まとめ

  • Cron式は「分 時 日 月 曜日」の5フィールド。**/na-ba,b,c の4種の記号で細かく指定できる。
  • crontabではコマンドのフルパスを使い、出力をログファイルにリダイレクトする習慣をつける。
  • GitHub Actions・AWS EventBridge・Laravelなど各プラットフォームで微妙に記法が異なる点に注意。
  • タイムゾーンは必ず確認——クラウドサーバーはUTCで動作しており、JST(UTC+9)とは9時間ずれる。
  • 日フィールドと曜日フィールドを同時に指定するとOR動作になる。
  • CronジェネレーターでGUI操作でCron式を作成し、Cronパーサーで既存の式を解読できます。