本記事にはアフィリエイト広告(Amazon アソシエイト)が含まれます。
実測日: Thu Aug 20 2026 09:00:00 GMT+0900 (Japan Standard Time)
自作の systemd unit を Ansible で配備し、systemctl start が通って検証も通ったのに、次の再起動で片方だけ起動していなかった。
原因は unit ファイルの 1 行で、しかも配備直後には露見しない種類のものだった。
本記事では、その 1 行が何を引き起こすのか、どう検査すればよいのかを示す。
輪ができる書き方
ファン制御の unit は 2 つある。
方針を決める fan-policy と、その方針に従って回転数のカーブを回す fan-curve で、fan-curve は fan-policy の後に起動する。
どちらも WantedBy=multi-user.target で有効化した。
ここで、fan-policy に「他のサービスが揃ってから動かしたい」というつもりで After=multi-user.target を足した。
依存関係はこうなる。
multi-user.target --(Wants)--> fan-curve
fan-curve --(After)--> fan-policy
fan-policy --(After)--> multi-user.target <-- これが輪を作る
WantedBy=multi-user.target は「multi-user.target が fan-policy を Wants する」という意味で、target 側が fan-policy を引く。
そこに After=multi-user.target を足すと、fan-policy は target の後に起動したいのに、target は fan-policy を含んで完了する、という循環になる。
systemd は輪を見つけると起動ジョブを削除する
起動時のログにはこう出る。
multi-user.target: Found ordering cycle on fan-curve.service/start
multi-user.target: Job fan-curve.service/start deleted to break ordering cycle
systemd は輪を見つけると、どれかのジョブを削除して輪をほどく。
削除されたのは、輪を作った fan-policy ではなく、それに依存する fan-curve の起動ジョブだった。
fan-curve は enabled のまま、一度も起動しない。
質が悪いのは、配備直後は正常に見えることである。
Ansible が systemctl start を打つので unit は active になり、配備後の検証も通る。
露見するのは次の再起動で、そのとき誰も見ていない。
systemd-analyze verify では見つからない
systemd-analyze verify にこの unit を通しても、何も指摘されなかった。
verify は unit ファイルの構文と参照先の存在を見るもので、起動順の循環までは追わない。
代わりに、次の 2 つの検査を配備の役割に組み込んだ。
- After に target が入っていないこと:
systemctl show <unit> --property=Afterの出力にmulti-user.targetが含まれないことを確認する。 - 起動ログに削除の記録が無いこと:
journalctl -b _PID=1にdeleted to break ordering cycleが無いことを確認する。
2 つ目は _PID=1 で絞ることが要る。
絞らずに journal を grep すると、Ansible が実行したコマンドの監査ログ(grep の文字列そのもの)に一致して誤検知する。
同じ 1 行を持つ別の機械
別の SoC を載せた機械の同名の unit も After=multi-user.target を持っており、そちらでも fan-curve が enabled のまま一度も起動していなかった。
同じ理由と考えてよい。
「他が揃ってから」を表したいなら、target ではなく具体的な unit を After に書く。
この記事で扱った機材
Radxa Fogwise AirBox(SOPHON BM1684X)
BM1684X 搭載の小型 AI ボックス。Ubuntu 20.04 ベースの BSP で、glibc の古さが USB アクセラレータの制約になる。
