systemdユニットファイルを1行ずつ解剖

起動しない・再起動されないといった障害を自力で切り分けられるようになる。AfterとRequiresの違い、Type=の選び方、enableの実体まで、.serviceファイルの各行を根拠から理解する。

応用設定ファイルsystemdLinuxインフラ最終更新: 2026-07-28
3つの要点
TL;DR
  1. After=が決めるのは起動順序だけで、依存関係は作らない。依存先を確実に起動させるにはRequires=かWants=の併記が必要で、実務では障害の連鎖を避けられるWants=とAfter=の組み合わせが基本形になる。
  2. Type=はプロセスモデルの宣言だ。フォアグラウンドで動き続けるならsimpleかexec、自らデーモン化するならforkingとPIDFile=、一度で終わる処理ならoneshot、準備完了を自己申告できる実装ならnotifyを選ぶ。
  3. systemctl enableの実体は、WantedBy=で指定したターゲットの.wants/ディレクトリへシンボリックリンクを張る操作にすぎない。反映は次回起動からで、いますぐ動かすにはstartかenable --nowを使う。

この設定ファイルは何者か

systemdユニットファイルは、現代の主要Linuxディストリビューションが採用するサービスマネージャーsystemdに対して、「何を・いつ・どのように起動し、どう監視するか」を宣言する設定ファイルだ。中でも拡張子が.serviceのユニットはWebアプリやデーモンといったプロセスの管理を担い、インフラ運用で書く頻度が最も高い。カーネル起動後にユーザー空間のサービス群を立ち上げていく仕組み全体はOSの領分だが、その主役がこのファイル群である。

書式はINI風で、[Unit][Service][Install]の3セクションが基本骨格だ。配置場所には優先順位があり、パッケージ付属のユニットは/usr/lib/systemd/systemに、管理者が自作するものは/etc/systemd/systemに置く。同名のユニットは/etc側が優先されるため、自作の定義や上書きは/etc配下に置くのが原則だ。

横にスクロール

systemdユニットの優先配置とdrop-inから起動状態とログを検証する手順
採用された定義をsystemctl catで確定し、依存順序・MainPID・実サービスの応答を照合する。

1行ずつ解剖

題材として、Node.js製のWebアプリをデータベースと組み合わせて動かす、現実的な.serviceファイルを用意した。

[Unit]
Description=Example web application
Wants=network-online.target
After=network-online.target postgresql.service
Requires=postgresql.service

[Service]
Type=simple
User=webapp
Group=webapp
WorkingDirectory=/opt/webapp
Environment=NODE_ENV=production
EnvironmentFile=-/etc/webapp/env
ExecStart=/usr/bin/node /opt/webapp/server.js
Restart=on-failure
RestartSec=5
NoNewPrivileges=true
PrivateTmp=true

[Install]
WantedBy=multi-user.target

[Unit]:起動順序と依存関係

Description=は、systemctl statusやジャーナルに表示される人間向けの説明文だ。飾りではなく、障害対応中の自分への伝言だと思って具体的に書く。

このセクションの本丸はAfter=Requires=Wants=の使い分けである。最重要の事実はこうだ。After=が決めるのは起動順序だけで、依存関係は一切作らない。After=postgresql.serviceと書いても、PostgreSQLを起動対象に加える効果はなく、「両方が起動対象になった場合に、どちらを先にするか」を決めるだけだ。逆にRequires=とWants=は「一緒に起動対象へ入れる」ことだけを宣言し、順序には関与しない。順序と依存は直交する別の概念なので、通常はセットで書く。

ディレクティブ決めること相手が失敗・停止したとき
After=順序のみ(相手の後に自分を起動)何も起きない(依存ではない)
Requires=強い依存(相手も起動対象になる)自分も起動しない・道連れで停止
Wants=弱い依存(相手も起動対象になる)自分は構わず起動を続ける

サンプルではデータベースをRequires=After=で強く結び、ネットワーク到達性を示すnetwork-online.targetをWants=After=で緩く結んだ。Requires=は相手が明示的に停止されると自分も停止する強い結合であり、乱用すると障害が連鎖する。実務の既定はWants=とし、「それなしでは1秒も動けない」相手にだけRequires=を使うのが定石だ。なお「相手の起動失敗で自分を止める」挙動は、After=で順序を固定して初めて確実に働く点も覚えておきたい。

After=は依存を作らない

After=だけを書いたユニットは、依存先が起動していない環境でも順序の保証だけを受けて平然と走り出し、接続エラーで倒れる。順序(After=)と依存(Wants=かRequires=)は必ず別々に宣言する。

[Service]:プロセスの起動と監視

Type=は、プロセスがどう振る舞うかをsystemdへ伝える宣言で、実質的には「起動完了をいつとみなすか」を決めている。ここがずれると、準備できていないのに後続ユニットが走り出したり、正常なのに起動失敗と誤判定されたりする。

Type想定するプロセス像起動完了とみなす瞬間
simpleフォアグラウンドで動き続けるメインプロセスのfork直後
execフォアグラウンドで動き続ける実行ファイルのexec成功時
forking自らforkして親が終了する旧来型デーモン親プロセスの終了時
oneshot処理を終えて終了するコマンドプロセスの終了時
notifysd_notifyで合図できる実装READY=1の通知受信時

simpleはforkの直後を成功とみなすため、実行ファイルのパスが間違っていても一瞬「起動成功」に見える。execはexecの成否まで確認するので、パス誤りや権限不備を起動時点で検出できる。フォアグラウンドで動く現代的なアプリはsimpleかexec、initスクリプト時代の自前デーモン化を行うソフトだけがforkingを選び、その場合はPIDFile=で主プロセスを明示する。oneshotはマイグレーションのような一度きりの処理向け、notifyは準備完了を自己申告できる行儀のよい実装向けだ。

ExecStart=は起動コマンド本体で、実行ファイルは絶対パスで書くのが基本だ。シェルを介さずに実行されるため、パイプやリダイレクトはそのままでは使えない。

Restart=on-failureは、非0の終了コード・シグナルによる異常終了・タイムアウトのときだけ再起動する設定だ。終了コード0の正常終了では再起動しないため、意図的な停止を邪魔せずにクラッシュだけを拾える。RestartSec=5は再起動までの待ち時間で、デフォルトは100ミリ秒。接続先の回復を待つ意味でも数秒に伸ばすと暴れにくい。

User=Group=は実行ユーザーの指定だ。何も書かなければシステムユニットはrootで動くため、専用の非rootユーザーを切って侵害時の被害を局所化する。

Environment=は環境変数の直接指定、EnvironmentFile=はKEY=VALUE形式のファイル読み込みで、パス先頭の-はファイルが無くてもエラーにしない印だ。秘密情報はユニットファイルへ直書きせず、権限を絞った別ファイルへ逃がす。WorkingDirectory=はカレントディレクトリを固定し、相対パスで資産を読むアプリの事故を防ぐ。

末尾のNoNewPrivileges=truePrivateTmp=trueはハードニング系ディレクティブの入口だ。前者はsetuidなどを通じた権限昇格を子プロセスまで含めて禁止し、後者は/tmpをサービス専用に隔離する。ProtectSystem=やProtectHome=など同系統は多数あり、systemd-analyze securityで露出度を採点できる。

[Install]:enableの正体

WantedBy=multi-user.targetは、「このサービスは、サーバーの通常運転状態であるmulti-user.targetに望まれる一員だ」という宣言である。重要なのは、[Install]セクションが起動中のsystemdからは読まれず、systemctl enableとdisableの実行時にだけ参照される点だ。

systemctl enableの実体は、シンボリックリンクの作成にすぎない。enableすると/etc/systemd/system/multi-user.target.wants/配下にユニットファイル本体を指すリンクが張られ、次回起動時にmulti-user.targetがこのディレクトリを読んで、リンクされたユニットを一緒に立ち上げる。disableはこのリンクを消すだけで、ファイル本体には触れない。隠しフラグではなくただのファイル操作なので、lsを叩けば有効化状態を検算できる。

つまずきやすい点

第一に、After=だけ書いて依存のつもりになる誤り。After=は順序のみで、依存先を起動対象に加えるのはWants=かRequires=の仕事だ。単体起動やリブート時にだけ露見するため、原因に気づきにくい。

第二に、daemon-reloadの忘れ。ユニットファイルを編集しても、systemctl daemon-reloadを実行するまでsystemdはメモリ上の古い定義を使い続ける。「直したのに挙動が変わらない」の大半はこれだ。

第三に、Type=forkingでPIDFile=を書かないケース。親プロセス終了後の主プロセスをsystemdが推測する羽目になり、監視対象を取り違えかねない。そもそもフォアグラウンド実行できるアプリにforkingを選ぶ理由はない。

第四に、enableとstartの混同。enableは次回起動からの自動起動を仕込むだけで、いまは何も起動しない。逆にstartは今すぐ起動するが再起動後には残らない。両方まとめて行うにはsystemctl enable --nowを使う。

まとめ

.serviceファイルは、順序と依存([Unit])、プロセスモデルと監視([Service])、自動起動の配線([Install])という3層の宣言だ。とりわけ「After=は順序のみ」「enableの実体はシンボリックリンク」という2つの事実を押さえるだけで、起動トラブルの切り分けは劇的に速くなる。サービス化はデプロイ自動化の土台としてDevOpsとも地続きなので、同シリーズのssh_configの解剖とあわせて、設定ファイルを根拠から読む習慣を育ててほしい。

設定ファイル解剖の記事ガイド

systemdユニットファイルを1行ずつ解剖を実務で読む

TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。

解決すること

設定ファイル

比較で見る軸

難易度: advanced / カテゴリ: 設定ファイル解剖 / タグ数: 4

導入後に効く点

Type=はプロセスモデルの宣言だ。フォアグラウンドで動き続けるならsimpleかexec、自らデーモン化するならforkingとPIDFile=、一度で終わる処理ならoneshot、準備完了を自己申告できる実装ならnotifyを選ぶ。

先に潰すリスク

用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。

数字・仕様の読み方
難易度
advanced
カテゴリ
設定ファイル解剖
タグ数
4

判断チェックリスト

  • 自社の用途が「設定ファイル / systemd」に近いか確認する。
  • 強みである「After=が決めるのは起動順序だけで、依存関係は作らない。依存先を確実に起動させるにはRequires=かWants=の併記が必要で、実務では障害の連鎖を避けられるWants=とAfter=の組み合わせが基本形になる。」が本当に評価軸になるか確認する。
  • 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
  • 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
  • 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
  • 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。

次に確認する観点

設定ファイルsystemdLinuxインフラ