【systemd】service二重トリガーの罠とtimerの正しい設定

systemd.timerを使用してスクリプトの定期実行をスケジュールした際、「OS再起動時」にスクリプトが勝手に実行されてしまうトラブルに遭遇した。原因は「Serviceユニット側の自動起動設定」にあった。

【systemd】service二重トリガーの罠とtimerの正しい設定

1.概要

Raspberry Pi 4で、月・水・金のAM 4:00に重いメンテナンススクリプトを走らせる設定を行った。
しかし、設定完了後にOSを再起動するたびに、本来の時間ではないにもかかわらずスクリプトが動き出してしまう現象が発生。

Persistent=false を設定しているため、過去の実行漏れを補完しているわけでもない。なぜ起動時に実行されるのか、その背後にはsystemdの「ターゲット」に関する重大な勘違いがあった。


2.結論

従来どおり指定時刻の定期実行を維持しつつ、再起動後のスクリプト実行が期待値どおり 行われなくなった。

修正後の構成では、サービス単体の自動起動を抑制し、タイマーのみがサービスを叩く構造にする。

修正後の maintenance.service

[Install] セクションを完全に排除し、単独での自動起動を防ぐ。

[Unit]
Description=Run maintenance script

[Service]
Type=oneshot
User=root
ExecStart=/usr/bin/bash /home/pi4/maintenance/run.sh
# [Install] セクションは不要

修正後の maintenance.timer

[Unit]
Description=Timer for Maintenance Script

[Timer]
OnCalendar=Mon,Wed,Fri *-*-* 04:00:00
Persistent=false
RandomizedDelaySec=1h

[Install]
WantedBy=timers.target

それぞれのファイルを/etc/systemd/system/ に配置。

# systemd設定ファイル再読み込み
sudo systemctl daemon-reload

# service自体は有効化不要
sudo systemctl disable maintenance.service

# timer側のみ有効化、スケジュール管理を一任する。TimerによりServiceがキックされる
sudo systemctl enable maintenance.timer

3.システム構成

  • Hardware: Raspberry Pi 4 Model B (8GB RAM)
  • OS: Ubuntu Server 24.04.4 LTS
  • Target: メンテナンス用シェルスクリプト(/home/pi4/maintenance/run.sh
  • Schedule: 月・水・金 04:00:00(1時間のランダム遅延あり)

4. トラブルの原因 : Service側の enable が元凶

当初、以下のような設定ミスをしていた。

普段、自作スクリプトやサービスの自動起動を設定するときの手癖で。

ハマりどころ : Serviceユニットに [Install]を書いていた

[Unit]
Description=Run maintenance script

[Service]
Type=oneshot
User=root
ExecStart=/usr/bin/bash /home/pi4/maintenance/run.sh

# NGな例
[Install]
WantedBy=multi-user.target

この記述がある状態で systemctl enable maintenance.service を実行すると、OSが「マルチユーザーモード(通常の稼働状態)」に到達した時点でこのサービスを実行するようリンクが貼られる。

つまり、

  1. Timerユニットによる時間起動
  2. OS起動プロセスによる自動起動

という二重のトリガーが設定されていたのだ。これが再起動時にスクリプトが走っていた正体である。


5. 検証 : 設定変更後の挙動

設定変更後、以下の手順で動作を確認した。

  1. サービスの無効化: sudo systemctl disable maintenance.service
  2. タイマーの有効化: sudo systemctl enable maintenance.timer
  3. テスト
    1. sudo reboot 直後、スクリプトが実行されないこと
    2. 定期実行が行われていること
メンテナンススクリプト完了後に送信されるDiscordメッセージ

結果、再起動直後にスクリプトが走ることはなくなり、タイマーによる待機状態(NEXT 実行待ち)が正しく維持されるようになった。


6. まとめ : タイマー運用のセオリー

systemd.timerを運用する際は、以下の役割分担を徹底すべきだ。

  • Timerユニット(目覚まし時計):
    • enable にする。
    • いつ実行するかを管理する。
  • Serviceユニット(作業員):
    • disable にしておく。
    • 何を、誰の権限で実行するかだけを記述する。
    • [Install] セクションは原則不要。