crontabが動かない時のチェックリスト - PATH・環境・ログ

crontabが動かない時のチェックリスト - PATH・環境・ログ

この記事で解決できること

  • 手動では動くスクリプトが cron だと動かない 理由が分かる
  • cron の失敗を ログから確認 する手順が分かる
  • PATH・環境変数・時刻書式・パーミッションの 定番ハマりどころ を順に潰せる

結論(切り分けの型)

cron が動かない原因はほぼ次の 5 つに収束する。上から順に確認する。

  1. cron デーモンが動いていない
  2. ログに実行記録がない(=そもそも起動されていない)
  3. PATH が違う(対話シェルと cron は別環境)
  4. 環境変数が無い.bashrc は読まれない)
  5. 時刻書式・パーミッション・改行コードのミス

前提(対象環境)

  • OS: Ubuntu / Debian 系(パッケージ名 cron、ログは /var/log/syslog
  • RHEL / CentOS 系はサービス名 crond、ログは /var/log/cron
  • ユーザー crontab(crontab -e)を主対象とする

なぜ手動では動くのに cron だと動かないのか?

結論: cron は対話ログインと違い .bashrc / .profile を読まない非対話シェルで、最小 PATH のまま実行する。手動成功・cron 失敗の大半はこの環境差が原因。

手動実行(ログインシェル)と cron 実行では、シェルの起動経路が根本的に違う。

項目 対話ログインシェル cron ジョブ
読み込むファイル .bash_profile / .bashrc なし
PATH フル(/usr/local/bin 等を含む) 最小(多くは /usr/bin:/bin
HOME / LOGNAME 設定済み 一部のみ
カレントディレクトリ 任意 実行ユーザーの $HOME
標準出力 端末 メール送信(または破棄)

この差を理解していれば、以降のチェックは「cron の最小環境で何が欠けているか」を 1 つずつ埋める作業になる。

cron デーモンは動いているか?

結論: まず systemctl status cron でデーモン稼働を確認する。停止していればジョブは一切実行されない。最初に潰すべき大前提。

# Ubuntu / Debian 系
$ systemctl status cron

# RHEL / CentOS 系
$ systemctl status crond

active (running) でなければ起動・有効化する。

$ sudo systemctl enable --now cron

サービス名はディストリで異なる。Ubuntu は cron、RHEL 系は crondstatusUnit cron.service could not be found が出たら、もう一方の名前を試す。

実行されたかをログで確認するには?

結論: cron は起動のたびに syslog へ記録を残す。grep CRON /var/log/syslog にエントリが無ければ、ジョブはそもそも起動されていない。

ログに行があるか無いかで、切り分けの方向が大きく変わる。

# Ubuntu / Debian 系
$ grep CRON /var/log/syslog | tail -20

# systemd journal で見る場合
$ journalctl -u cron --since "1 hour ago"

# RHEL / CentOS 系
$ sudo grep CRON /var/log/cron | tail -20

実行されると次のような行が残る。

Jun  5 10:00:01 host CRON[12345]: (alice) CMD (/home/alice/backup.sh)

判定:

  • ログにエントリがある → cron は起動済み。原因はスクリプト側(PATH / 権限 / 環境変数)。次節以降へ。
  • ログにエントリが無い → そもそも起動されていない。時刻書式の誤り・crontab の設置場所・デーモン停止を疑う。

Ubuntu でログ自体が出ない場合、rsyslog が未導入・停止のことがある(systemctl status rsyslog)。その場合は journalctl -u cron を一次情報として使う。

PATH が原因で動かないのはなぜか?

結論: cron の PATH は最小(多くは /usr/bin:/bin)。/usr/local/bin 等のコマンドは絶対パスで書くか、crontab 先頭で PATH を定義する。

「手動では動くのに cron で command not found」の典型はこれ。対処は 3 通り。

# 1) コマンドを絶対パスで書く(which で確認)
$ which node
/usr/local/bin/node

# crontab では絶対パス指定
* * * * * /usr/local/bin/node /home/alice/job.js
# 2) crontab の先頭で PATH を定義する
PATH=/usr/local/bin:/usr/bin:/bin
0 * * * * node /home/alice/job.js
# 3) スクリプト内で PATH を export してから処理する
#!/bin/bash
export PATH=/usr/local/bin:/usr/bin:/bin
node /home/alice/job.js

実際の cron 環境を採取すると確実。一時的に次の行を仕込み、出力された cronenv を手元環境と diff する。

* * * * * env > /tmp/cronenv 2>&1
$ diff <(env) /tmp/cronenv

環境変数が無いせいで失敗していないか?

結論: cron は .bashrc / .profile を読まない。LANG や言語ランタイムの環境変数(NODE_ENV / JAVA_HOME 等)が未設定で落ちることが多い。

対話シェルで暗黙に設定されていた変数が、cron では空になる。スクリプト側で明示的に定義するのが最も確実。

#!/bin/bash
# cron 環境では未設定になりやすい変数を明示
export LANG=ja_JP.UTF-8
export HOME=/home/alice
export NODE_ENV=production

cd "$HOME/app" || exit 1
/usr/local/bin/node index.js

source ~/.bashrc を cron スクリプト内で呼ぶ回避策は脆い。.bashrc 冒頭の「非対話シェルなら即 return」ガード(Ubuntu 既定)で何も読み込まれないことがある。必要な変数は個別に export する。

時刻指定・書式の間違いをどう見つけるか?

結論: フィールドは「分 時 日 月 曜日」の 5 つ。曜日と日の AND/OR、% のエスケープ漏れが定番のミス。ログに起動記録が無ければまず書式を疑う。

┌── 分 (0-59)
│ ┌── 時 (0-23)
│ │ ┌── 日 (1-31)
│ │ │ ┌── 月 (1-12)
│ │ │ │ ┌── 曜日 (0-7, 0と7は日曜)
│ │ │ │ │
* * * * *  実行するコマンド

つまずきやすい点:

  • % はリテラルにならない: コマンド中の % は cron が改行に変換する。date +%Y-%m-%ddate +\%Y-\%m-\%d とバックスラッシュでエスケープする。
  • 日と曜日の両指定: 0 0 1 * 1(毎月1日 かつ 月曜ではなく)は「1日 または 月曜」の OR で動く。意図とずれやすい。
  • コメントは行末に置けない: * * * * * cmd # メモ は不可。コメントは独立行 # のみ。

書式の意味は crontab.guru で確認すると速い。登録後は crontab -l で意図どおり保存されたか必ず見返す。

パーミッションと crontab の設置場所を確認する

結論: スクリプトに実行権限が無い・shebang のパスが違うと起動直後に失敗する。/etc/cron.d/ 配置時はファイルにユーザー欄が必要な点も忘れやすい。

スクリプトの実行権限と shebang

$ chmod +x /home/alice/backup.sh
$ head -1 /home/alice/backup.sh
#!/bin/bash

Windows で編集したスクリプトは改行コード CRLF が混入し bad interpreter で失敗することがある。詳細は「bad interpreter」エラーの直し方を参照。

crontab の種類で書式が違う

設置場所 ユーザー欄 用途
crontab -e(ユーザー) 無し 個人ジョブ
/etc/crontab 必要 システム全体
/etc/cron.d/<file> 必要 パッケージ等の追加ジョブ
# /etc/cron.d/ や /etc/crontab はユーザー欄が必須
# ┌分 ┌時 ┌日 ┌月 ┌曜 ┌ユーザー  ┌コマンド
  0   3   *   *   *   alice    /home/alice/backup.sh

ユーザー欄の有無を取り違えると、ログに Unauthorized 等のエラーが出るか、無言で実行されない。

出力をリダイレクトしてデバッグするには?

結論: cron は標準出力・標準エラーをメール送信する。MTA が無い環境では出力が消えるため、ファイルへリダイレクトしてエラーを可視化するのが確実。

# 標準出力と標準エラーをログファイルへ
* * * * * /home/alice/backup.sh >> /tmp/backup.log 2>&1

2>&1 で標準エラーも同じファイルに集約する。実行後に /tmp/backup.log を見れば、command not foundPermission denied の生メッセージが残る。

最小再現の型

原因が絞れないときは、まず「1 分ごとに env と日時を吐くだけ」のジョブで cron 自体が動くか確認する。

* * * * * date >> /tmp/cron-test.log 2>&1

このログが増えれば cron は正常。あとはスクリプト側の問題に切り分けられる。

チェックリストまとめ

結論: 「デーモン → ログ → PATH → 環境変数 → 書式 → 権限 → 出力」の順に上から潰せば、cron が動かない原因はほぼ特定できる。

上から順に確認する。

  • [ ] systemctl status cron(RHEL は crond)が active (running)
  • [ ] grep CRON /var/log/syslog に起動エントリがある
  • [ ] コマンドは絶対パス、または crontab 先頭で PATH= を定義
  • [ ] 必要な環境変数(LANG / ランタイム系)をスクリプトで export
  • [ ] 時刻フィールドは 5 つ、% はエスケープ、日と曜日の OR に注意
  • [ ] スクリプトに実行権限、shebang 正常、改行は LF
  • [ ] >> /tmp/job.log 2>&1 でエラーを可視化

次に読む記事: