systemd timer による定期実行
近年のディストリビューションでは cron の代わりに systemd の timer ユニットで定期実行を組む方法が広く使われます。timer は実行する処理を書いた service ユニットと、起動タイミングを書いた timer ユニットの2つで構成します。cron に比べてログが journal に統合され、依存関係の指定や、電源オフで逃した実行を後から補う Persistent 設定ができる点が利点です。systemctl list-timers で次回起動の予定を一覧できます。
定期実行は cron だけではない。近年の主要なディストリビューションでは、起動処理やサービス管理を一手に担う systemd が標準になっている。この systemd にも定期実行のしくみが備わっている。
cron の代わりに timer ユニットで定期処理を組む方法が、広く使われるようになった。考え方は cron と同じ「決めたタイミングで処理を動かす」だ。
違いは、systemd の世界に統合される点にある。これにより、ログの一元管理・依存関係の指定・逃した実行の補完といった、素の cron には無い利点が得られる。
新しくサーバを組むなら、定期実行は cron にするか systemd timer にするか、という選択肢を意識して持っておくと、設計の幅が広がる。
とくに、すでに他のサービスを systemd で管理している環境なら、定期処理も timer に寄せると運用の見え方がそろって扱いやすくなる。
🧩 なぜファイルを2つに分けるのか
systemd timer の最大の特徴は、「何を実行するか」と「いつ実行するか」を別々のユニットファイルに分けて書く点だ。
前者を service ユニット(拡張子 .service)、後者を timer ユニット(拡張子 .timer)と呼ぶ。この2つをペアで用意する。
原則として同じ名前にそろえる。たとえば backup.service と backup.timer のように対応させると、timer 側が時間になったとき自動的に同名の service を起動してくれる。
これらのファイルは、システム全体用なら /etc/systemd/system/ に、ユーザ個人用なら ~/.config/systemd/user/ に置く。
service には実際の処理を ExecStart=/usr/local/bin/backup.sh のように書く。一度だけ走って終わる処理なので Type=oneshot を指定するのが定番だ。
なぜ2つに分けるのか。役割が2つのファイルに分かれることで、「実行内容の修正」と「スケジュールの変更」を独立して扱えるのが利点なのだ。
ファイルを新規に置いたり書き換えたりした後は、systemctl daemon-reload で systemd に読み直させる必要がある点も覚えておこう。
🗓 スケジュールは、どう書けば読み返しやすいのか
timer ユニットでスケジュールを書く中心が OnCalendar= だ。書式は 曜日 年-月-日 時:分:秒 の並びで、省略した部分や * の部分は「毎」として扱われる。
たとえば OnCalendar=*-*-* 03:00:00 は毎日午前3時。OnCalendar=Mon..Fri 09:00 は平日の午前9時。OnCalendar=*-*-01 00:00:00 は毎月1日の午前0時を表す。
daily・weekly・monthly・hourly といった分かりやすい短縮名も使える。OnCalendar=daily は毎日午前0時、OnCalendar=hourly は毎時0分の意味になる。
複数の時刻を指定したいときは行を複数並べることもできる。
書いた式が狙いどおりに解釈されるかは、systemd-analyze calendar \"Mon..Fri 09:00\" のように検証できる。その式が次に該当する日時まで表示してくれるので、登録前の確認に重宝する。
cron 式が暗号的に見えるのに比べ、OnCalendar は曜日や日付が言葉に近く書ける。後から読み返したときに意図が分かりやすいのも利点だ。
🔁 一定間隔の繰り返しはどう書くか
cron の */15 のような「一定間隔の繰り返し」は、OnCalendar では書きにくい。これにはどうするか。
OnCalendar 以外に、基準時刻からの相対指定もある。OnBootSec= はシステム起動からの経過時間で、OnUnitActiveSec= はそのユニットが最後に動いてからの経過時間でトリガーする。
OnUnitActiveSec=15min なら「前回実行から15分後」に繰り返し動く。これで cron の */15 のような一定間隔の繰り返しを表現できる。
起動直後に1回動かしたうえで以後一定間隔で回す、といった組み合わせも、これらを併用すれば書ける。
🚀 ファイルを置いただけでは動かない
ユニットファイルを置くだけでは動かない。timer を有効にして起動する必要がある。
systemctl enable --now backup.timer のように --now を付けると、「次回のシステム起動時からの自動有効化」と「いますぐの起動」を同時に行える。ここで有効化するのは service ではなく timer 側である点に注意してほしい。
では、ちゃんと次が予約されたかをどう確かめるか。登録された timer の状況は systemctl list-timers で一覧できる。NEXT 列に次回起動の予定時刻、LAST 列に前回起動時刻、UNIT 列に対応する timer 名が並ぶので、「ちゃんと次が予約されているか」を目で確認できる。
実行結果のログは、cron のようにメールやファイルへ自分で逃がす必要はない。journal に自動で記録される。
journalctl -u backup.service のように unit を指定すれば、その処理の過去の実行ログだけをまとめて時系列で追える。これが cron に対する大きな使い勝手の差だ。失敗した回も含めて履歴がそろって残るので、原因の切り分けが格段にやりやすくなる。
🔋 電源が落ちていた間の実行は取り戻せるのか
systemd timer がとりわけ強いのが、電源が落ちていた間に逃した実行を後から補える点だ。素の cron は逃した実行を黙って諦める。timer はどうか。
timer ユニットに Persistent=true を設定しておくと、systemd は前回の実行時刻を記録する。もし予定時刻にマシンが停止していて実行を逃した場合、次に起動したときにその分をすぐ実行してくれる。
常時起動でないノートPCや、間欠的に電源を入れるエッジ機器で「電源を入れていなかった日のバックアップが丸ごと飛ぶ」といった事態を防げるわけだ。
これは、逃した実行は黙って諦める素の cron には無い挙動だ。同様の補完を cron 系で得たいときは anacron という別のしくみを併用する必要がある。
まとめると、ログが journal に統合される・依存関係や条件を細かく書ける・Persistent で逃した実行を補える、という3点が systemd timer を選ぶ主な理由だ。
一方で、ちょっとした処理を1行でさっと仕込むだけなら crontab -e のほうが手軽、という住み分けになる。要件が素直なら cron、運用の作り込みや堅牢さを求めるなら systemd timer、と覚えておくと選択に迷わない。