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.sh1-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
EnvironmentFile= にパスワードを書く場合は、ファイル権限を 600 に設定してサービス実行ユーザーのみ読める状態にすること。
sudo chmod 600 /etc/myapp/env sudo chown root:root /etc/myapp/env
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