crontabが動かない時のチェックリスト - PATH・環境・ログ
この記事で解決できること
- 手動では動くスクリプトが cron だと動かない 理由が分かる
cronの失敗を ログから確認 する手順が分かる- PATH・環境変数・時刻書式・パーミッションの 定番ハマりどころ を順に潰せる
結論(切り分けの型)
cron が動かない原因はほぼ次の 5 つに収束する。上から順に確認する。
- cron デーモンが動いていない
- ログに実行記録がない(=そもそも起動されていない)
- PATH が違う(対話シェルと cron は別環境)
- 環境変数が無い(
.bashrcは読まれない) - 時刻書式・パーミッション・改行コードのミス
前提(対象環境)
- 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 系は crond。status で Unit 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-%dはdate +\%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 found や Permission denied の生メッセージが残る。
最小再現の型
原因が絞れないときは、まず「1 分ごとに env と日時を吐くだけ」のジョブで cron 自体が動くか確認する。
* * * * * date >> /tmp/cron-test.log 2>&1
このログが増えれば cron は正常。あとはスクリプト側の問題に切り分けられる。