ブログに戻る

cron vs systemdタイマー:どちらを使うべきか?

2026年9月11日

モダンなdistroでは、自分の所有するものはsystemdタイマーに任せ、cronは1行のユーザージョブと自分で構築していないサーバー用に取っておく。 タイマーは全実行をjournalに記録し、マシンが落ちていた間に逃した予定を取り戻せて(Persistent=true)、システムの他の部品と同じunitファイルで依存関係を組めます。cronの強みは簡潔さ——crontab -e の1行は2つのunitファイルに勝つ——で、最小構成のコンテナや場末のUnixボックスで確実に存在する唯一のスケジューラでもあります。ただし代償があります: cronは静かに失敗する。ジョブが午前3時にエラーを出しても、cronは誰も読まないローカルのメールボックスに投函するだけですが、タイマーなら journalctl -u mytimer.service に全出力が揃っています。以下、実際の違い、それぞれのテスト法、タイマーが発火しない理由、決定表です。

cronとsystemdタイマーの違いは何?

cronはデーモンで、行の表——5つの時間フィールドとコマンド——を読み、壁時計が一致したら各コマンドを実行します。モデルはそれで全部です。ジョブをオブジェクトとして扱う概念がありません: unitなし、ステータスなし、依存関係なし、自分のログエントリなし。

systemdタイマーはunitファイル(foo.timer)で、スケジュールが一致したら別のunitファイル(foo.service)を発火させます。ジョブは第一級のオブジェクトで、他のサービスと同じく startstatuslogs、失敗の追跡を持ちます。スケジューリングはカレンダーベース(cron風)かモノトニック(OnBootSec=15min、壁時計の変更に惑わされない)のどちらかです。

実用上の帰結は次の通りです:

  • ログ: タイマーはstdout/stderrをunitごとにjournalへ記録。cronはせいぜいローカルユーザーにメール。
  • 取りこぼし: Persistent=true のタイマーは予定を逃していれば起動時に1回実行。cronはただスキップ。
  • 依存関係: タイマーは network-online.target やマウントポイントを待てる。cronはスクリプト内でリトライを手書きするしかない。
  • 構文: cronは1行。タイマーは2ファイル。乗り換えコストはそれだけです。

スケジュール構文はcrontabとOnCalendar、どちらが楽?

cronの5フィールドはコンパクトな現職です: */15 * * * * は15分ごとで、大半の管理者は寝ていても読めます。systemdの OnCalendar= は冗長ですが表現力は厳密に上で、systemd-analyze calendar は保存前に次回実行を教えてくれます——cronには相当するdry-runがありません。

# cron: 毎日03:30
30 3 * * * /usr/local/bin/backup.sh

# systemd: 同じスケジュール、保存前に検証できる
systemd-analyze calendar "*-*-* 03:30:00"
# -> 次回実行: Fri 2026-09-11 03:30:00 ...

OnCalendar はcronではきれいに書けないものを処理できます: Mon..Fri *-*-* 09..17:00:00(平日の営業時間帯)や、*:0/15RandomizedDelaySec=10m を合わせて1000台のマシンが同じ瞬間にサーバーを叩かないようにする、などです。

待たずにsystemdタイマーをテストするには?

3コマンドで全部答えが出ます。予定と次回発火を一覧し、タイマーと同じ形でジョブを手動実行し、ログを読みます:

systemctl list-timers --all                 # 全タイマーの次回 + 前回実行
sudo systemctl start backup.service         # ジョブを今発火、タイマーと同じunit
journalctl -u backup.service -f             # 出力をライブで監視

分割に注意: systemctl start backup.timer はスケジュールを武装するだけで、サービスがジョブ本体です。list-timers にタイマーが載り、systemctl status backup.service が緑で、journalに出力があれば、鎖の全体が機能しています。

cronジョブを手動で実行するには?

cronジョブは削ぎ落とされた環境で実行されます。「自分のシェルでは動くのにcronでは動かない」が1ジャンルのバグとして存在するのはこのためです。cronを忠実に再現するには、cronと同じ環境でコマンドを sh 経由で実行します:

crontab -l                                  # 正確な行を確認
env -i SHELL=/bin/sh PATH=/usr/bin:/bin sh -c '/usr/local/bin/backup.sh'
grep CRON /var/log/syslog | tail            # そもそも発火した? (Debian/Ubuntu)
journalctl -u cron -n 20                    # systemd distroならこちら

この env -i の行が誠実な手動テストです: cronのデフォルト PATH だけを持った素の環境。サイレントなcron失敗の大半は素の PATH か、コマンド中のクォートされていない %(cronは % を改行として扱う)で、どちらもこの方法で即座に表面化します。

systemdタイマーが発火しないのはなぜ?

ほぼ全ケースを4つの原因が覆います。確認順に:

  1. サービス名とタイマー名が一致していない。 foo.timerfoo.service を発火させます——typoした OnFailure やリネームされたunitがあると、タイマーは「何もない所へ実行」します。systemctl cat foo.timer が狙い先を正確に表示します。
  2. unitファイルを編集したのにreloadしていない。 unitファイルを変えた後は: sudo systemctl daemon-reload && sudo systemctl restart foo.timer。これがないと新しいスケジュールは生きていません。
  3. Persistent=true に期待通りの OnCalendar セマンティクスが付いていない——あるいは systemctl status foo.timer(アイドルでも常にactiveと表示)を見ていて list-timers を見ていない。
  4. 発火先のサービスが即座に落ちている。 タイマーが死んで見えるのはこのためです。journalctl -u foo.service --since -1h が、list-timers が隠すクラッシュを見せてくれます。

ログを見る段になったら、journalctl cheat sheet がこの作業を速くするフィルタ(-u--since-f)を扱っています。

cron vs systemdタイマー:決定表

cronsystemdタイマー
セットアップcrontab 1行unitファイル2つ
ログローカルメール、たぶん未読journal、unitごと
取りこぼし実行(マシン停止中)スキップPersistent=true で1回実行
依存関係・順序なし —— スクリプト内でDIYunit依存をフルに使える
ランダム遅延$RANDOM でDIYRandomizedDelaySec=
スケジュールのテスト/dry-run不可systemd-analyze calendar
どこでも存在はい —— コンテナ、BSD、組み込みsystemd(PID 1)が必要
rootなしのユーザージョブcrontab -esystemd --user ユニット

経験則: 自分のsystemdマシンにジョブを載せる → タイマー。手早い個人リマインダーや自分で組んでいないボックス → cron。依存関係、リトライ、実際に走ったかを知る必要のあるもの → 常にタイマー。定番パターンはタイマーで保守ジョブを走らせる形——例えば夜間の rsync backup ——で、Persistent=true は予定の瞬間にマシンが眠っていてもバックアップが起こることを保証します。cronにはこれは単純にできません。

— mrsaynothing

Get the next one by email

One email per post. No spam, no algorithms.

self-hosted · no third parties · one-click unsubscribe

what is this?

llama.cpp vs Ollama:2026年、どちらを動かすべきか?

記事を楽しんでいただけましたか?私の本業はこのような構築です。 採用のご相談