systemd ユニットファイルを書く - 自作サービスの登録

systemd ユニットファイルを書く - 自作サービスの登録

systemd ユニットファイルとは?

systemd ユニットファイルは、サービス・マウントポイント・タイマーなどをシステムに登録するための設定ファイルだ。.service 拡張子のファイルを /etc/systemd/system/ に置くことで、自作スクリプトやアプリをデーモンとして管理できる。

結論

  • ユニットファイルの置き場所: /etc/systemd/system/myapp.service
  • 3 セクション構成: [Unit] / [Service] / [Install]
  • 作成・変更後は systemctl daemon-reload が必須

ユニットファイルの基本構造

ユニットファイルは 3 つのセクションで構成される。

[Unit]
Description=My Custom Service
After=network.target

[Service]
ExecStart=/usr/local/bin/myapp
Restart=on-failure
User=myuser

[Install]
WantedBy=multi-user.target
セクション 役割
[Unit] サービスの説明・起動順序・依存関係
[Service] 起動コマンド・再起動設定・実行ユーザー
[Install] systemctl enable の対象ターゲット

1. ミニマル構成で自作サービスを登録する

最短手順でシェルスクリプトをサービス化する。

1-1. スクリプトを準備する

sudo tee /usr/local/bin/myapp.sh > /dev/null << 'EOF'
#!/bin/bash
while true; do
    echo "$(date) running" >> /var/log/myapp.log
    sleep 10
done
EOF
sudo chmod +x /usr/local/bin/myapp.sh

1-2. ユニットファイルを作成する

sudo tee /etc/systemd/system/myapp.service > /dev/null << 'EOF'
[Unit]
Description=My Application
After=network.target

[Service]
ExecStart=/usr/local/bin/myapp.sh
Restart=on-failure

[Install]
WantedBy=multi-user.target
EOF

1-3. デーモンをリロードして起動する

sudo systemctl daemon-reload
sudo systemctl enable --now myapp
sudo systemctl status myapp
● myapp.service - My Application
     Loaded: loaded (/etc/systemd/system/myapp.service; enabled)
     Active: active (running) since Sun 2026-05-31 12:00:00 JST; 3s ago
   Main PID: 12345 (myapp.sh)

daemon-reload を忘れると「Unit not found」や古い定義のままで起動してしまう。ユニットファイルを変更するたびに実行すること。

2. [Unit] セクション — 依存関係を正しく書く

[Unit] はサービスのメタ情報と起動順序を制御する。

ディレクティブ 意味
Description= サービスの説明(systemctl status に表示)
After= 指定ユニットの起動後に自サービスを起動(順序のみ)
Requires= 指定ユニットが失敗すると自サービスも停止
Wants= 指定ユニットを起動しようとするが、失敗しても続行
ConditionPathExists= ファイル/ディレクトリが存在する場合のみ起動
[Unit]
Description=Web Application Backend
After=network.target postgresql.service
Wants=postgresql.service

After=network.target はネットワーク利用可能後に起動する定番パターン。DB 依存なら After=postgresql.service を追加する。After= は起動順序の制御で、依存の強制ではないことに注意。

3. [Service] セクション — 起動コマンドと挙動を設定する

最も設定項目が多いセクション。主要ディレクティブを押さえる。

Type= の選択

Type= はプロセスの起動方式を決定する。

Type 用途
simple(デフォルト) フォアグラウンドで常駐するプロセス
forking 自分でデーモン化(フォーク)するプロセス
oneshot 一度実行して終了するスクリプト
notify systemd に READY=1 を送って起動完了を通知するプロセス

ほとんどのケースで simple または oneshot を選ぶ。

主要ディレクティブ

[Service]
Type=simple
ExecStart=/usr/local/bin/myapp --config /etc/myapp/config.yml
ExecStop=/bin/kill -TERM $MAINPID
ExecReload=/bin/kill -HUP $MAINPID
Restart=on-failure
RestartSec=5
User=www-data
Group=www-data
WorkingDirectory=/var/lib/myapp
EnvironmentFile=/etc/myapp/env
ディレクティブ 意味
ExecStart= 起動コマンド(フルパス必須)
ExecStop= 停止コマンド(省略時は SIGTERM)
ExecReload= リロードコマンド
Restart= 再起動ポリシー(no / on-failure / always
RestartSec= 再起動待機秒数
User= / Group= 実行ユーザー / グループ
WorkingDirectory= 作業ディレクトリ
EnvironmentFile= 環境変数ファイルのパス

ExecStart= にはバイナリまたはシェルの絶対パスを指定する。/usr/bin/bash /path/to/script.sh のように書く。./script.sh など相対パスは動作しない。

環境変数を渡す

[Service]
Environment="NODE_ENV=production"
Environment="PORT=3000"
EnvironmentFile=/etc/myapp/env

/etc/myapp/env の記述例:

DB_HOST=localhost
DB_PORT=5432
SECRET_KEY=changeme

4. [Install] セクション — enable の挙動を決める

systemctl enable で自動起動を設定する際に参照されるセクション。

[Install]
WantedBy=multi-user.target

WantedBy=multi-user.target が定番の設定。systemctl enable myapp を実行すると /etc/systemd/system/multi-user.target.wants/myapp.service にシンボリックリンクが作成され、システム起動時に自動実行される。

用途
multi-user.target 通常のマルチユーザーモード(サーバー向け標準)
graphical.target グラフィカル環境が必要なサービス
network-online.target ネットワーク完全起動後に自動起動

5. サービスの操作コマンド

ユニットファイル作成後の操作まとめ。

# ユニット再読み込み(変更のたびに必要)
sudo systemctl daemon-reload

# 有効化(自動起動登録)+ 即時起動
sudo systemctl enable --now myapp

# 状態確認
sudo systemctl status myapp

# 再起動
sudo systemctl restart myapp

# リロード(設定再読み込み、ExecReload が必要)
sudo systemctl reload myapp

# 停止
sudo systemctl stop myapp

# 無効化(自動起動解除)
sudo systemctl disable myapp

# ログを確認(直近 50 行)
journalctl -u myapp -n 50 --no-pager

6. よくある失敗パターンと対処法

ExecStart のパスが通っていない

myapp.service: control process exited with error code
ExecStart=/usr/local/bin/myapp (code=exited, status=203/EXEC)

対処: フルパスを確認する。

which myapp
ls -la /usr/local/bin/myapp

daemon-reload を忘れた

ユニットファイルを変更しても反映されない、または「Unit not found」が出る場合。

sudo systemctl daemon-reload
sudo systemctl restart myapp

パーミッションエラー

journalctl -u myapp -n 20
# myapp.sh: Permission denied

対処: 実行権限を確認する。

ls -la /usr/local/bin/myapp.sh
# 実行権限がない場合
sudo chmod +x /usr/local/bin/myapp.sh

User= 設定がある場合は、指定ユーザーがファイルを読める権限を持っているかも確認する。

EnvironmentFile が見つからない

EnvironmentFile= に指定したファイルが存在しない場合、サービスが起動しない。ファイルが存在しなくても起動させたい場合は - プレフィックスを使う。

EnvironmentFile=-/etc/myapp/env

手順の全体像(チートシート)

# 1. スクリプト作成
sudo vim /usr/local/bin/myapp.sh
sudo chmod +x /usr/local/bin/myapp.sh

# 2. ユニットファイル作成
sudo vim /etc/systemd/system/myapp.service

# 3. 登録・起動
sudo systemctl daemon-reload
sudo systemctl enable --now myapp

# 4. 確認
sudo systemctl status myapp
journalctl -u myapp -f

次に読む