サーバー管理の現場で、こうした状況は珍しくない。特に自作のユニットファイルを書いた直後や、OS を RHEL 8 系から RHEL 9 系へ移行した際によく発生する。
systemd サービスの起動失敗は、正しい手順でログを追えば、ほとんどのケースで原因を特定できる。闇雲に設定を変更するのではなく、「なぜ failed になっているのか」を確認する順序が重要だ。
本記事では、systemd サービスが起動しない・failed になる時の調査手順を、journalctl のログ確認・依存関係のチェック・ユニットファイルの設定修正まで、現場で即使える手順で解説する。
動作確認環境:RHEL 9.4 / Ubuntu 24.04 LTS で確認済み。
この記事のポイント
・systemctl status → journalctl -xe の順で調査すれば原因の9割が特定できる
・failed の多くは ExecStart のパス誤り・実行権限・依存サービスの未起動が原因
・ユニットファイルを編集したら必ず daemon-reload を実行してから再起動する
・依存関係エラーは After/Requires の見直しか、依存先サービスの修正で解消できる
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
systemdサービスがfailedになる主な原因
systemctl status で failed と表示されるケースは、大きく次のカテゴリに分類できる。・ExecStart の設定ミス:実行ファイルのパスが間違っている、または実行権限(+x)がない
・依存関係の問題:起動順序(After/Requires)が正しく設定されていない
・Type 指定のミス:フォアグラウンドで動くのに Type=forking を指定しているなど
・環境変数・設定ファイルの欠落:必要なファイルやディレクトリが存在しない
・タイムアウト:起動に時間がかかりすぎて TimeoutStartSec を超過している
どのカテゴリに該当するか、以下の手順で絞り込んでいく。
基本調査手順:ログから原因を特定する
1. systemctl statusでエラーの種類を把握する
まず systemctl status コマンドで、サービスの状態と直近のログを確認する。-l オプションを付けると、長いエラーメッセージが途中で省略されなくなる。# サービス状態の確認(-l で長いメッセージも省略しない) $ systemctl status myapp.service -l
* myapp.service - My Application Service Loaded: loaded (/etc/systemd/system/myapp.service; enabled; vendor preset: disabled) Active: failed (Result: exit-code) since Fri 2026-07-11 09:23:14 JST; 3min ago Process: 12847 ExecStart=/usr/local/bin/myapp --config /etc/myapp/app.conf (code=exited, status=203/EXEC) Main PID: 12847 (code=exited, status=203/EXEC) CPU: 2ms Jul 11 09:23:14 server01 systemd[1]: myapp.service: Control process exited, code=exited, status=203/EXEC Jul 11 09:23:14 server01 systemd[1]: myapp.service: Failed with result 'exit-code'. Jul 11 09:23:14 server01 systemd[1]: Failed to start My Application Service.
status=203/EXEC は特に重要で、「ExecStart に指定した実行ファイルが見つからない、または実行できない」を意味している。この時点でパスか権限の問題に目星がつく。
よく見かける Result の値とその意味をまとめると次の通りだ。
・exit-code:プロセスが 0 以外の終了コードで終了(設定ミスや起動失敗)
・signal:プロセスがシグナルで強制終了(segfault など)
・timeout:TimeoutStartSec を超えて応答がなかった
・resources:メモリ・ファイルディスクリプタ等の上限超過
2. journalctlで詳細ログを確認する
systemctl status の出力だけでは情報が不十分な場合、journalctl でより詳細なログを確認する。# 直近のシステムジャーナルを確認(エラー前後のコンテキストが見やすい) $ journalctl -xe # 特定サービスのログのみ抽出(直近50行) $ journalctl -u myapp.service -n 50 # 直近の起動失敗のみ表示(リアルタイム追跡) $ journalctl -u myapp.service -f # 優先度 error 以上のみ表示 $ journalctl -u myapp.service -p err
Jul 11 09:23:14 server01 systemd[1]: Starting My Application Service... Jul 11 09:23:14 server01 systemd[1]: myapp.service: Control process exited, code=exited, status=203/EXEC Jul 11 09:23:14 server01 systemd[1]: myapp.service: Failed with result 'exit-code'. Jul 11 09:23:14 server01 systemd[1]: Failed to start My Application Service. -- Subject: Unit myapp.service has failed -- Defined-By: systemd -- Support: https://access.redhat.com/support -- Unit myapp.service has entered the 'failed' state with result 'exit-code'.
3. systemctl catでユニットファイルを確認する
journalctl のログで原因のカテゴリを絞り込んだら、ユニットファイルの設定を確認する。# ユニットファイルの内容を確認(読み込まれているすべての設定を表示) $ systemctl cat myapp.service # ユニットファイルを直接編集 $ systemctl edit --full myapp.service # 設定値の有効状態を確認(dropinも含めた最終的な設定値) $ systemctl show myapp.service
# /etc/systemd/system/myapp.service [Unit] Description=My Application Service After=network.target [Service] Type=simple ExecStart=/usr/local/bin/myapp --config /etc/myapp/app.conf Restart=on-failure [Install] WantedBy=multi-user.target
/usr/local/bin/myapp が存在しないことが判明した。確認コマンド:
# 実行ファイルの存在と権限を確認 $ ls -la /usr/local/bin/myapp ls: cannot access '/usr/local/bin/myapp': No such file or directory # インストール先を探す(which コマンドまたは find) $ which myapp 2>/dev/null || find /usr /opt -name "myapp" -type f 2>/dev/null /opt/myapp/bin/myapp
/opt/myapp/bin/myapp に存在することが分かった。ユニットファイルの ExecStart を修正する。依存関係エラーを解消する
1. After/Requires/Wantsの違いを理解する
「Dependency failed for XXX.service」というエラーは依存関係の問題だ。systemd の依存関係ディレクティブの違いを理解することが解消への近道になる。・After=:起動順序のみ制御。依存先が失敗しても自サービスは起動を試みる
・Requires=:強い依存。依存先が起動していないと自サービスも起動しない
・Wants=:弱い依存。依存先が失敗しても自サービスは起動を続ける
・BindsTo=:Requires よりさらに強い。依存先が停止したら自サービスも停止する
よくある間違いは、Requires= に指定したサービスが failed になっているケースだ。
例えば
Requires=postgresql.service と書いていて、PostgreSQL が起動に失敗していれば、自サービスも起動できない。2. 依存先サービスの状態を確認する
# 依存先サービスの状態確認 $ systemctl status postgresql.service # 失敗しているサービスを一括確認 $ systemctl list-units --state=failed # 全サービスの起動状態を確認 $ systemctl list-units --type=service --state=active
3. systemd-analyzeで依存ツリーを確認する
依存関係が複雑になってきたら、systemd-analyze コマンドで視覚的に確認できる。詳細な使い方は「systemd-analyze で起動時間計測」も参照してほしい。
# 特定サービスの依存ツリーを表示 $ systemd-analyze critical-chain myapp.service # ユニットファイルの文法チェック $ systemd-analyze verify myapp.service # 起動シーケンス全体のタイムライン確認 $ systemd-analyze plot > /tmp/boot-timeline.svg
systemd のトラブルシューティングをもっと体系的に学びたい方へ
Linux Master Pro Seminar では、systemd によるサービス管理・依存関係の設計・障害時の切り分け手順を、RHEL 9 実機を使ったハンズオンで習得できます。
ユニットファイルの設定ミスを修正する
1. ExecStartのパス・実行権限を確認する
status=203/EXEC や status=217/USER などのエラーは、ExecStart 周辺の設定ミスを示している。・status=203/EXEC:実行ファイルが存在しない、または実行権限がない
・status=217/USER:User= に指定したユーザーが存在しない
・status=200/CHDIR:WorkingDirectory= に指定したディレクトリが存在しない
# 実行ファイルの確認 $ ls -la /opt/myapp/bin/myapp -rwxr-xr-x 1 root root 2048576 Jul 10 14:30 /opt/myapp/bin/myapp # 実行権限がない場合は付与する $ chmod +x /opt/myapp/bin/myapp # User= に指定するユーザーの存在確認 $ id myappuser uid=1001(myappuser) gid=1001(myappuser) groups=1001(myappuser)
2. Typeの指定ミスを修正する(forking vs simple)
Type の指定ミスは見落としやすいが、起動失敗の定番原因のひとつだ。・Type=simple(デフォルト):ExecStart のプロセスがフォアグラウンドで動き続けるサービス向け。プロセスが即座に終了するとfailed になる
・Type=forking:デーモン型(二重フォークして親プロセスが終了するサービス)向け。PIDFile= の指定が必要
・Type=oneshot:実行して終了するジョブ向け。終了しても failed にならない
・Type=notify:プロセスが準備完了を systemd に通知するまで待つ(Nginx 等が対応)
旧来の init スクリプト(SysV init)から移植したサービスで Type=forking を指定しているのに PIDFile= が設定されていないケースをよく見かける。
# Type=forking を使う場合の正しい書き方 [Service] Type=forking PIDFile=/var/run/myapp.pid ExecStart=/opt/myapp/bin/myapp -D --pidfile /var/run/myapp.pid
3. daemon-reloadとrestartを実行する
ユニットファイルを編集した後は、必ず daemon-reload を実行してから再起動する。この手順を忘れると、変更前の設定のままサービスが起動してしまう。# ユニットファイルを編集 $ vi /etc/systemd/system/myapp.service # 編集後: daemon-reload で設定を再読み込み(必須) $ systemctl daemon-reload # サービスの再起動 $ systemctl restart myapp.service # 起動を確認 $ systemctl status myapp.service
また、起動時に自動起動させたい場合は enable も設定する。
# 自動起動を有効化(OS 再起動後も起動) $ systemctl enable myapp.service # 自動起動の有効化と即時起動を同時に行う $ systemctl enable --now myapp.service # 自動起動状態の確認 $ systemctl is-enabled myapp.service enabled
よくあるエラーパターンと対処法
「Failed to start」とExecStart系エラー
ポートがすでに使用中の場合も起動に失敗する。サービスが使用するポートを別プロセスが占有していないか確認しよう。ポート確認の詳細は「Linux ポート確認の全コマンド」が参考になる。
# 特定ポートの使用状況を確認(例: 8080番) $ ss -tlnp | grep 8080 LISTEN 0 128 0.0.0.0:8080 0.0.0.0:* users:(("oldapp",pid=9923,fd=5)) # 占有しているプロセスを終了 $ kill 9923 # または systemd サービスの場合は stop $ systemctl stop oldapp.service
「Dependency failed」の解消方法
# 依存関係エラーのログ確認 $ journalctl -u myapp.service | grep -i "dependency\|depend\|failed" Jul 11 09:35:22 server01 systemd[1]: Dependency failed for My Application Service. Jul 11 09:35:22 server01 systemd[1]: myapp.service: Job myapp.service/start failed with result 'dependency'. # 依存先の状態確認 $ systemctl status postgresql.service * postgresql.service - PostgreSQL Database Server Active: failed (Result: exit-code) since Fri 2026-07-11 09:34:50 JST # 依存先を先に修正・起動してから自サービスを起動 $ systemctl start postgresql.service $ systemctl start myapp.service
timeoutによるfailed
起動に時間がかかるサービス(初期化処理が重い Java アプリ等)では、TimeoutStartSec のデフォルト値(90秒)を超過して failed になることがある。# ログでタイムアウトを確認 $ journalctl -u myapp.service | grep -i timeout Jul 11 10:12:05 server01 systemd[1]: myapp.service: Start operation timed out. Terminating. # ユニットファイルにタイムアウト値を追加 [Service] TimeoutStartSec=300 # または無制限に設定(推奨しないが必要に応じて) TimeoutStartSec=infinity
本記事のまとめ
systemd サービスの起動失敗を調査する際の手順と、よく使うコマンドをまとめる。| やりたいこと | コマンド |
|---|---|
| サービスの状態・直近ログを確認 | systemctl status サービス名 -l |
| 詳細ログを確認(直近50行) | journalctl -u サービス名 -n 50 |
| エラー以上のログのみ表示 | journalctl -u サービス名 -p err |
| ユニットファイルの内容確認 | systemctl cat サービス名 |
| ユニットファイルの文法チェック | systemd-analyze verify サービス名.service |
| 依存ツリーの確認 | systemd-analyze critical-chain サービス名.service |
| 失敗中サービスを一覧表示 | systemctl list-units --state=failed |
| 設定変更後の再読み込み | systemctl daemon-reload |
| サービス再起動 | systemctl restart サービス名 |
| 自動起動有効化と即時起動 | systemctl enable --now サービス名 |
起動時間の詳細分析には「systemd-analyze で起動時間計測」も参照してほしい。
また、mount の依存関係が絡む場合は「mount コマンドの使い方」が役立つ。
今回解説した調査手順を身につけることで、failed ステータスで手が止まる状況から脱却できるはずだ。
現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、
Linux Master Pro Seminar では、systemd によるサービス管理・障害切り分け・サーバー構築を、RHEL 10 実機を使った2日間ハンズオンで実践的に習得できます。3,100名以上の受講実績。
3,100名以上が実践した「型」を無料で公開中
プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。
登録10秒/合わなければ解除3秒 / 詳細はこちら

無料メルマガで学習を続ける
Linuxの実践スキルをメールで毎週お届け。
登録は1分、解除もいつでも可。
登録無料・いつでも解除できます