systemdのserviceファイルを自作して常駐プロセスを管理する

※本ページには広告(アフィリエイトプログラム等)が含まれます。

※本ページには広告(アフィリエイトプログラム等)が含まれます。掲載しているコマンドの出力は 2026年8月16日時点で運用中の VPS(Debian 12・メモリ 958MB)から採取した実測値です。ユーザー名はマスクしています。

🎯 結論:常駐させたいなら unit ファイル 10 行。既存サービスの変更は Drop-In で上書きする

結論から書きます。VPS で自作のスクリプトを常駐させたいなら、`nohup` でも `screen` でもなく systemd の unit ファイルを書くのが正解です。10 行ほどで、再起動後の自動復帰・異常終了時の再起動・ログの集約がまとめて手に入ります。

そしてもう 1 つ、意外と知られていないのが既存サービス(nginx や php-fpm)の設定を変えたいとき、元の unit ファイルを編集してはいけないという点です。パッケージ更新で上書きされます。正解は Drop-In です。

  • 自作スクリプトを常駐 → `/etc/systemd/system/<name>.service` を作る
  • 落ちても復帰させたい`Restart=always` と `RestartSec`
  • 既存サービスの設定変更Drop-In(`systemctl edit`)。元ファイルは触らない
  • ログを見たい → `journalctl -u <name> -f`。標準出力が自動で入る
  • ⚠️ メモリの少ない VPS → サービスごとの使用量を測ってから増やす
  • `nohup … &` で放置 → 再起動で消える。落ちても気づけない

まず現状を測る

いくつサービスが動いているか

手を入れる前に、いま何が動いているかを数えます。

$ systemctl list-units --type=service --state=running --no-pager --no-legend | wc -l
19

メモリ 958MB の VPS で 19 個。ここに自作のサービスを足していくので、余裕がどれくらいあるかを先に把握しておきます。

どのサービスがメモリを食っているか

systemd はサービス単位でメモリ使用量を持っているので、そのまま集計できます。

$ systemctl list-units --type=service --state=running --no-pager --no-legend \
  | awk '{print $1}' \
  | while read u; do
      m=$(systemctl show "$u" -p MemoryCurrent --value)
      [ "$m" != "[not set]" ] && echo "$m $u"
    done | sort -rn | head -6 \
  | awk '{printf "%8.1f MB  %s\n", $1/1048576, $2}'

実際の出力がこれです。

メモリ サービス
325.0 MB php8.3-fpm.service
203.1 MB cron.service
75.7 MB mariadb.service
21.7 MB nginx.service
18.3 MB systemd-journald.service
15.8 MB webmin.service

php-fpm が 325MB で突出しています。全体 958MB のうち 3 分の 1 です。ここに 7B クラスの LLM を載せようとしても入らない、といった判断がこの表からできます。

起動にかかっている時間

$ systemd-analyze
Startup finished in 1.788s (kernel) + 4.451s (userspace) = 6.240s

$ systemd-analyze blame | head -6
8.753s chkrootkit.service
1.993s networking.service
1.522s nginx.service
1.500s [email protected]
1.408s php8.3-fpm.service
1.250s mariadb.service

ここで引っかかる人が多いので補足します。`blame` の合計は起動時間と一致しません。chkrootkit が 8.753 秒かかっているのに全体は 6.240 秒です。並列に起動していて、しかも起動完了の判定を待たないユニットがあるからです。`blame` は「遅いユニット探し」には使えますが、足し算してはいけません。

自作スクリプトを常駐させる unit ファイル

最小構成

`/etc/systemd/system/myapp.service` を作ります。これだけで動きます。

[Unit]
Description=My background worker
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=<user>
WorkingDirectory=/home/<user>/myapp
ExecStart=/usr/bin/python3 /home/<user>/myapp/worker.py
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target

各行が何をしているか

ディレクティブ 役割 省くとどうなるか
After / Wants=network-online.target ネットワーク確立後に起動 起動直後に接続失敗して落ちる
User 実行ユーザー root で動く(危険)
WorkingDirectory カレントディレクトリ 相対パスの読み込みが全部ずれる
ExecStart 実行コマンド(絶対パス必須 PATH が対話シェルと違うので見つからない
Restart=always 落ちたら再起動 落ちたまま止まる
RestartSec 再起動までの待ち 即死ループで CPU を焼く

とくに `ExecStart` は絶対パスが要点です。systemd の実行環境は対話シェルと PATH が違うので、`python3` と書いただけでは見つからないことがあります。

有効化して起動する

$ sudo systemctl daemon-reload
$ sudo systemctl enable --now myapp
$ systemctl status myapp --no-pager
$ journalctl -u myapp -f

`daemon-reload` を忘れると、ファイルを直したのに反映されません。変更したら必ず打ちます。標準出力は自動で journal に入るので、ログ出力の仕組みを自前で書く必要はありません。

既存サービスの設定は Drop-In で変える

元の unit ファイルを編集してはいけない

`/lib/systemd/system/php8.3-fpm.service` のようなパッケージが配布しているファイルを直接編集すると、更新で上書きされます。気づかないうちに設定が消えるので、事故として最悪の部類です。

Drop-In なら追記で上書きできる

正解は `/etc/systemd/system/<name>.service.d/` に断片を置くことです。実際にこの VPS で使っている例がこれです。

$ cat /etc/systemd/system/php8.3-fpm.service.d/umask.conf
[Service]
UMask=0002

たった 2 行です。これで php-fpm が作るファイルのパーミッションが変わり、Web サーバーと CLI の両方から書けるようになります。元のファイルは 1 文字も触っていません。

合成結果を確認する

Drop-In が効いているかは `systemctl cat` で見ます。元ファイルと断片が順に表示され、後ろが勝ちます

$ systemctl cat php8.3-fpm | tail -8
RestrictNamespaces=true

[Install]
WantedBy=multi-user.target

# /etc/systemd/system/php8.3-fpm.service.d/umask.conf
[Service]
UMask=0002

`status` にも `Drop-In:` の行が出るので、こちらでも確認できます。

$ systemctl status php8.3-fpm --no-pager | head -8
● php8.3-fpm.service - The PHP 8.3 FastCGI Process Manager
     Loaded: loaded (/lib/systemd/system/php8.3-fpm.service; enabled; preset: enabled)
    Drop-In: /etc/systemd/system/php8.3-fpm.service.d
             └─umask.conf
     Active: active (running) since Sun 2026-08-16 17:00:37 JST; 4h 56min ago
   Main PID: 691 (php-fpm8.3)
     Status: "Processes active: 0, idle: 5, Requests: 19379, slow: 0, Traffic: 2.20req/sec"
     Memory: 324.9M

`systemctl edit php8.3-fpm` を使うと、この断片ファイルを対話的に作れます。手で `mkdir` する必要はありません。

メモリの少ない VPS で気をつけること

制限をかけて巻き添えを防ぐ

メモリ 958MB のような環境では、1 つのプロセスが暴走すると関係ないサービスまで OOM Killer に殺されます。自作サービスには上限を付けておくと安全です。

[Service]
MemoryMax=200M
MemoryHigh=150M
CPUQuota=50%

Restart の暴走を止める

`Restart=always` は便利ですが、起動直後に必ず落ちる不具合が入ると永久に再起動し続けます。回数の上限を付けます。

[Unit]
StartLimitIntervalSec=300
StartLimitBurst=5

止めたいときは disable まで

`stop` だけだと再起動で戻ってきます。恒久的に止めるなら `disable` まで打ちます。

$ sudo systemctl stop myapp
$ sudo systemctl disable myapp

よくある質問

Q1. cron と systemd、どちらで常駐させるべきですか?

結論:常駐なら systemd、定時実行なら cron か systemd timer です。ずっと動き続けるプロセスを cron で起こすと二重起動の管理が必要になります。`Restart=always` で自動復帰できる systemd のほうが素直です。

Q2. unit ファイルを直したのに反映されません

結論:`sudo systemctl daemon-reload` を打っていないからです。systemd は unit ファイルをメモリに読み込んで持っているので、ファイルを変えただけでは反映されません。変更したら必ず `daemon-reload` → `restart` の順で打ちます。

Q3. ExecStart のコマンドが見つからないと言われます

結論:絶対パスで書いてください。systemd の実行環境は対話シェルと PATH が異なります。`which python3` で確認した絶対パス(例:`/usr/bin/python3`)をそのまま書くのが確実です。

Q4. 既存サービスの設定を変えたいときは?

結論:Drop-In を使います。`/lib/systemd/system/` のファイルを直接編集するとパッケージ更新で上書きされます。`systemctl edit <name>` で `/etc/systemd/system/<name>.service.d/` に断片を作り、`systemctl cat` で合成結果を確認してください。

Q5. systemd-analyze blame の合計が起動時間と合いません

結論:並列に起動しているので合いません。実際にこの VPS では blame の最上位が 8.753 秒なのに、起動全体は 6.240 秒でした。blame は遅いユニットを見つける道具であって、足し算する表ではありません。

Q6. ログはどこに出ますか?

結論:標準出力・標準エラーがそのまま journal に入ります。`journalctl -u <name> -f` で追えます。ログファイルの出力先を自前で設計する必要はありません。

まとめ

systemd の unit ファイルは 10 行ほどで書けて、再起動後の自動復帰・異常終了時の再起動・ログ集約がまとめて手に入ります。`nohup` で放置する理由はもうありません。

  • 🎯 自作スクリプトの常駐 → `/etc/systemd/system/` に unit を作り `enable –now`
  • 🎯 既存サービスの変更Drop-In(実例:`UMask=0002` の 2 行)
  • 🎯 メモリの少ない VPS → `MemoryMax` と `StartLimitBurst` で巻き添えを防ぐ

出力はすべて 2026年8月16日時点で運用中の VPS から採取したものです。ログの追い方はAI を使ったログの切り分け、サーバーの初期構築はさくらの VPS の初期セットアップ、メモリが足りないときはスワップの追加にまとめています。

読んで頂いて有り難うございます!