- 技術 💻
systemctlコマンドの使い方とsystemdとは|statusとエラー原文
結論
systemd は Linux の起動とサービス(常駐プログラム)を管理する仕組みで、systemctl はその systemd に「起動して」「止めて」「自動起動にして」と頼むコマンドです。まず systemctl status サービス名 で状態とログの末尾を見て、start・stop・restart・enable を使い分けます。
この記事でわかること
- status の出力の読み方と、start・restart・reload・enable の違い(止まる時間の実測つき)
- 自作サービスの unit ファイル・timer の書き方と、実際に動かした結果
- failed になったときの切り分けと、よくあるエラー17種の原文
systemctl は、systemd という「サービスの管理役」に指示を出す窓口です。コマンドを打つと、systemd が unit ファイル(サービスの設定書)を読み、プロセスを起動・停止し、ログは journald に集まります。
flowchart TD
U[systemctl start hello] --> D[systemd PID 1]
F[unit ファイル<br>hello.service] --> D
D --> P[プロセスを起動<br>cgroup で管理]
P --> J[journald にログ]
J --> K[journalctl -u hello]
よく調べられることの答えを先にまとめます。
| 知りたいこと | 答え | 詳しい節 |
|---|---|---|
| systemd とは | PID 1 として動き、サービス・マウント・タイマーなどを「ユニット」として管理する仕組み | systemdとは |
| systemctl status の見方 | Loaded(設定と自動起動)・Active(今の状態)・Main PID・末尾のログを見る | statusの見方 |
| restart と reload の違い | restart は止めて起動し直す。reload はプロセスを止めずに設定だけ読み直させる | start・stop・restart・reload |
| 自動起動の設定 | systemctl enable --now 名前(今すぐ起動も同時に) |
enable・disable |
| unit ファイルを書き換えた | systemctl daemon-reload のあと restart |
systemctl edit と daemon-reload |
| サービスのログを見る | journalctl -u 名前 -n 50、追いかけるなら -f |
journalctl |
| failed になった | status の Process 行の終了コード→journalctl→reset-failed の順 | failedの切り分け |
| cron の代わりに定期実行 | .service と .timer を組で作り、enable --now 名前.timer |
systemd timer |
| service コマンドとの違い | Ubuntu の service は systemd があれば systemctl を呼ぶ | serviceコマンド・SysVinit |
確認環境:Ubuntu 24.04.5 LTS(カーネル 6.18)のコンテナで確かめました。このコンテナの PID 1 は systemd ではないため、systemd 255(255.4-1ubuntu8.18)を、unshare で作った PID・マウント・UTS・IPC・cgroup の名前空間と専用のネットワーク名前空間の中で PID 1 として起動しました。ファイルシステムは overlay で重ね、変更はすべて作業用の場所に書いています(ホストの systemd と設定には触れていません)。この中で、自作の Web アプリ(Python 3.13.18 の http.server)を hello.service として動かし、start・stop・restart・reload・enable・disable・edit・mask、Restart= による自動再起動、起動回数の上限、わざと失敗させた unit 9種、timer と systemd-run、systemctl --user を実行し、出力をそのまま載せています。疎通は curl 8.5.0 で 127.0.0.1 に送りました。電源操作(reboot・poweroff)、target の切り替え(isolate・rescue)、リモート操作(-H)、WSL での有効化は、systemd の公式マニュアル(freedesktop.org)と Microsoft Learn で確かめた範囲だけを書いています。図は mermaid 11.16.0 で描画を確かめました(確認日 2026年10月29日)。
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「systemctlコマンドの使い方とsystemdとは|statusとエラー原文」
systemdとは・systemctlとは(PID 1とユニット)
この節では、systemd と systemctl の関係と、「ユニット」という考え方が分かります。
systemd は、Linux で最初に起動するプロセス(PID 1)として動き、ほかのプログラムの起動・停止・再起動・ログを管理するソフトウェアです。 Ubuntu・Debian・RHEL・Fedora など主な Linux ディストリビューションで、以前の SysVinit(/etc/init.d のスクリプトで起動する方式)に代わって標準になっています。systemctl は systemd を操作するためのコマンドで、systemd 本体ではありません。
systemd は管理する対象をユニットと呼び、種類を拡張子で表します。今回の環境で読み込まれていた 160 個のユニットを種類別に数えると、次のようになりました(systemctl list-units --all)。
| 拡張子 | 何を管理するか | この環境の数 |
|---|---|---|
| .service | 常駐プログラム・1回だけの処理(Web サーバー、DB、バッチ) | 73 |
| .target | ユニットのまとまり。以前のランレベルにあたる(multi-user.target など) | 40 |
| .mount | ファイルシステムのマウント | 21 |
| .socket | 接続が来たらサービスを起動する待ち受け | 11 |
| .slice | cgroup の階層(CPU・メモリの区切り) | 4 |
| .timer | 決まった時刻・間隔でサービスを起動する(cron の代わり) | 3 |
| .path | ファイルの変化を見張ってサービスを起動する | 2 |
サービスの .service は省略でき、timer などは backup.timer のように拡張子を付けます。
systemd(管理役)
PID 1 として常に動いている
unit ファイルを読んでプロセスを起動・監視
落ちたら Restart= に従って起動し直す
systemctl(指示を出すコマンド)
打ったときだけ動いて終わる
D-Bus・専用ソケット経由で systemd に頼む
systemd が PID 1 でないと使えない
PID 1 が systemd ではない環境(Docker のコンテナ・設定前の WSL)では、次のように断られます(今回のコンテナで実行。終了コード 1)。
$ systemctl status cron
System has not been booted with systemd as init system (PID 1). Can't operate.
Failed to connect to bus: Host is down
systemctlコマンド一覧(よく使う20個)
この節では、systemctl のサブコマンドを目的別に一覧で確かめられます。
書式は systemctl サブコマンド ユニット名 です。状態を変える操作には root 権限が要るため、一般ユーザーは sudo を付けます。
| やりたいこと | コマンド | メモ |
|---|---|---|
| 状態とログの末尾を見る | systemctl status hello | 権限なしで見られる |
| 起動する | systemctl start hello | 起動済みなら何もしない |
| 止める | systemctl stop hello | SIGTERM→時間切れで SIGKILL |
| 止めて起動し直す | systemctl restart hello | 止まっていれば起動する |
| 設定だけ読み直させる | systemctl reload hello | ExecReload= がある時だけ |
| reload できなければ restart | systemctl reload-or-restart hello | スクリプト向き |
| 動いている時だけ restart | systemctl try-restart hello | 止まっていれば何もしない |
| 自動起動にする(今も起動) | systemctl enable --now hello | --now なしは次の起動から |
| 自動起動をやめる(今も停止) | systemctl disable --now hello | |
| 起動できなくする | systemctl mask hello | 戻すのは unmask |
| 動いているか | systemctl is-active hello | 終了コードで判定できる |
| 自動起動か | systemctl is-enabled hello | |
| 失敗したユニット | systemctl --failed | list-units --failed と同じ |
| サービス一覧 | systemctl list-units --type=service | 読み込み済みのもの |
| unit ファイル一覧 | systemctl list-unit-files | enabled・disabled が分かる |
| 設定の中身を見る | systemctl cat hello | ドロップインも順に表示 |
| 設定値を1つ見る | systemctl show hello -p MainPID | スクリプト向き |
| 設定を部分的に上書き | systemctl edit hello | 保存すると自動で再読み込み |
| unit ファイルを読み直す | systemctl daemon-reload | ファイルを直接編集した後 |
| 失敗の記録を消す | systemctl reset-failed hello | 起動回数の上限も戻る |
電源操作の reboot・poweroff、SSH 経由で別のマシンを操作する -H ユーザー@ホスト もあります(公式マニュアルで確認。今回は実行していません)。
systemctl statusの見方(出力を1行ずつ読む)
この節では、systemctl status の出力のどこを見れば、動いているか・自動起動か・なぜ止まったかが分かるのかが分かります。
自作の hello.service を起動した直後の status です。
$ systemctl status hello
● hello.service - Hello sample web app (t151)
Loaded: loaded (/etc/systemd/system/hello.service; disabled; preset: enabled)
Active: active (running) since Fri 2026-10-30 14:44:43 UTC; 564ms ago
Main PID: 296 (python3)
Tasks: 1 (limit: 9607)
Memory: 8.7M ()
CGroup: /system.slice/hello.service
└─296 /usr/bin/python3 -u /opt/hello/app.py
Oct 30 14:44:43 vm systemd[1]: Started hello.service - Hello sample web app (t151).
Oct 30 14:44:43 vm python3[296]: listening on 127.0.0.1:8151
Oct 30 14:44:44 vm python3[296]: access: "GET / HTTP/1.1" 200 -
| 行 | 読み方 | この例では |
|---|---|---|
| 先頭の記号 | ● 緑=動作中、○=止まっている、× 赤=失敗 | ● 動作中 |
| Loaded | unit ファイルの場所; 自動起動の設定; ディストリビューションの既定(preset) | /etc/systemd/system に置いた自作。自動起動はしない |
| Drop-In | 部分上書きのファイル(あるときだけ表示) | なし |
| Active | 今の状態(括弧の中が詳しい状態)と、いつからか | 564ms 前から動作中 |
| Main PID | 本体のプロセス番号 | 296 の python3 |
| Process | 終わったプロセスと終了コード(失敗時に出る) | なし |
| CGroup | このサービスに属するプロセスの木 | 1つだけ |
| 末尾のログ | journald に集まった直近10行 | 起動とアクセスの記録 |
Active に出る状態は、次の5つを覚えれば読めます。
active (running)
常駐プログラムが動いている。
● 緑
active (exited)
1回だけの処理が成功して終わった(Type=oneshot と RemainAfterExit=yes)。
止まっているが正常
inactive (dead)
止まっている。起動していない、または正常に止めた。
○
activating (auto-restart)
落ちたので、RestartSec= の間待ってから起動し直すところ。
再起動待ち
failed
失敗して止まった。Result: の後ろに理由(exit-code・timeout・start-limit-hit など)。
× 赤
status は、ページャー(less)で表示されて止まることがあります。 スクリプトや貼り付け用には --no-pager を付け、ログの行数は -n 30 のように変えます。行が右で切れるときは -l(全文表示)を付けます。
start・stop・restart・reloadの違い(止まる時間を実測)
この節では、4つの操作が何をするかと、restart と reload で利用者から見た止まり方がどう違うかが分かります。
hello.service は、/etc/hello/message.txt の文言を返す小さな Web アプリです。SIGHUP を受けると文言を読み直すように作り、unit ファイルに ExecReload=/bin/kill -HUP $MAINPID を書きました。文言を「Hello v2」に変えて reload と restart を続けて実行した結果です。
$ echo "Hello v2" > /etc/hello/message.txt
$ systemctl reload hello
$ curl -s http://127.0.0.1:8151/
Hello v2 (pid=174 GREETING=from-EnvironmentFile)
$ systemctl restart hello
$ curl -s http://127.0.0.1:8151/
Hello v2 (pid=187 GREETING=from-EnvironmentFile)
reload のあとは pid=174 のまま文言だけが変わり、restart のあとは pid=187 に変わりました。 reload はプロセスを止めず、restart は止めて新しいプロセスを起動します。
では、利用者から見てどれだけ止まるのか。20ms おきに curl で問い合わせながら、途中で restart と reload を1回ずつ実行しました(3秒間)。
| 操作 | プロセス | 止まっていたら | 向いている場面 |
|---|---|---|---|
| start | 新しく起動 | 起動する | 止まっているサービスを動かす |
| stop | SIGTERM で止める(応答が無ければ既定90秒後に SIGKILL) | 何もしない | 作業のために止める |
| restart | 止めて新しく起動(PID が変わる) | 起動する | プログラムの更新・unit ファイルの変更後 |
| reload | そのまま(ExecReload= を実行) | エラー | nginx・sshd などの設定ファイルを変えた後 |
reload は、ExecReload= が書かれているサービスだけで使えます。止まっているサービスに reload を送ると hello.service is not active, cannot reload. と断られました。どちらか迷うスクリプトでは reload-or-restart を使います。
短い間に restart を繰り返さない
Start request repeated too quickly. と出て failed(start-limit-hit)になったsystemctl is-active で確かめる。間隔を2.2秒あけた5回は問題なく通った既定では10秒間に5回まで(StartLimitIntervalSec=10s・StartLimitBurst=5。systemctl show で表示された値)
enable・disableで自動起動を設定する(--now・mask)
この節では、OS の起動時にサービスを自動で動かす設定と、その中身(シンボリックリンク)が分かります。
enable は「次に OS が起動したときに動かす」設定で、今すぐ起動はしません。 今すぐ起動もしたいときは --now を付けます。
$ systemctl enable hello
Created symlink /etc/systemd/system/multi-user.target.wants/hello.service → /etc/systemd/system/hello.service.
$ systemctl is-enabled hello
enabled
$ systemctl disable hello
Removed "/etc/systemd/system/multi-user.target.wants/hello.service".
$ systemctl stop hello
$ systemctl enable --now hello
Created symlink /etc/systemd/system/multi-user.target.wants/hello.service → /etc/systemd/system/hello.service.
$ systemctl is-active hello
active
出力のとおり、enable の正体は multi-user.target.wants というフォルダーにシンボリックリンクを置くことです。unit ファイルの [Install] にある WantedBy=multi-user.target がリンクを置く場所を決めています。[Install] が無い unit は enable できず、list-unit-files で static(ほかのユニットから呼ばれて動く)と表示されます。
mask:手動でも起動できなくする
disable は自動起動をやめるだけで、systemctl start や依存関係からは起動できます。どうしても起動させたくないときは mask を使います。redis-server で試しました。
$ systemctl mask redis-server
Created symlink /etc/systemd/system/redis-server.service → /dev/null.
$ systemctl start redis-server
Failed to start redis-server.service: Unit redis-server.service is masked.
$ systemctl unmask redis-server
Removed "/etc/systemd/system/redis-server.service".
mask は /etc/systemd/system に /dev/null へのリンクを置く仕組みなので、自分で /etc/systemd/system に置いた unit には使えません。hello.service に mask を打つと、次のように断られました。
$ systemctl mask hello
Failed to mask unit: File /etc/systemd/system/hello.service already exists.
is-active・is-enabled・is-failedと終了コード
この節では、シェルスクリプトや監視でサービスの状態を判定する方法と、終了コードの意味が分かります。
is-active などは、状態を1語で表示し、終了コード($?)でも結果を返します。表示を消すなら --quiet を付けます。
$ systemctl is-active hello; echo "exit=$?"
active
exit=0
$ systemctl is-enabled hello; echo "exit=$?"
disabled
exit=1
$ systemctl is-failed hello; echo "exit=$?"
active
exit=1
$ systemctl status helo; echo "exit=$?"
Unit helo.service could not be found.
exit=4
| 終了コード | status・is-active での意味 | 今回の例 |
|---|---|---|
| 0 | 動いている(active) | 動作中の status・is-active |
| 1 | is-failed で「失敗ではない」、is-enabled で「自動起動ではない」 | disabled のとき |
| 3 | 動いていない(inactive・failed) | 止めた後・失敗後の status |
| 4 | そのユニットが無い | 名前を打ち間違えた helo |
この番号は LSB(以前の init スクリプトの決まり)の値で、公式マニュアルは状態の文字列を直接見ることを勧めています。監視では systemctl is-active --quiet hello || 通知する の形が使いやすく、詳しい状態は systemctl show hello -p ActiveState -p SubState -p Result で取ります。
システム全体の状態は systemctl is-system-running で分かります。今回、失敗したユニットが6個残っている間は degraded(終了コード1)、systemctl reset-failed で記録を消すと running に戻りました。
list-units・list-unit-files・list-dependenciesで一覧を見る
この節では、サービスの一覧と、自動起動の設定・依存関係の見方が分かります。
2つの一覧は対象が違います。list-units は「今メモリに読み込まれているユニット」、list-unit-files は「ディスクにある unit ファイル」です。自動起動の設定を見るなら list-unit-files を使います。
$ systemctl list-unit-files --type=service
UNIT FILE STATE PRESET
hello.service disabled enabled
postgresql.service disabled enabled
postgresql@.service indirect enabled
redis-server.service enabled enabled
redis.service alias -
(略)
STATE 欄の値は、今回の環境(unit ファイル 274 個)では次の割合でした。
| STATE | 意味 | 件数 |
|---|---|---|
| static | [Install] が無い。ほかから呼ばれて動く。enable できない | 184 |
| disabled | 自動起動しない | 55 |
| alias | 別名(redis.service → redis-server.service など) | 15 |
| masked | 起動できなくしてある | 10 |
| indirect | テンプレート(名前@.service)など、間接的に有効 | 3 |
| enabled | 自動起動する | 3 |
絞り込みは --type=service・--state=running・--state=failed・--all が便利です。
依存関係は list-dependencies で木の形に表示されます。--reverse を付けると「このユニットに頼っている側」が出ます。
$ systemctl list-dependencies app-req
app-req.service
○ ├─demo-db.service
● ├─system.slice
● └─sysinit.target
● ├─dev-hugepages.mount
(略)
$ systemctl list-dependencies --reverse demo-db
demo-db.service
● └─app-wants.service
自作サービスのunitファイルの書き方(Restart・依存・環境変数)
この節では、自分のプログラムを systemd のサービスにする unit ファイルの書き方と、各項目が実際にどう効くかが分かります。
自作の unit ファイルは /etc/systemd/system/名前.service に置きます。パッケージが入れる unit は /usr/lib/systemd/system にあり、同じ名前なら /etc 側が優先されます。今回動かした hello.service の全文です。
[Unit]
Description=Hello sample web app (t151)
After=network.target
[Service]
Type=simple
EnvironmentFile=/etc/hello/hello.env
ExecStart=/usr/bin/python3 -u /opt/hello/app.py
ExecReload=/bin/kill -HUP $MAINPID
Restart=on-failure
RestartSec=2
User=hello
[Install]
WantedBy=multi-user.target
実行するユーザーを作る
useradd --system --no-create-home --shell /usr/sbin/nologin hello。root で動かさない。nobody を指定すると、検証で Special user nobody configured, this is not safe! と警告された。
unit ファイルを置いて検証する
systemd-analyze verify /etc/systemd/system/hello.service。書き間違いや存在しない実行ファイルを、起動前に見つけられる。
読み込ませる
systemctl daemon-reload。新しいファイルや変更を systemd に読み直させる。
起動して確かめる
systemctl start hello のあと systemctl status hello と journalctl -u hello。Python は -u を付けないと出力がバッファーに溜まり、ログがすぐに出ない。
自動起動にする
systemctl enable hello。動作を確かめてから有効にする。
Type=:起動完了をいつとみなすか
| Type | 起動完了とみなす時点 | 向いているもの |
|---|---|---|
| simple(既定) | プロセスを作った直後 | 前面で動き続けるプログラム |
| exec | 実行ファイルの起動に成功した時 | simple の代わり。起動失敗を start の結果に出したいとき |
| notify | プログラムが「準備できた」と通知した時 | sd_notify に対応したサーバー |
| forking | 起動したプロセスが子を残して終わった時 | 自分でデーモンになる以前のプログラム |
| oneshot | 処理が終わった時 | バックアップなど1回だけの処理・timer から呼ぶもの |
Type=simple では、実行ファイルが無くても systemctl start は成功(終了コード0)を返しました。 失敗は直後の status で分かります。Type=exec に変えると、同じ間違いで start が終了コード1を返しました。公式マニュアルにも「simple では実行ファイルを起動できなくても start は成功を報告する」と書かれています。
Type=simple: $ systemctl start bad-exec → exit=0(直後に status=203/EXEC で failed)
Type=exec: $ systemctl start bad-exec2
Job for bad-exec2.service failed because the control process exited with error code.
See "systemctl status bad-exec2.service" and "journalctl -xeu bad-exec2.service" for details.
→ exit=1
Restart=:落ちたら起動し直す
アプリの /crash に問い合わせて、終了コード3でわざと落としました。Restart=on-failure と RestartSec=2 によって、落ちてから 2.18 秒後に新しいプロセス(PID 305)で起動し直されました。
14:44:49.826 python3[296]: crash requested
14:44:49.827 systemd[1]: hello.service: Main process exited, code=exited, status=3/NOTIMPLEMENTED
14:44:49.827 systemd[1]: hello.service: Failed with result 'exit-code'.
14:44:51.979 systemd[1]: hello.service: Scheduled restart job, restart counter is at 1.
14:44:52.006 systemd[1]: Started hello.service - Hello sample web app (t151).
| Restart= | 起動し直す場合 | 使いどころ |
|---|---|---|
| no(既定) | しない | 落ちたら人が見るもの |
| on-failure | 0以外で終了・シグナル・タイムアウト・ウォッチドッグ | Web アプリ・API の多く |
| on-abnormal | シグナル・タイムアウト・ウォッチドッグ(終了コードは対象外) | 自分で異常終了コードを返すもの |
| always | 正常終了も含めて必ず | 止まっては困る常駐プログラム |
何度も落ちると、10秒間に5回の上限で起動をあきらめます。RestartSec を 100ms にして7回落とすと、6回目で次のように止まりました。
hello.service: Scheduled restart job, restart counter is at 6.
hello.service: Start request repeated too quickly.
hello.service: Failed with result 'exit-code'.
Failed to start hello.service - Hello sample web app (t151).
この状態で systemctl start hello を打っても Job for hello.service failed because the control process exited with error code. と出て起動しません(メッセージは紛らわしいですが、ログには Start request repeated too quickly. が出ます)。systemctl reset-failed hello で記録を消すと、すぐに起動できました。
After=・Requires=・Wants=:依存関係
After= は「順番」、Requires= と Wants= は「一緒に起動するか」を決めます。両方書くのが基本です。DB 役の demo-db と、それに Requires= で頼る app-req、Wants= で頼る app-wants を作り、demo-db を止めました。
$ systemctl start app-req app-wants → demo-db も一緒に起動
$ systemctl stop demo-db
$ systemctl is-active demo-db app-req app-wants
inactive
inactive
active
Requires= の app-req は一緒に止められ、Wants= の app-wants は動き続けました。 相手が止まったら自分も止まるべきなら Requires=、相手が無くても動けるなら Wants= を選びます。network.target は「ネットワークの設定が始まった」だけを意味し、つながったことは保証しません。接続後に起動したいときは network-online.target を After= と Wants= の両方に書きます。
Environment=・EnvironmentFile=:環境変数
環境変数は unit ファイルに Environment=KEY=値 で書くか、EnvironmentFile= で別ファイルから読みます。パスワードなどは unit ファイルに書かず、権限を絞った別ファイルに置きます。
ここで1つ、つまずきやすい動きを確かめました。hello.env に GREETING=from-EnvironmentFile を書いたまま、ドロップインで Environment=GREETING=from-drop-in を足して restart すると、アプリに届いた値は from-EnvironmentFile のままでした。同じ変数が両方にあるときは EnvironmentFile= の値が使われます。上書きしたいときは、EnvironmentFile= のファイル側を書き換えます。
systemctl editとdaemon-reloadで設定を変える
この節では、パッケージの unit ファイルを直接書き換えずに設定を変える方法(ドロップイン)と、daemon-reload が要る場面が分かります。
/usr/lib/systemd/system の unit ファイルは、パッケージの更新で上書きされます。 設定を変えるときは systemctl edit 名前 で「差分だけ」のファイル(ドロップイン)を作ります。保存先は /etc/systemd/system/名前.service.d/override.conf で、保存すると自動で daemon-reload されます。
edit を開くと、次のひな形がエディターに表示されました(後半は本体と既存のドロップインの内容がコメントで並びます)。
### Editing /etc/systemd/system/hello.service.d/extra.conf
### Anything between here and the comment below will become the contents of the drop-in file
### Edits below this comment will be discarded
### /etc/systemd/system/hello.service
# [Unit]
# Description=Hello sample web app (t151)
(略)
書くのは2つのコメントの間です。 下のコメントより後ろに書いた内容は捨てられます。ファイルの末尾に追記するエディター操作をしたところ、エラーも出ずにドロップインが作られませんでした。間に書いて保存すると Successfully installed edited file '/etc/systemd/system/hello.service.d/extra.conf'. と表示されました。
| コマンド | 作られるもの | 元に戻す |
|---|---|---|
systemctl edit hello | hello.service.d/override.conf(差分) | systemctl revert hello |
systemctl edit --drop-in=名前 hello | hello.service.d/名前.conf | 同上 |
systemctl edit --full hello | /etc に本体の写し(全文を置き換え) | 同上 |
systemctl edit --runtime hello | /run に置く(再起動で消える) | 再起動 |
systemctl revert hello を実行すると、ドロップインがすべて消えました。
$ systemctl revert hello
Removed "/etc/systemd/system/hello.service.d/exec.conf".
Removed "/etc/systemd/system/hello.service.d/extra.conf".
Removed "/etc/systemd/system/hello.service.d/hardening.conf".
Removed "/etc/systemd/system/hello.service.d/override.conf".
Removed "/etc/systemd/system/hello.service.d".
ExecStart= を変えるときは空の行を先に書く
ドロップインに ExecStart= を1行だけ書くと、本体の ExecStart= に「追加」されて2つになり、起動を断られました。
Failed to restart hello.service: Unit hello.service has a bad unit file setting.
hello.service: Service has more than one ExecStart= setting, which is only allowed for Type=oneshot services. Refusing.
先に空の ExecStart= を書いて一覧を消してから、新しい値を書きます。
[Service]
ExecStart=
ExecStart=/usr/bin/python3 -u /opt/hello/app.py
daemon-reload が要る場面
unit ファイルやドロップインをエディターで直接書き換えたときは、systemd がまだ前の内容を覚えています。この状態で status を見ると、先頭に次の警告が出ました。
Warning: The unit file, source configuration file or drop-ins of hello.service changed on disk. Run 'systemctl daemon-reload' to reload units.
systemctl daemon-reload は unit ファイルを読み直すだけで、動いているサービスは止めません。新しい設定でプロセスを動かすには、続けて restart します。daemon-reload(unit ファイルを読み直す)と reload(アプリに設定ファイルを読み直させる)は別物です。
journalctlでサービスのログを見る
この節では、systemd で動かしたサービスのログを journalctl で絞り込んで見る基本が分かります。
systemd のサービスが標準出力・標準エラーに書いた内容は、journald が集めます。ログファイルの場所を探さなくても、ユニット名で取り出せます。
$ journalctl -u hello --since "-2min" -n 3
2026-10-30T14:49:44+00:00 vm python3[2065]: access: "GET / HTTP/1.1" 200 -
2026-10-30T14:49:44+00:00 vm python3[2065]: access: "GET / HTTP/1.1" 200 -
2026-10-30T14:49:44+00:00 vm python3[2065]: access: "GET / HTTP/1.1" 200 -
| やりたいこと | コマンド |
|---|---|
| サービスのログ(新しい50行) | journalctl -u hello -n 50 |
| 流れてくるログを追う | journalctl -u hello -f |
| 今回の起動以降だけ | journalctl -u hello -b |
| 時間で絞る | journalctl -u hello --since "10 min ago" |
| 警告以上だけ | journalctl -p warning -b |
| 失敗の説明つきで末尾へ | journalctl -xeu hello |
エラーメッセージで案内される journalctl -xeu の -x は説明文を足すオプションです。bad-user.service で実行すると、行ごとに次のような説明が付きました。
Oct 30 14:45:37 vm systemd[1]: bad-user.service: Main process exited, code=exited, status=217/USER
░░ Subject: Unit process exited
░░ Defined-By: systemd
░░
░░ An ExecStart= process belonging to unit bad-user.service has exited.
░░
░░ The process' exit code is 'exited' and its exit status is 217.
容量の管理や出力形式(JSON)など journalctl の詳しい使い方は、関連記事「journalctlの使い方|目的別コマンド逆引き一覧と絞り込み例」で扱っています。
systemd timerでcronの代わりに定期実行する
この節では、systemd の timer で決まった時刻・間隔に処理を動かす方法と、cron との違いが分かります。
timer は 実行する中身(.service)と、いつ動かすか(.timer)を同じ名前の2つのファイルに分けて書きます。毎日3時のバックアップの例です。
# /etc/systemd/system/backup.service
[Unit]
Description=nightly backup (sample)
[Service]
Type=oneshot
ExecStart=/bin/sh -c "tar -czf /var/tmp/hello-$(date +%%F).tgz /etc/hello && echo backup ok"
# /etc/systemd/system/backup.timer
[Unit]
Description=run backup daily at 03:00
[Timer]
OnCalendar=*-*-* 03:00:00
Persistent=true
RandomizedDelaySec=10min
[Install]
WantedBy=timers.target
unit ファイルの中では % が特別な意味を持つため、date の書式は %%F と2つ重ねます。有効にするのは .service ではなく .timer です。
$ systemctl enable --now backup.timer
Created symlink /etc/systemd/system/timers.target.wants/backup.timer → /etc/systemd/system/backup.timer.
$ systemctl list-timers backup.timer
NEXT LEFT LAST PASSED UNIT ACTIVATES
Sat 2026-10-31 03:02:41 UTC 12h - - backup.timer backup.service
$ systemctl start backup.service ← 時刻を待たずに今すぐ試す
$ journalctl -u backup.service -o cat
Starting backup.service - nightly backup (sample)...
tar: Removing leading `/' from member names
backup ok
backup.service: Deactivated successfully.
Finished backup.service - nightly backup (sample).
NEXT が 03:00 ではなく 03:02:41 なのは、RandomizedDelaySec=10min で0〜10分の範囲でずらしたためです。多くのサーバーが同時に動くのを避けるための設定です。
OnCalendar の書き方は systemd-analyze calendar で確かめる
$ systemd-analyze calendar --iterations=2 "Mon..Fri *-*-* 09:00:00"
Normalized form: Mon..Fri *-*-* 09:00:00
Next elapse: Mon 2026-11-02 09:00:00 UTC
From now: 2 days left
Iteration #2: Tue 2026-11-03 09:00:00 UTC
$ systemd-analyze calendar "every day 9am"
Failed to parse calendar specification 'every day 9am': Invalid argument
| 動かしたい時 | OnCalendar= | cron で書くと |
|---|---|---|
| 毎日0時 | daily(= *-*-* 00:00:00) | 0 0 * * * |
| 平日の9時 | Mon..Fri *-*-* 09:00:00 | 0 9 * * 1-5 |
| 15分ごと | *-*-* *:00/15:00 | */15 * * * * |
| 土日の3時半 | Sat,Sun 03:30 | 30 3 * * 0,6 |
| 起動の5分後から1時間ごと | OnBootSec=5min と OnUnitActiveSec=1h | 書けない |
timer は「ちょうどその時刻」には動かない(実測)
systemd-run --on-active=2s で「2秒後に1回だけ」の一時的な timer を作り、実際に動いた時刻を測りました。
timer は、CPU を起こす回数を減らすために、既定で最大1分の幅で実行をまとめます(AccuracySec=1min)。 秒単位で正確に動かしたいときは、.timer に AccuracySec=1s などを書きます。
1ログが journald に残る
cron はメールや自前のリダイレクトが要るが、timer なら journalctl -u backup で結果と終了コードが見られる。
2電源オフ中の分を取り戻せる
Persistent=true なら、止まっていた間に過ぎた回を起動後に1回動かす(OnCalendar= のときだけ)。
3二重に動かない
前の回の .service が動いている間は、次の回を重ねて起動しない。
cron の書式や落とし穴は、関連記事「cronの書き方|設定例30パターンの早見表と落とし穴まとめ」で扱っています。
failedになったときの切り分け手順
この節では、サービスが failed になったときに、どの順番で何を見れば原因に近づけるかが分かります。
わざと失敗させた unit の status は、次のようになりました。Process 行の status= を見るだけで、失敗の場所が分かれます。
| わざと入れた間違い | status の表示 | journal に出た原文 |
|---|---|---|
| ExecStart= のファイルが無い | status=203/EXEC | Main process exited, code=exited, status=203/EXEC |
| User= のユーザーが無い | status=217/USER | Failed to determine user credentials: No such process |
| WorkingDirectory= のフォルダーが無い | status=200/CHDIR | Changing to the requested working directory failed: No such file or directory |
| ポートが使用中 | status=1/FAILURE | OSError: [Errno 98] Address already in use |
| Type=forking なのに前面で動き続ける | Result: timeout | start operation timed out. Terminating. |
| 10秒に6回落ちた | Result: exit-code(手動の連打なら start-limit-hit) | Start request repeated too quickly. |
この環境では、203/EXEC のときに「どのファイルが無いか」を示す行は journal に出ませんでした。status の Process 行に ExecStart= の内容が出るので、そのパスを ls -l で確かめます。systemd-analyze verify なら bad-exec.service: Command /opt/hello/no-such-file is not executable: No such file or directory と、起動前にファイル名つきで教えてくれました。
ポートの使用中を調べる方法は関連記事「Address already in useの解決方法【Windows・Mac・Linux】」、プロセスの確かめ方と止め方は関連記事「Linuxプロセス確認と停止|ps・top・kill逆引きコマンド一覧」で扱っています。systemd で動かしているプロセスを kill で止めると、Restart= の設定によってはすぐ起動し直されます。止めるときは systemctl stop を使います。
よくあるエラーと直し方(原文つき)
この節では、systemctl を使っていて出るエラーの原文と、原因・直し方が一覧で分かります。すべて今回の環境で実際に出たものです。
| エラーの原文 | 原因 | 直し方 |
|---|---|---|
| System has not been booted with systemd as init system (PID 1). Can't operate. | Docker のコンテナ・設定前の WSL など、PID 1 が systemd ではない | コンテナではプログラムを直接起動する。WSL は wsl.conf で有効にする(よくある質問) |
| Failed to start helo.service: Unit helo.service not found. | 名前の打ち間違い・unit ファイルが無い・置き場所の違い | systemctl list-unit-files | grep 名前 で探す |
| Failed to restart hello.service: Interactive authentication required. | 一般ユーザーで状態を変えようとした | sudo を付ける |
| Failed to start redis-server.service: Unit redis-server.service is masked. | mask されている | 意図を確かめて systemctl unmask |
| Failed to mask unit: File /etc/systemd/system/hello.service already exists. | /etc に置いた自作 unit を mask しようとした | disable と stop を使う |
| Warning: The unit file, source configuration file or drop-ins of hello.service changed on disk. | ファイルを直接編集して読み直していない | systemctl daemon-reload |
| Unit hello.service has a bad unit file setting. | ドロップインで ExecStart= が2つになった など | 空の ExecStart= を先に書く |
| Unknown key name 'Descripton' in section 'Unit', ignoring. | 項目名の綴り違い(その行は無視されて起動はする) | systemd-analyze verify で見つけて直す |
| Missing '=', ignoring line. | = の無い行 | 同上 |
| Failed to parse service restart specifier, ignoring: sometimes | Restart= に無い値 | no・on-failure・always などにする |
| Job for hello.service failed because the control process exited with error code. | 起動に失敗(起動回数の上限のときも同じ文言) | status と journalctl -xeu を見る |
| Job for bad-timeout.service failed because a timeout was exceeded. | 起動完了の合図が来ない(Type の選び間違い・起動が遅い) | Type= を見直す・TimeoutStartSec= を延ばす |
| hello.service: Start request repeated too quickly. | 10秒に5回を超えて起動した | 原因を直して reset-failed |
| hello.service is not active, cannot reload. | 止まっているサービスに reload | start か reload-or-restart |
| Cannot edit units if not on a tty. | スクリプトなど端末の無い所で systemctl edit | ドロップインのファイルを直接置いて daemon-reload |
| Special user nobody configured, this is not safe! | User=nobody(ほかのプログラムと共有のユーザー) | 専用のユーザーか DynamicUser=yes |
| Failed to parse calendar specification 'every day 9am': Invalid argument | OnCalendar= の書式違い | systemd-analyze calendar で試す |
一般ユーザーで status は見られますが、start・stop・restart は断られます。サービス用のユーザーを作る手順と sudo の権限の付け方は、関連記事「Linuxユーザー追加とsudo権限付与|useradd手順と確認コマンド一覧」で扱っています。
ExecStart= に相対パスを書いたとき
systemd 255 では ExecStart=python3 /opt/hello/app.py のように名前だけでも起動できます。ただし探す場所は /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin の順で(systemd-path search-binaries-default で表示)、シェルの PATH とは違います。今回の環境では /usr/bin/python3 ではなく /usr/local/bin/python3 が使われました(ログのファイル名が /usr/local/lib/python3.13/ になっていた)。意図した版を確実に使うため、絶対パスで書きます。
安全のための設定(systemd-analyze security)
systemd-analyze security hello は、サービスに付けた制限を点数にします(10に近いほど制限が少ない)。hello.service は 9.2 UNSAFE でしたが、次のドロップインを足すと 3.5 OK になり、アプリはそのまま動きました。
[Service]
NoNewPrivileges=yes
ProtectSystem=strict
ProtectHome=yes
PrivateTmp=yes
PrivateDevices=yes
ProtectKernelTunables=yes
ProtectKernelModules=yes
ProtectControlGroups=yes
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX
RestrictNamespaces=yes
LockPersonality=yes
MemoryDenyWriteExecute=yes
RestrictRealtime=yes
SystemCallArchitectures=native
CapabilityBoundingSet=
UMask=0077
ProtectSystem=strict は OS のファイルを読み取り専用にするため、書き込みが要るアプリは ReadWritePaths= で場所を足します。
serviceコマンド・SysVinitとの違いと判断表
この節では、以前の service・chkconfig・/etc/init.d との対応と、場面ごとにどのコマンドを使えばよいかが分かります。
Ubuntu の service コマンドは、systemd が動いていれば内部で systemctl を呼びます(/usr/sbin/service の中に exec systemctl $sctl_args ${ACTION} ${UNIT} の行がある)。service hello status を実行すると、systemctl status hello と同じ出力になりました。
| やりたいこと | 以前(SysVinit) | systemd |
|---|---|---|
| 起動・停止 | service hello start・/etc/init.d/hello stop | systemctl start hello |
| 自動起動にする | chkconfig hello on・update-rc.d hello enable | systemctl enable hello |
| 一覧 | service --status-all・chkconfig --list | systemctl list-unit-files |
| ランレベルを変える | init 3 | systemctl isolate multi-user.target |
| 既定のランレベル | /etc/inittab | systemctl get-default・set-default |
ランレベルは target に置き換わり、互換の名前がリンクで残っています(/usr/lib/systemd/system/runlevel*.target)。今回の環境では、0→poweroff.target、1→rescue.target、2〜4→multi-user.target、5→graphical.target、6→reboot.target でした。
最後に、場面ごとの判断表です。
| 場面 | 使うもの | 理由 |
|---|---|---|
| nginx の設定を変えた | systemctl reload nginx | 接続を切らずに読み直せる |
| アプリを新しい版に入れ替えた | systemctl restart | プロセスを新しくする必要がある |
| unit ファイルを直接直した | daemon-reload → restart | 読み直さないと前の設定のまま |
| パッケージの unit を少し変えたい | systemctl edit | 更新で上書きされない |
| デプロイのスクリプトで状態を判定 | is-active --quiet | 終了コードで分岐できる |
| 毎日決まった時刻にバッチ | timer(ログを残したい)か cron(手軽さ優先) | timer は journal に残り、二重起動しない |
| 一般ユーザーの常駐プログラム | systemctl --user | sudo なしで管理できる |
| Docker のコンテナの中 | systemctl を使わない | コンテナは1プロセスが基本。PID 1 が systemd ではない |
systemctl --user:ユーザーごとのサービス
一般ユーザーは、~/.config/systemd/user に unit を置いて systemctl --user で自分のサービスを管理できます。ユーザー dev を作って試しました。
$ systemctl --user enable --now mytool
Created symlink /home/dev/.config/systemd/user/default.target.wants/mytool.service → /home/dev/.config/systemd/user/mytool.service.
$ systemctl --user status mytool
● mytool.service - user-level sample
Loaded: loaded (/home/dev/.config/systemd/user/mytool.service; enabled; preset: enabled)
Active: active (running) since Fri 2026-10-30 14:48:18 UTC; 8ms ago
ログアウト中も動かすには、root で loginctl enable-linger dev を実行します(今回はこれでユーザー用の systemd が起動しました)。
よくある質問
Q. systemctl とは何ですか? A. systemd(Linux の起動とサービスを管理する仕組み)に指示を出すコマンドです。サービスの起動・停止・再起動・自動起動の設定・状態の確認を行います。systemd 本体ではなく、打ったときだけ動く窓口です。
Q. systemd とは何ですか? A. Linux で最初に起動するプロセス(PID 1)として動き、サービス・マウント・タイマーなどを「ユニット」として管理するソフトウェアです。以前の SysVinit に代わり、主なディストリビューションの標準になっています。
Q. restart と reload の違いは? A. restart はプロセスを止めて新しく起動し、reload はプロセスを止めずに設定だけ読み直させます。今回の測定では、restart 中は問い合わせが5回失敗し、reload 中は0回でした。reload は ExecReload= があるサービスだけで使えます。
Q. enable したのに起動していません。
A. enable は「次の OS 起動から自動で動かす」設定で、今すぐは起動しません。systemctl enable --now 名前 にすると、今すぐの起動も同時に行います。
Q. daemon-reload はいつ必要ですか?
A. unit ファイルやドロップインをエディターで直接作った・書き換えたときです。systemctl edit で保存した場合は自動で行われます。daemon-reload だけでは動いているプロセスは変わらないので、続けて restart します。
Q. status で failed と出ました。まず何を見ればよいですか?
A. Process 行の status=番号/名前 です。203/EXEC・217/USER のように200番台なら unit ファイルの設定(実行ファイル・ユーザー)、1/FAILURE などならプログラム自身のエラーなので journalctl -u 名前 でアプリの出力を読みます。
Q. Start request repeated too quickly と出て起動できません。
A. 既定の上限(10秒間に5回)を超えて起動したため、systemd が起動を止めています。落ちる原因を直してから systemctl reset-failed 名前 を実行すると、また起動できます。
Q. Docker のコンテナの中で systemctl が使えません。
A. コンテナの PID 1 は systemd ではないため、System has not been booted with systemd as init system (PID 1). Can't operate. と出ます。コンテナでは ENTRYPOINT・CMD でプログラムを直接起動し、再起動はコンテナの再起動の設定に任せるのが基本です。
Q. WSL で systemctl を使うには?
A. Microsoft Learn によると、WSL 0.67.6 以降で使えます。wsl --install で入れた Ubuntu は既定で有効です。ほかのディストリビューションでは /etc/wsl.conf に [boot] と systemd=true を書き、PowerShell で wsl.exe --shutdown を実行してから開き直します。systemctl status で状態が表示されれば有効です。
まとめ
- systemd は PID 1 として動くサービスの管理役で、systemctl はそこへ指示を出すコマンド。PID 1 が systemd でないコンテナでは使えない。
- まず
systemctl statusの Loaded・Active・Process 行と末尾のログを読む。ログの続きはjournalctl -u 名前。 - restart は止めて起動し直し(今回の測定で問い合わせ5回失敗)、reload は止めずに読み直す(0回)。enable は次の起動からで、今すぐなら
--now。 - 自作の unit は /etc/systemd/system に置き、
systemd-analyze verify→ daemon-reload → start → enable の順。Type=simple は起動失敗でも start が成功を返すので、Type=exec も検討する。 - failed は status=番号で切り分け、直したら reset-failed。timer は .service と .timer の組で作り、既定で最大1分ずれる点に気を付ける。
参考・出典
いずれも確認日は 2026年10月29日です。
- systemd 公式マニュアル「systemctl」:https://www.freedesktop.org/software/systemd/man/latest/systemctl.html
- systemd 公式マニュアル「systemd.service」:https://www.freedesktop.org/software/systemd/man/latest/systemd.service.html
- systemd 公式マニュアル「systemd.timer」:https://www.freedesktop.org/software/systemd/man/latest/systemd.timer.html
- systemd 公式マニュアル「systemd.exec」:https://www.freedesktop.org/software/systemd/man/latest/systemd.exec.html
- Microsoft Learn「Use systemd to manage Linux services with WSL」:https://learn.microsoft.com/ja-jp/windows/wsl/systemd
- Red Hat「第10章 systemd によるサービス管理」(RHEL 7 システム管理者のガイド):https://docs.redhat.com/ja/documentation/Red_Hat_Enterprise_Linux/7/html/system_administrators_guide/chap-managing_services_with_systemd
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「systemctlコマンドの使い方とsystemdとは|statusとエラー原文」
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。