ブログに戻る

systemdサービスが起動しない?原因と直し方

2026年9月14日

起動しないsystemdサービスは、ほとんどの場合謎ではありません。systemctl status <unit> を実行し、続けて journalctl -u <unit> -n 50 --no-pager でそのunitの直近50行を読む——この2つで、5つの原因のどれかがほぼ名指しで書いてあります: 間違ったパス、存在しないバイナリ、誤ったパーミッション、SELinux/AppArmorの拒否、unitファイルの構文エラー。 失敗の理由はログの中にあり、以下の修正はそれに対するパターンマッチでしかありません。

systemdサービスが失敗した理由を見るには?

まずstatus、次にjournalです:

systemctl status myapp.service
journalctl -u myapp.service -n 50 --no-pager

status は状態(inactive (dead)failed (exit-code)activating (auto-restart))と直近のログ数行をくれます。journalは全文をくれます: stdout、stderr、そしてunitに対するsystemd自身の苦情です。

過去に失敗していてその実行の記録が欲しいなら、現在ブートなら -b、今日なら --since today を足します:

journalctl -u myapp.service -b --no-pager

フルツールキット——ブート、プライオリティ、出力のライブ追尾——は journalctl cheat sheet をどうぞ。同じ筋肉の記憶です。

もう1組、知っておく価値があります:

systemctl list-units --failed
systemctl reset-failed myapp.service

1つ目はボックス上の全赤unitを一覧します。2つ目は直した後にfailed状態を晴らします——見た目だけですが、ステータスページが狼少年になるのを止めます。

最も多い原因は何?

ログが症状を名指した後、原因はほぼ必ずこの5つのどれかです:

ログの表示推定原因直し方
status=203/EXECExecStart= のパス違いか、インタプリタ不在絶対パス、chmod +x、shebangを確認
スクリプトで status=203/EXECスクリプトのCRLF行末か誤ったshebangdos2unix script.sh、1行目を直す
status=1/FAILURE、アプリのログなしワーキングディレクトリか環境変数の欠落WorkingDirectory= を設定、Environment= を追加
Permission deniedユーザーがファイルを読めないか、ポートをbindできない所有権を直す。1024未満のポートは AmbientCapabilities=CAP_NET_BIND_SERVICE かroot
Unit is masked誰かが systemctl mask を実行したsystemctl unmask myapp.service
unitファイル編集が「何も起きない」デーモンがreloadされていないsystemctl daemon-reload

203/EXEC ファミリーは特別扱いに値します。午後を丸ごと食べるのがこいつだからです。Systemdは ExecStart= を起動するのにあなたのシェルを使いません。つまり:

# 誤り —— シェルなし、~ は展開されず、PATH検索もなし
ExecStart=~/app/run.sh

# 正しい
ExecStart=/opt/app/run.sh

そしてスクリプト自体が実行可能で、本物のshebang(#!/bin/bash#!/usr/bin/env bash)で始まる必要があります。ターミナルでは完走するのにsystemd下では203で落ちるスクリプトは、ほぼ必ず次のどれかです: 実行可能でない、CRLF行末、行き先のないshebang、相対パス。

手動では起動するのにブート時は起動しないのはなぜ?

定番の順序バグです。ログがブート直後の失敗を示すのに、手動の systemctl start では問題なく立つなら、サービスはレースに負けています——ネットワーク、マウント済みディスク、データベースを、その物が存在する前に掴みに行っています。

願う代わりに依存を宣言するのが直し方です:

[Unit]
After=network-online.target postgresql.service
Wants=network-online.target

[Service]
ExecStartPre=/usr/bin/test -f /opt/app/config.toml

After= が起動順を決め、Wants= がsystemdに依存を実際に上げさせます。network-online.target はdistroでネットワーク待ちサービスが有効な時だけ機能するので、systemctl is-enabled NetworkManager-wait-online.service(またはsystemd-networkdの相当物)を確認してください。ExecStartPre= のガードは、スタックトレースの代わりに読めるメッセージで大声で落ちてくれる、安くて誠実な方法です。

亜種がもう1つ: ブートでは起動するが即座に死ぬパターン。対話環境にあってブートにないものを探します——PATH の違い、virtualenv、HOME。必要なものは明示的に設定します:

[Service]
Environment="PATH=/opt/app/venv/bin:/usr/bin"
User=appuser
WorkingDirectory=/opt/app

サービスがjournalにログを出さないのはなぜ?

journalctl -u に何も出ないなら、順に3つ確認します:

  1. unit内の StandardOutput=StandardError=——journal(デフォルト)か journal+console である必要があります。誰かが null にしているかもしれません。
  2. アプリがstdoutではなくファイルへ書いている。Systemdが捕捉するのはstdout/stderrだけです。ログファイル書き出しはjournalを完全に迂回します。アプリをstdoutへ向けるか、ファイルを読む。
  3. ストレージ上限が古い行を落とした: journalctl --disk-usage、journalが詰まっているなら /etc/systemd/journald.confSystemMaxUse=

起動自体のデバッグでは、ブート時コンテキストへシェルを落とすのに勝つものはありません:

[Service]
ExecStart=/bin/bash -c 'exec /opt/app/bin/server 2>&1'

または、本物の謎には、unitのユーザーとしてシェルで正確な ExecStart= コマンドを実行します——環境差の大半は最初の10秒で表面化します。

クラッシュ後に自動で再起動させるには?

デフォルトポリシーは Restart=no です: 落ちたサービスは死んだまま、あなたはユーザーから知らされます。サービスごとに直しましょう:

[Service]
Restart=on-failure
RestartSec=5
設定再起動する条件
no(デフォルト)決して
on-failure非ゼロ終了、シグナル、タイムアウト
alwaysあらゆる終了、正常終了でも
on-watchdogwatchdogタイムアウト時のみ

Restart=on-failure には開始レートのガードを合わせます。クラッシュループがボックスを叩き続けないように: [Unit] セクションの StartLimitIntervalSec=StartLimitBurst=。60秒に5回の失敗はCPUを回すのではなく、人間を呼ぶべきです。

デーモンではなく定期ジョブとして組むなら、先に cron vs systemd timer を天秤に——タイマーはjournalログと依存の順序を無料でくれます。この記事が何度も手を伸ばしているのはまさにそれです。

60秒のチェックリスト

  1. systemctl status <unit> —— 状態と直近の行を読む。
  2. journalctl -u <unit> -n 50 --no-pager —— 本物のエラーを探す。
  3. 203/EXEC? パス、shebang、パーミッションを直す。Permission denied? ユーザーとファイル所有権を直す。
  4. ブート時だけ落ちる? After=/Wants=network-online.targetExecStartPre= ガードを足す。
  5. unitファイルを変えた? systemctl daemon-reload && systemctl restart <unit>
  6. Restart=on-failure を足す。次のクラッシュは隠れず、自分から名乗りを上げます。

「systemdは複雑」という瞬間の大半は、エラーは最初からjournalの中にあったことへ収束します。unitファイルを編集する前に読む。後からではありません。

— mrsaynothing

Get the next one by email

One email per post. No spam, no algorithms.

self-hosted · no third parties · one-click unsubscribe

what is this?

GGUF量子化:どのレベルを選ぶべきか?

記事を楽しんでいただけましたか?私の本業はこのような構築です。 採用のご相談