systemd(システムディー)は、Linuxの起動時にカーネルが最初に実行するプロセス(PID 1)として動き、サービスの起動・停止・監視、ログの収集、定期実行までを1つの仕組みで管理するソフトウェアです。Ubuntu・Debian・Fedora・RHELといった主要ディストリビューションが標準で採用しており、サーバーを触るならsystemctlとjournalctlは避けて通れません。
2026年11月時点の最新版は、2026年9月22日公開のv262と、10月16日公開の修正版v262.1です(GitHubのリリース)。この記事では、読み方、SysVinit(init.d)との違い、systemctlの基本操作、ユニットファイルの書き方、起動しないときの調べ方を、v262のmanページとリリースノート(NEWS)に沿って説明します。
まとめ:systemdの役割とよく使う操作
- systemdはPID 1として動くinitシステム兼サービスマネージャーです。読みは「システムディー」、操作コマンドのsystemctlは「システムコントロール」と読まれることが多く、公式の表記は小文字のsystemdです。
- サービスは「ユニットファイル」という宣言的な設定ファイルで定義します。SysVinitのようにシェルスクリプトで起動手順を書く方式とは違います。
- systemdが本体、systemctlはそれに指示を出すコマンドです。起動・停止・自動起動の設定はすべてsystemctlで行います。
- ユニットファイルを書き換えたら
systemctl daemon-reloadで読み直し、動いているサービスに反映するには続けてrestartします。 - 自動再起動は既定で無効(
Restart=no)です。落ちたら上げ直したいサービスにはRestart=on-failureを明示します。 - 起動しないときは
systemctl status→journalctl -xeu ユニット名→systemd-analyze verifyの順に見ます。 - v258でランレベル(
init 3・telinit)、v260でinit.dスクリプトの対応が削除されました。2026年10月公開のUbuntu 26.10と、11月公開予定のFedora 45は、init.dスクリプトが動かない版を同梱しています。
systemdの役割と読み方
読み方:systemd・systemctl・SysVinitの対照表
systemdは「システムディー」と読みます。旧公式サイト(freedesktop.org)は表記について「systemdと書き、system DやSystem D、SystemDとは書かない」と明記しており、理由を「システムデーモンであり、Unix/Linuxではデーモンは小文字で末尾に小文字のdを付けるから」と説明しています(freedesktop.orgのsystemdページ)。sshdやhttpdと同じ命名規則です。
周辺のコマンドや用語には公式の読みが定められていません。現場でよく使われる読みは次のとおりで、どちらで言っても通じます。
| 表記 | よく使われる読み | 由来 |
|---|---|---|
| systemd | システムディー | system daemon |
| systemctl | システムコントロール/システムシーティーエル | system control |
| journalctl | ジャーナルコントロール | journal control |
| SysVinit | システムファイブ・イニット | UNIX System V のinit |
| init.d | イニット・ディー | initスクリプトの置き場 |
| daemon | デーモン | 常駐プロセス |
SysVinitの「V」はローマ数字の5で、AT&TのUNIX System V(システムファイブ)に由来します。「シスブイ」と読むのは誤りではありませんが、元の製品名に合わせるなら「ファイブ」です。
systemdが管理する範囲:PID 1と周辺コンポーネント
カーネルの初期化が終わると、カーネルは/sbin/init(systemd環境ではsystemdへのリンク)を起動し、これがPID 1になります。以降、すべてのサービスはsystemdの子として、Linuxカーネルの機能であるcgroup(コントロールグループ)単位で管理されます。プロセスがforkを繰り返しても、どのサービスに属するかをsystemdが見失わないのはcgroupのおかげです。カーネルとユーザー空間の分担はカーネルとは?OSの中核が担う役割と仕組みを実装目線で解説【2026年版】で扱っています。
systemdという名前はPID 1の本体だけでなく、同じリポジトリで開発される一群のツールも指します。日常の運用で触るのは次のものです。
| コンポーネント | 役割 | 操作コマンド |
|---|---|---|
| systemd(PID 1) | サービスの起動・停止・依存解決 | systemctl |
| systemd-journald | ログの収集と保存 | journalctl |
| systemd-logind | ログインセッションの管理 | loginctl |
| systemd-networkd | ネットワーク設定 | networkctl |
| systemd-resolved | 名前解決 | resolvectl |
| systemd-timesyncd | 簡易な時刻同期(SNTP) | timedatectl |
このうちnetworkdやresolved、timesyncdを使うかどうかはディストリビューションごとに異なります。たとえばRHEL系の時刻同期はtimesyncdではなくchronydが標準で、設定方法はchronyとは:chrony.confの設定とchronyc sources・trackingの読み方【4.9系】にまとめています。
SysVinit(init.d)との違い
SysVinitは、/etc/init.d/に置いたシェルスクリプトをランレベルごとに番号順で実行する仕組みでした。systemdとの違いは、起動手順を「手続き」で書くか「宣言」で書くかにあります。
| 観点 | SysVinit | systemd |
|---|---|---|
| サービスの定義 | シェルスクリプト | ユニットファイル(INI形式) |
| 起動順序 | スクリプト名の番号順 | 依存関係から計算・並列起動 |
| 状態の単位 | ランレベル(0〜6) | ターゲット(multi-user.target等) |
| プロセスの追跡 | PIDファイル | cgroup |
| 終了後の自動再起動 | inittabのrespawnのみ | 各ユニットのRestart= |
| ログ | 各デーモンがsyslogへ | journaldが標準出力も収集 |
実務での差が最も大きいのはプロセスの追跡です。SysVinitではデーモンが自分で書いたPIDファイルを頼りに停止するため、PIDファイルが古いと別のプロセスを止める事故が起きました。systemdはサービスが生んだ子プロセスまでcgroupでまとめて把握し、systemctl stopでまとめて終了させます。
v258〜v260で削除されたSysV互換機能
systemdは長らくSysVinitの互換機能を残してきましたが、2025年から2026年にかけて順に削除しました。NEWSに記載された変更は次のとおりです(出典:v258・v259・v260のリリースノート)。
| 版(公開日) | 変更 |
|---|---|
| v258(2025-09-17) | runlevel・telinit・initctlとinit 3形式の切り替えを削除 |
| v258(2025-09-17) | cgroup v1を削除、最低カーネルを5.4へ |
| v259(2025-12-17) | SysV initスクリプト対応を非推奨化 |
| v260(2026-03-17) | SysV initスクリプト対応とrc-localを削除 |
| v260(2026-03-17) | 最低カーネルを5.10へ |
v260で削除されたのは、init.dスクリプトをユニットに変換するsystemd-sysv-generatorと、/etc/rc.localを実行するrc-local.serviceです。v260以降では/etc/init.d/に置いただけのスクリプトはサービスとして認識されず、rc.localも実行されません。ベンダー製の古いエージェントやミドルウェアにinit.dスクリプトしか付属していない場合は、ユニットファイルを自分で書く必要があります。どちらもビルド時の選択ではなく上流のコードから削除されており、v262では関連するビルドオプション(-Drc-local=など)も消えました。
ディストリビューション別のsystemdの版
どの変更が自分のサーバーに効くかは、ディストリビューションが同梱するsystemdの版で決まります。2026年11月2日にUbuntuのパッケージ履歴(Launchpad)、Debianのソース一覧、Fedoraの更新システム(Bodhi)で確認した版は次のとおりです。
| ディストリビューション | systemdの版 | init 3 | init.dスクリプト |
|---|---|---|---|
| RHEL 9 | 252系 | 使える | 使える |
| Ubuntu 24.04 LTS | 255.4 | 使える | 使える |
| Debian 13(trixie) | 257.13 | 使える | 使える |
| RHEL 10 | 257系 | 使える | 使える |
| Ubuntu 26.04 LTS | 259.9 | 使えない | 使える(非推奨) |
| Fedora 44 | 259.10 | 使えない | 使える(非推奨) |
| Ubuntu 26.10 | 261.3 | 使えない | 使えない |
| Fedora 45(11月公開予定) | 262.1 | 使えない | 使えない |
2026年10月15日公開のUbuntu 26.10は、v260以降のsystemdを同梱しています。26.04のsystemdパッケージにはsystemd-sysv-generatorとrc-local.serviceが入っていますが、26.10のパッケージのファイル一覧からは両方とも消えています。LTSで影響が出るのは2028年4月の次期LTSからですが、Debianの開発版(forky・sid)もすでにv262系です。init.dスクリプトで動かしているサービスは、今のうちにユニットファイルへ書き換えておくのが安全です。Ubuntu 26.04の変更点全体はUbuntu 26.04 LTS(Resolute Raccoon)とは|リリース日・新機能と24.04からのアップグレード【26.04.1対応】にまとめています。
自分の環境の版はsystemctl --versionで確かめられます。OS自体の版の確認方法はLinuxのバージョン確認コマンド|OSはos-release、カーネルはuname -rで見るを参照してください。
systemctlの基本操作とsystemdとの違い
systemdはバックグラウンドで動く本体、systemctlはその本体にD-Bus経由で指示を出すコマンドです。サービスを起動・停止するときに打つのはsystemctlで、実際にプロセスを起動するのはsystemdです。
起動・停止・状態確認:restartとreloadの違い
sudo systemctl start nginx.service # 起動
sudo systemctl stop nginx.service # 停止
sudo systemctl restart nginx.service # 停止してから起動(プロセスが入れ替わる)
sudo systemctl reload nginx.service # 設定を再読み込み(masterは継続し、workerは順次交代)
systemctl status nginx.service # 状態と直近のログ
systemctl is-active nginx.service # active / inactive / failed を1語で返す
.serviceは省略でき、systemctl status nginxでも同じ意味です。reloadはサービス自身の設定(nginxならnginx.conf)を読み直させる命令で、ユニットファイルを読み直す命令ではありません。systemctlのmanページはApacheを例に「httpd.confを読み直すのであって、apache.serviceを読み直すのではない」と書いています。reloadに対応していないサービスでは失敗します。reload-or-restartはreloadできればreload、できなければrestartするので、一瞬の停止を許容できるサービスに限って使います。
is-activeは、ユニットがactive状態なら終了コード0、それ以外なら0以外を返します。ただし、active状態でも常駐プロセスが存在するとは限りません。シェルスクリプトではif systemctl is-active --quiet nginx; then …のように、表示を消して終了コードだけで分岐させます。manページの対応表では3が「動いていない」、4が「そのユニットが無い」ですが、manページ自身がこの数字に頼らず状態名で判定するよう勧めています。
自動起動の設定:enable・disable・maskの違い
sudo systemctl enable --now nginx # 自動起動を有効にし、今すぐ起動も行う
sudo systemctl disable nginx # 自動起動を外す(手動では起動できる)
sudo systemctl mask nginx # どの方法でも起動できなくする
sudo systemctl unmask nginx # maskを解除
systemctl is-enabled nginx # enabled / disabled / masked などを返す
enableは、ユニットファイルの[Install]セクションにあるWantedBy=に従ってシンボリックリンクを作るだけで、それ自体はサービスを起動しません。今すぐ起動もしたいときは--nowを付けます。
disableとmaskの差は、手動起動や他ユニットの依存による起動を許すかどうかです。maskはユニット名で/dev/nullへのシンボリックリンクを/etc/systemd/system/に作り、manページの言葉では「手動での起動を含むあらゆる起動を禁止する、disableの強い版」です。maskは起動を禁止するだけで、動作中のプロセスは止めません。競合する2つのサービス(たとえばiptablesとfirewalld)の片方を今すぐ止めたうえで二度と起動させたくないときは、systemctl mask --nowで停止と起動禁止を同時に行います。
サービス一覧の確認:list-unitsとlist-unit-files
systemctl list-units --type=service # 読み込み済みで動作中・失敗中のサービス
systemctl list-units --type=service --all # 停止中も含める
systemctl list-units --type=service --state=failed # 失敗したものだけ
systemctl list-unit-files -t service # インストール済みの全サービスと有効化状態
「今どのサービスが動いているか」を見たいときはlist-units、「どのサービスが自動起動に設定されているか」を見たいときはlist-unit-filesです。-tは--type=の短縮形です。list-unitsは既定で、動作中・ジョブ待ち・失敗のいずれかの状態にあるユニットだけを表示します。ほかのLinuxコマンドとあわせて覚えるならLinuxコマンド一覧|よく使う49個の早見表と実行例【Ubuntu 26.10対応】が早見表として使えます。
daemon-reloadの影響範囲
systemctl daemon-reloadは、manページによると「ジェネレーターの再実行、全ユニットファイルの再読み込み、依存関係ツリーの再構築」を行うコマンドです。サービスの停止や起動はこの中に含まれていません。ユニットファイルのExecStart=やEnvironment=を書き換えてdaemon-reloadしても、動いているプロセスは古い設定のまま動き続けます。
sudo systemctl daemon-reload
sudo systemctl restart myapp.service # ここで初めて新しい設定のプロセスに入れ替わる
逆に、daemon-reloadを忘れてrestartだけ打つと、systemdはディスク上の新しいユニットファイルではなく、メモリに読み込み済みの古い定義でサービスを再起動します。このときsystemctl statusには、ユニットファイルが変更されているのでdaemon-reloadを実行するよう促す警告が出ます。
ターゲットの確認と切り替え:init 3の代わり
ターゲットは、複数のユニットをまとめて「この状態まで起動する」という目標を表すユニットで、SysVinitのランレベルに当たります。v258でランレベルの概念とinit 3形式の切り替えが削除されたため、ターゲットを直接指定します。
| 旧ランレベル | ターゲット | 状態 |
|---|---|---|
| 0 | poweroff.target | 電源オフ |
| 1 | rescue.target | シングルユーザー(保守用) |
| 3 | multi-user.target | GUIなしのマルチユーザー |
| 5 | graphical.target | GUIあり |
| 6 | reboot.target | 再起動 |
systemctl get-default # 起動時のターゲットを表示
sudo systemctl set-default multi-user.target # 次回起動からGUIなしにする
sudo systemctl isolate multi-user.target # 今すぐGUIなしの状態へ切り替える
sudo systemctl rescue # 保守用のrescue.targetへ
get-defaultとset-defaultが扱うのは、default.targetというエイリアスがどのターゲットを指すかです(systemd.special)。isolateは指定したターゲットとその依存ユニットを起動し、それ以外を止めるので、SSHで入っているサーバーでrescue.targetへisolateすると接続が切れます。デスクトップ環境を入れたサーバーをGUIなしで起動させたいときは、set-default multi-user.targetだけで足ります。
ユニットファイルの書き方
ユニットファイルは、systemdが管理する対象(ユニット)を定義するINI形式のファイルです。拡張子で種類が決まります。「.serviceファイル」とは、このうちサービスを定義するユニットファイルを指します。
| 拡張子 | 管理する対象 | 例 |
|---|---|---|
| .service | 常駐プロセス・一回きりの処理 | nginx.service |
| .socket | 待ち受けソケット | sshd.socket |
| .timer | 定期実行 | logrotate.timer |
| .target | ユニットのまとまり | multi-user.target |
| .mount | マウントポイント | tmp.mount |
| .path | ファイルの変化の監視 | – |
| .slice | cgroupの資源の区画 | user.slice |
ユニットファイルの配置場所と読み込みの優先順位
同じ名前のユニットファイルが複数の場所にあると、上にある場所のものが優先されます(systemd.unitの「Unit File Load Path」より、主要な4か所を抜粋)。
| 優先度 | パス | 置く人 |
|---|---|---|
| 高 | /etc/systemd/system | 管理者が作成・上書き |
| ↓ | /run/systemd/system | 実行時に生成(再起動で消える) |
| ↓ | /usr/local/lib/systemd/system | 管理者が自前でインストール |
| 低 | /usr/lib/systemd/system | パッケージマネージャー |
aptやdnfで入れたパッケージのユニットファイルは/usr/lib/systemd/system/に入ります。ここを直接編集すると、パッケージの更新で上書きされて変更が消えます。自作のサービスは/etc/systemd/system/に置き、パッケージ由来のサービスを変えたいときは後述のドロップインを使います。
最小構成の.serviceファイル
Pythonで書いたWebアプリを常駐させる例です。/etc/systemd/system/myapp.serviceとして保存します。実行ユーザーmyapp、/opt/myappの仮想環境とアプリ、/etc/myapp/envは事前に用意してある前提です。
[Unit]
Description=My web application
Wants=network-online.target
After=network-online.target
[Service]
Type=exec
User=myapp
WorkingDirectory=/opt/myapp
EnvironmentFile=/etc/myapp/env
ExecStart=/opt/myapp/.venv/bin/python -m myapp
Restart=on-failure
RestartSec=5s
[Install]
WantedBy=multi-user.target
[Unit]は説明と依存関係、[Service]は起動方法、[Install]はenableしたときにどのターゲットに組み込むかを書きます。ExecStart=のコマンドはスラッシュを含まない実行ファイル名でも書けますが、その場合はビルド時に決まった検索パスから探されるため、例のように絶対パスで書くほうが確実です。シェルを経由しないので、パイプやリダイレクトを使うなら/bin/sh -c '…'で包みます。
Type=は、systemdが「起動完了」とみなすタイミングを決めます。Type=を省くとsimpleになりますが、simpleはforkした直後に起動成功と判定するため、実行ファイルのパス間違いや存在しないUser=でもsystemctl startが成功を返します。systemd.serviceも「通常はType=execのほうがよい」としており、execはプログラムの実行(execve)まで成功してから完了とみなすので、こうした誤りがstartの時点で失敗として返ります。自分でforkしてバックグラウンドに回る古いデーモンにはforking、一度実行して終わる処理にはoneshotを使います。
EnvironmentFile=は、環境ごとに変わる設定値をユニットファイル本体から外へ出すための指定です。ただしsystemd.execは、環境変数はD-Bus経由で非特権のクライアントからも見えるため、パスワードや鍵の受け渡しには向かないと明記しています。秘密情報はLoadCredential=などのクレデンシャル機能で渡します。環境変数の渡し方全般は環境変数とは?Linux・Windowsの設定方法とDocker・CIでの受け渡しで扱っています。
ドロップインで既存の設定を上書き
sudo systemctl edit nginx.service
エディターが開き、保存すると/etc/systemd/system/nginx.service.d/override.confが作られ、daemon-reloadも自動で行われます。書くのは変えたい項目だけで、たとえば[Service]の下にLimitNOFILE=65536と書けば、元のファイルはそのままに上限だけが上書きされます。ExecStart=のように複数指定できる項目を置き換えるときは、先に空のExecStart=を1行書いて元の値を消してから新しい値を書きます。元のファイルとドロップインの中身はsystemctl cat nginx.serviceで並べて表示でき、systemdが実際に保持している値はsystemctl show nginx.service -p LimitNOFILEのようにshowで確認します。
systemd-analyze verifyで書き間違いを検出
systemd-analyze verify /etc/systemd/system/myapp.service
ユニットファイルを読み込み、ディレクティブ名の打ち間違いや、ExecStart=の実行ファイルが存在しないといった誤りを警告として表示します。systemd-analyzeのmanページが挙げる検出対象は、未知のセクションとディレクティブ、起動に必要な依存ユニットの欠落、Documentation=のmanページの欠落、ExecStart=等のコマンドの欠落・実行権限なしの4種類です。静的な検査なので、アプリが実際に起動できるか、接続先に繋がるかまでは分かりません。それでも、daemon-reloadの前に通しておけば書き間違いでの手戻りは減らせます。
依存関係と起動順序:WantsとAfterの役割の違い
systemdでは「必要とする(要求)」と「後に起動する(順序)」を別々に書きます。manページはAfter=・Before=について、Requires=・Wants=とは独立した直交する設定だと明記しています。
| ディレクティブ | 種類 | 意味 |
|---|---|---|
| Wants= | 要求(弱) | 一緒に起動する。相手が失敗しても続行 |
| Requires= | 要求(強) | 相手を明示的に停止すると自分も止まる |
| After= | 順序 | 相手の起動完了を待ってから起動 |
| Before= | 順序 | 相手より先に起動 |
| Conflicts= | 排他 | 相手を起動すると自分が止まる |
Requires=postgresql.serviceだけ書いてAfter=を書かないと、PostgreSQLとアプリが同時に起動し、アプリがDBに繋げず落ちることがあります。「DBが起動し終わってから自分を起動する」には、Wants=postgresql.service(またはRequires=)とAfter=postgresql.serviceを両方書きます。上の.serviceの例でnetwork-online.targetを両方に書いているのと同じ形です。
systemctl list-dependencies myapp.service # 依存関係をツリー表示
systemd-analyze critical-chain # 起動完了までの最も時間がかかった経路
systemd-analyze blame # ユニットごとの起動時間(長い順)
起動が遅いサーバーでは、まずcritical-chainで起動完了を遅らせている経路を見ます。blameの上位は並列に起動しているユニットも含むため、時間が長いユニットが全体の遅れの原因とは限りません。
自動再起動の設定:Restart=とStartLimit
systemdはサービスが落ちたら上げ直す機能を持っていますが、既定値はRestart=noで、何も書かなければ再起動しません。v262のmanページにある主な値は次のとおりです。
on-failure:0以外の終了コード、異常なシグナルによる終了(Type=oneshot以外では、SIGHUP・SIGINT・SIGTERM・SIGPIPEの4つは既定で正常終了扱い)、タイムアウト、ウォッチドッグで再起動します。常駐サービスの標準的な選択です。always:正常終了でも再起動します。ただしsystemctl stopで止めたときは再起動しません。on-abnormal:異常なシグナルによる終了・タイムアウト・ウォッチドッグのときだけ再起動し、0以外の終了コードでは再起動しません。
再起動までの待ち時間RestartSec=の既定は100ミリ秒です。起動直後に落ちるサービスは100ミリ秒間隔で再起動を繰り返すことになり、ここで効いてくるのが起動回数の制限です。systemdの既定(DefaultStartLimitIntervalSec=10s・DefaultStartLimitBurst=5)では、10秒の間に許される起動は5回までで、6回目の起動要求は拒否され、ユニットは失敗状態で止まります。
[Unit]
StartLimitIntervalSec=300
StartLimitBurst=5
[Service]
Restart=on-failure
RestartSec=10s
この設定なら、終了してから10秒待って再起動し、300秒の間の起動が初回を含めて5回を超えようとした時点で止まります。数えるのは失敗の回数ではなく起動の回数です。StartLimitIntervalSec=とStartLimitBurst=は[Service]ではなく[Unit]セクションに書きます。v262からはこの期間の計測にサスペンド中の時間も含まれるようになりました(v262のリリースノート)。上限に達して止まったユニットは、原因を直した後にsystemctl reset-failed myapp.serviceでカウンターを戻してから起動します。
ログの確認:journalctlの使い方
systemd配下のサービスが標準出力・標準エラーに書いたものは、既定の設定(StandardOutput=journal)ではsystemd-journaldが自動で集めます。アプリ側でログファイルを用意しなくても、journalctlでサービスごとに読めます。
journalctl -u myapp.service # myappのログ(古い順)
journalctl -u myapp.service -f # 追いかけ表示(tail -f相当)
journalctl -u myapp.service -n 100 # 直近100行
journalctl -u myapp.service --since "1 hour ago"
journalctl -u myapp.service -b # 今回の起動以降
journalctl -b -1 -p err # 前回の起動のエラー以上
journalctl --disk-usage # ジャーナルの使用容量
sudo journalctl --vacuum-size=500M # アーカイブ済みログの使用量を500MiB未満にする
ジャーナルの保存先は/etc/systemd/journald.confのStorage=で決まります。v258までの既定はautoで、/var/log/journalディレクトリがあればディスクに、無ければメモリ(/run/log/journal)に保存し、後者は再起動でログが消えました。v259で既定がpersistent(ディスク保存)に変わっています。ただしこの既定値はビルド時に決まるため、ディストリビューションが別の値でビルドしている可能性があります。前回の起動のログがjournalctl -b -1で読めなければ、保存先がメモリになっていないかを疑ってください。
ディスクの使用量は、既定でファイルシステムの10%(上限4GB)までに制限されます。--vacuum-size=が消すのはアーカイブ済みのファイルだけで、書き込み中のファイルは残ります。恒久的に上限を下げるならjournald.confのSystemMaxUse=を設定します。長期保管や別サーバーへの転送が必要なら、journaldからrsyslogへ流す構成が一般的で、設定方法はrsyslogとは?Linuxのログ収集・転送の仕組みとrsyslog.conf設定・導入判断を実装目線で解説で扱っています。
サービスが起動しない・すぐ落ちるときの調べ方
原因の切り分けは、状態→ログ→設定の順に見ると早く終わります。
systemctl status myapp.service # 1. 状態・終了コード・直近10行のログ
journalctl -xeu myapp.service # 2. 詳しいログを末尾から、説明文付きで
systemd-analyze verify myapp.service # 3. ユニットファイルの書き間違い
systemctl cat myapp.service # 4. ディスク上のユニットファイルとドロップイン
sudo systemctl reset-failed myapp.service # 5. 直した後、失敗状態と起動回数を戻す
journalctl -xeuは3つのオプションの組み合わせです。-xはメッセージカタログの説明文を添え、-eはページャーで末尾へ飛び(直近1000行・今回の起動に絞る)、-uでユニットを指定します(journalctlのmanページ)。statusの出力で次の表示が出ていれば、原因はほぼ絞れます。
| statusの表示 | 主な原因 |
|---|---|
| status=203/EXEC | ExecStart=のパス違い・実行権限なし |
| status=217/USER | User=のユーザーが存在しない |
| status=200/CHDIR | WorkingDirectory=が存在しない |
| start-limit-hit | 起動回数の上限に到達 |
| Unit … not found | ユニット名の誤り・daemon-reload忘れ |
203や217のような番号は、systemdがプロセスを起動する準備の段階で失敗したことを示す独自の終了コードで、アプリ自体はまだ動いていません。アプリのログを探しても何も出ないときは、この番号を先に確認します。
ユーザー単位のサービス:systemctl –user
root権限を持たない開発者が自分のプロセスを常駐させたいときは、ユーザーごとのサービスマネージャーを使います。ユニットファイルを~/.config/systemd/user/に置き、sudoを付けずに--userで操作します。
mkdir -p ~/.config/systemd/user
systemctl --user daemon-reload
systemctl --user enable --now myjob.service
journalctl --user -u myjob.service
sudo loginctl enable-linger $USER # ログアウト後も動かし続ける
lingerが無効の場合、ユーザーのサービスマネージャーは通常、最後のログインセッションが終了し、logind.confのUserStopDelaySec=で決まる待機時間が過ぎると停止します。このため、サーバーで常駐させるならloginctl enable-lingerを設定します。lingerを有効にしたユーザーは、起動時にサービスマネージャーが立ち上がり、ログアウト後も残ります(loginctlのmanページ)。ユーザー単位のサービスの[Install]に書くターゲットは、multi-user.targetではなくdefault.targetです。
リソース制限:メモリ・CPU・I/Oの上限
systemdはサービスごとにcgroupを作るので、ユニットファイルに書くだけでメモリやCPUの上限を掛けられます(systemd.resource-control)。
[Service]
# 768MiBを超えるとメモリ回収とスロットリングの対象になる
MemoryHigh=768M
# メモリ使用量を1GiB以内に抑えられない場合はユニット内でOOM killerが作動
MemoryMax=1G
# CPU時間をCPU 1.5個分までに制限
CPUQuota=150%
# このデバイスからの読み込みを毎秒50MB(1000基準)までに制限
IOReadBandwidthMax=/dev/nvme0n1 50M
manページはメモリの制御について、MemoryHigh=を主な手段にし、MemoryMax=は最後の防衛線として使うよう勧めています。MemoryMaxでは、メモリ回収などで使用量を上限内に抑えられない場合に、ユニット内でOOM killerが作動するためです。CPUQuota=はCPU 1個分を100%とする値で、2個分以上を割り当てるなら100%を超えて書きます。IOReadBandwidthMax=はデバイスのパスと毎秒のバイト数を空白で区切って指定し(K・M・Gは1000基準)、デバイスが空いていても上限を超えて読ませません。
動作中のサービスにはsudo systemctl set-property myapp.service MemoryMax=1Gで再起動なしに掛けられます。この変更はディスクに保存されて次回の起動以降も残り、--runtimeを付けたときだけ再起動までの一時的な設定になります。各サービスの使用量はsystemd-cgtopで一覧できます。これらの設定はcgroup v2(unified hierarchy)のコントローラーを操作するもので、v258でcgroup v1が削除されたため、v258以降はv1との差を気にする必要がなくなりました。
サンドボックス設定の点検にはsystemd-analyze security myapp.serviceが使えます。ProtectSystem=やNoNewPrivileges=などの設定を項目ごとに採点し、0.0〜10.0の「露出度」で表示します。値が大きいほど制限が少ない状態です。
オンデマンド起動:timerとsocket
timerによるcronの置き換えと実行時刻の確認
定期実行はcronの代わりに.timerユニットで書けます。同名の.serviceとペアで使い、timerがserviceを起動します。
# /etc/systemd/system/backup.service
[Unit]
Description=Nightly backup
[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.sh
# /etc/systemd/system/backup.timer
[Unit]
Description=Run backup daily at 03:00
[Timer]
OnCalendar=*-*-* 03:00:00
Persistent=true
[Install]
WantedBy=timers.target
有効化はsudo systemctl enable --now backup.timer(serviceではなくtimerをenableする)で、次回の実行予定はsystemctl list-timersで一覧できます。OnCalendar=の書式が意図どおりかはsystemd-analyze calendar "*-*-* 03:00:00"で次回時刻を表示させて確かめられます。
cronとの実用上の差は3つあります。実行結果がjournalctlに残ること、Persistent=trueを付けると電源が切れていて逃した回を、タイマーが再び有効になった時点(通常は次の起動時)で1回実行すること(OnCalendar=のときだけ有効・既定はfalse)、前回の実行が終わっていなければ同じserviceを重ねて起動しないことです。一方で、AccuracySec=の既定が1分のため、指定時刻から最大1分の幅で発火します。この幅を詰めたいときはAccuracySec=1sのように小さくしますが、これはタイマーの許容幅の指定で、処理が秒単位で開始されることまでは保証しません。
socketによる接続時の起動とアプリ側の対応条件
ソケットアクティベーションは、待ち受けソケットだけをsystemdが先に開き、最初の接続が来た時点で対応するサービスを起動する仕組みです。myapp.socketに[Socket]セクションでListenStream=8080と書くと、systemdがTCP 8080番を開いて待ち、接続が来ると同名のmyapp.serviceを起動してソケットを渡します。
起動の遅いサービスを使う直前まで起動しないで済むことに加え、サービスを再起動している間もソケット自体は開いたままなので、その間の接続がカーネルのキューで待たされ、取りこぼしになりにくいという利点があります。ただし、アプリ側がsystemdから受け取ったソケットを使う実装(sd_listen_fdsなど)になっている必要があり、自分でポートを開くアプリにはそのまま使えません。
systemdの設定で詰まりやすい箇所
書き忘れるとサービスが止まったままになる、または待つべきものを待たずに起動する設定が次の3つです。
- Restart=を書いていない:既定はnoなので、プロセスが異常終了してもsystemdは再起動しません。停止を検知するには別途監視が要ります。
- Type=simpleのまま:実行ファイルのパスを間違えても
startは成功を返し、直後に失敗状態になります。監視スクリプトがstartの戻り値だけを見ていると検知できません。 - network.targetで待っている:
network.targetはネットワークの設定完了を意味しません。起動時に外部のDBやAPIへ接続するサービスは、network-online.targetをWants=とAfter=の両方に書きます。このターゲットが実際に待つのは、systemd-networkd-wait-online.serviceやNetworkManager-wait-online.serviceといった待機サービスが有効な場合だけで、待つのもIPアドレスの設定までです。接続先のサーバーに届くことまでは保証しないため、アプリ側にも接続の再試行が要ります。
反対に、systemdを使わないほうがよい場面もあります。Alpine LinuxのようにOpenRCを標準とするディストリビューションや、1プロセスだけを動かすコンテナでは、systemdを入れずに済ませます。1プロセスのコンテナでは、プロセスの再起動をDockerやKubernetesの再起動ポリシーが担うため、コンテナ内にsystemdを置くと再起動の管理が二重になります。コンテナとホストの役割分担はDockerとは|仮想マシンとの違い・必要スペック・Desktop/Engineの違いとインストール手順【2026年10月】で扱っています。init.dスクリプトからの書き換えやサーバー構成の見直しを外部に任せる場合は、一窓のインフラ構築(AWS・Google Cloud・Azure)でも対応しています。
よくある質問
systemdとsystemctlの違いは何ですか?
systemdはPID 1として常駐し、サービスを実際に起動・停止する本体です。systemctlは、そのsystemdに「起動して」「止めて」と指示を出すためのコマンドです。
systemdとsystemctlの読み方は?
systemdは「システムディー」です。system daemon(システムデーモン)の略で、公式には小文字でsystemdと書きます。systemctlに公式の読みはなく、「システムコントロール」または「システムシーティーエル」と読まれます。
init.dとsystemdの違いは何ですか?
init.d(SysVinit)はシェルスクリプトを番号順に実行して起動します。systemdはユニットファイルに書いた依存関係から順序を計算し、並列に起動します。systemd v260でinit.dスクリプトの対応は削除され、Ubuntu 26.10以降ではinit.dに置いたスクリプトは起動しません。
systemctl daemon-reloadを実行すると動いているサービスに影響はありますか?
サービスは停止も再起動もされません。ユニットファイルの再読み込みと依存関係の再構築だけが行われ、変更を動作中のプロセスに反映するには、続けてsystemctl restartが必要です。
Linuxで動いているサービスの一覧を確認するには?
systemctl list-units --type=serviceで動作中・失敗中のサービスを表示します。自動起動の設定状況まで見たいときはsystemctl list-unit-files -t serviceを使います。