Linux環境で自作のシェルスクリプトやPythonプログラム、Go/Node.jsアプリケーションなどをバックグラウンドで安定稼働させたい場合、最も確実で標準的な手法が「systemd の自作サービス(Systemd Service Unit)」としての登録です。
nohup ./app & や screen、cron の @reboot を使った常駐化は手軽ですが、「サーバー再起動時に確実に自動起動させたい」「プロセスが異常終了(クラッシュ)した際に自動で再起動させたい」「ログを標準的な仕組みで一括管理したい」といった実務要件を満たすには限界があります。
systemdに自作サービスとして登録すれば、OS起動時の自動起動(systemctl enable)、異常終了時の即時リカバリ(Restart=always)、リソース制限、専用ユーザーでの安全な実行、journalctl によるログ収集などを統合的に実現できます。
この記事では、ユニットファイル(.service)の書き方、主要ディレクティブの意味、Bash・Pythonでの実践的なデーモン化手順、daemon-reload からの反映フロー、journalctl でのトラブルシューティングまで、現場で即コピペして使える形で徹底解説します。
- 【早見表】systemd 自作サービス登録の完全フロー チートシート
- 1. なぜ自作プログラムを systemd でデーモン化するのか?
- 2. ユニットファイル(.service)の基本構造と主要ディレクティブ
- 3. 【実践例1】自作シェルスクリプトを常駐デーモン化する手順
- 4. 【実践例2】Pythonスクリプト(仮想環境venv含む)を常駐化する手順
- 5. 【実践例3】1回だけ実行するバッチ処理(Type=oneshot)の作成
- 6. 自作サービスの反映・管理・自動起動コマンド一覧
- 7. journalctl を使ったログ確認とデバッグ
- 8. 実務でよくあるエラーとアンチパターン解決チェックリスト
- まとめ & 関連リファレンス
- 関連記事
【早見表】systemd 自作サービス登録の完全フロー チートシート
自作プログラムを systemd サービスとして登録し、自動起動・常駐化させるまでの標準フローです。
| ステップ | 作業内容 | 対象ファイル / ディレクトリ | 実行コマンド例 |
|---|---|---|---|
| Step 1 | 実行ファイルの準備 | /opt/myapp/run.sh 等 |
chmod +x /opt/myapp/run.sh |
| Step 2 | ユニットファイル作成 | /etc/systemd/system/myapp.service |
sudo nano /etc/systemd/system/myapp.service |
| Step 3 | 定義の再読込 | systemd デーモン | sudo systemctl daemon-reload |
| Step 4 | サービスの起動と状態確認 | 稼働プロセス | sudo systemctl start myappsudo systemctl status myapp |
| Step 5 | OS起動時の自動起動有効化 | シンボリックリンク生成 | sudo systemctl enable myapp |
| Step 6 | ログのリアルタイム監視 | systemd-journald | journalctl -u myapp -f |
1. なぜ自作プログラムを systemd でデーモン化するのか?
Linuxでバックグラウンド処理を実行する方法にはいくつか選択肢がありますが、systemd は現代の主要ディストリビューション(Ubuntu, Debian, RHEL, AlmaLinux, Rocky Linux等)における標準のサービス管理フレームワークです。
nohup・cron と systemd の比較
| 管理方式 | 自動起動 | クラッシュ時再起動 | ログ管理 | プロセス制御 |
|---|---|---|---|---|
| nohup / screen | 不可(手動実行) | 不可 | ファイルへリダイレクト | PID調査とkill |
| cron (@reboot) | 可能 | 不可 | メール / 手動リダイレクト | PID調査とkill |
| systemd サービス | 完全自動(enable) | 自動復旧(Restart) | journald で統合収集 | systemctl で一元管理 |
- 自動復旧(プロセス監視): 予期せぬエラーでプロセスが終了しても、
Restart=alwaysを設定しておけば自動的に再起動します。 - ライフサイクルの完全管理: プロセスがフォークして子プロセスを生成した場合でも、Linuxの cgroup(Control Groups) により孤児プロセスを残さず確実に停止・再起動できます。
- 実行権限の分離:
User=やGroup=を指定することで、root 権限ではなく専用の非特権ユーザーで安全に実行できます。 - 起動依存関係の制御: 「ネットワークが完全に開通した後(
network-online.target)」や「データベース起動後(mysql.service)」など、起動順序を厳密に制御できます。
ユニットファイルの配置場所
systemd の設定ファイル(ユニットファイル)は複数箇所に存在しますが、自作サービスを作成する場合は必ず /etc/systemd/system/ に配置します。
/etc/systemd/system/(推奨・自作サービス用): システム管理者が手動で作成・カスタマイズする最優先ディレクトリ。パッケージ更新で上書きされません。/lib/systemd/system/または/usr/lib/systemd/system/: apt や dnf などのパッケージマネージャーがインストールするデフォルト定義。直接編集してはいけません。~/.config/systemd/user/(ユーザーサービス用): 一般ユーザー権限で管理するサービス用(systemctl --userで操作)。
2. ユニットファイル(.service)の基本構造と主要ディレクティブ
サービス定義ファイル(.service)は、主に [Unit]、[Service]、[Install] の3つのセクションで構成されます。
[Unit]
Description=My Custom Python Application
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=appuser
Group=appuser
WorkingDirectory=/opt/myapp
ExecStart=/opt/myapp/venv/bin/python /opt/myapp/main.py
Restart=always
RestartSec=5s
Environment="APP_ENV=production" "PORT=8080"
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
[Unit] セクション:サービスのメタ情報と依存関係
サービス全般の説明や、他のユニットとの起動順序・依存関係を定義します。
| ディレクティブ | 説明と実務での使い分け |
|---|---|
Description |
サービスの説明文。systemctl status やシステムログに表示されます。 |
After |
起動順序の指定。指定したターゲットやサービスが起動した後に本サービスを起動します(例: After=network-online.target)。※依存関係自体は作らないため、対象が起動失敗しても本サービスは起動を試みます。 |
Before |
指定したターゲットやサービスより前に本サービスを起動します。 |
Wants |
軟弱な依存関係。本サービス起動時に対象も同時に起動しようと試みますが、対象が起動失敗しても本サービスの起動は継続します(推奨)。 |
Requires |
強固な依存関係。対象が起動に成功しない限り、本サービスは起動できません。不要な指定は起動連鎖エラーの原因になるため注意が必要です。 |
[Service] セクション:プロセスの起動・停止・動作設定
プロセスの実行コマンド、実行ユーザー、再起動ポリシーなど、サービスの振る舞いの中核を定義します。
| ディレクティブ | 説明と設定値 |
|---|---|
Type |
プロセスの起動挙動(下記で詳解)。通常は simple(常駐プロセス)または oneshot(単発バッチ)。 |
ExecStart |
起動時に実行するコマンドライン(必須)。必ず絶対パスで記述します(例: /usr/bin/python3 /opt/app.py)。 |
ExecStop |
サービス停止時に実行するカスタムコマンド(省略時はメインプロセスに SIGTERM が送信されます)。 |
ExecReload |
systemctl reload 実行時に設定再読込を行うコマンド。 |
WorkingDirectory |
コマンド実行時のカレントディレクトリ。相対パスでファイルを参照するプログラムに必須。 |
User / Group |
サービスを実行する Linux ユーザー名およびグループ名。セキュリティ上、root 以外の専用ユーザー指定を推奨。 |
Restart |
自動再起動の条件。no(再起動しない)、on-failure(異常終了時のみ)、always(終了理由を問わず常に再起動)。常駐デーモンには always または on-failure を指定。 |
RestartSec |
プロセス終了から再起動を試みるまでの待機時間(例: 5s, 10s)。急激な再起動ループ(フラッピング)を防ぎます。 |
Environment |
プロセスに渡す環境変数(例: Environment="PORT=8000" "NODE_ENV=production")。 |
EnvironmentFile |
環境変数を記述した設定ファイル(.env 形式)の絶対パスを指定(例: EnvironmentFile=/etc/myapp.env)。 |
StandardOutputStandardError |
標準出力・標準エラー出力の転送先。デフォルトは journal(journalctl で閲覧可能)。 |
Type= の主な種類と選択基準
Type=simple(デフォルト・最頻出):ExecStartで指定したプロセスがそのままメインプロセスとして常駐し続ける場合に使用します(Node.js, Pythonスクリプト, FastAPI, Go, 前面で動くデーモン)。Type=oneshot: 処理を一度実行して正常終了するバッチ処理や初期化スクリプトに使用します。RemainAfterExit=yesを併用すると、終了後も active 状態を維持できます。Type=forking: 起動後にプロセス自らフォークしてバックグラウンドへ移行する、伝統的なデーモンプログラム(Apache, Nginx, 旧来のCプログラム等)に使用します。PIDFile=の指定が推奨されます。Type=notify: 起動完了時にsd_notify()API を通じて systemd に通知を送る高度な常駐プログラム用。
[Install] セクション:自動起動(enable)の紐付け
sudo systemctl enable コマンドを実行した際に、どのターゲット(ランレベルに相当)に登録するかを指定します。
WantedBy=multi-user.target: 通常のサーバー環境(CUIマルチユーザーモード)で OS 起動時に自動起動させたい場合は、ほぼ常にこれを指定します。
3. 【実践例1】自作シェルスクリプトを常駐デーモン化する手順
サーバーの稼働状況やログを定期的に監視・記録する Bash スクリプトを、systemd サービスとして常駐化させる具体的な手順です。
ステップ1:実行対象のスクリプトを作成する
まず、/opt/scripts/server-monitor.sh に常駐型シェルスクリプトを配置し、実行権限を付与します。
sudo mkdir -p /opt/scripts
sudo nano /opt/scripts/server-monitor.sh
スクリプトの内容例:
#!/bin/bash
# /opt/scripts/server-monitor.sh
echo "[$(date '+%Y-%m-%d %H:%M:%S')] サーバー監視サービスを開始しました (PID: $$)"
# SIGTERM / SIGINT をトラップしてクリーンアップ終了
cleanup() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] 停止シグナルを受信しました。終了します。"
exit 0
}
trap cleanup SIGTERM SIGINT
while true; do
LOAD_AVG=$(awk '{print $1}' /proc/loadavg)
DISK_USAGE=$(df -h / | awk 'NR==2 {print $5}')
echo "[$(date '+%Y-%m-%d %H:%M:%S')] LoadAvg: ${LOAD_AVG} | Disk /: ${DISK_USAGE}"
sleep 60
done
実行権限(chmod +x)を付与し、手動でテスト実行できることを確認します。
sudo chmod +x /opt/scripts/server-monitor.sh
# 動作確認(Ctrl+C で停止)
/opt/scripts/server-monitor.sh
ステップ2:ユニットファイルを作成する
/etc/systemd/system/server-monitor.service を作成します。ヒアドキュメントと sudo tee コマンド を使用するとワンライナーで一発作成できます。
sudo tee /etc/systemd/system/server-monitor.service << 'EOF'
[Unit]
Description=Server Resource Monitor Daemon
After=network.target
[Service]
Type=simple
ExecStart=/bin/bash /opt/scripts/server-monitor.sh
Restart=always
RestartSec=5s
KillMode=mixed
TimeoutStopSec=10s
[Install]
WantedBy=multi-user.target
EOF
ステップ3:反映・起動・自動起動の有効化
ユニットファイルを作成したら、以下の順序で反映と起動を行います。
# 1. systemd に新しい設定ファイルを読み込ませる
sudo systemctl daemon-reload
# 2. サービスを即時起動し、OS起動時の自動起動を有効化
sudo systemctl enable --now server-monitor.service
# 3. 稼働状態を確認
sudo systemctl status server-monitor.service
Active: active (running) と緑色で表示されていれば、自作サービスの常駐化と自動起動設定は完了です。
4. 【実践例2】Pythonスクリプト(仮想環境venv含む)を常駐化する手順
APIサーバーやDiscord/Slack Bot、定期収集プログラムなどの Python アプリケーションを常駐化する場合、「仮想環境(venv)の指定」「専用非特権ユーザーでの実行」「ログのバッファリング解除」が実務上の重要なポイントになります。
ステップ1:専用ユーザーとアプリディレクトリの準備
セキュリティのため、root 権限ではなく専用のシステムユーザー(例: pyapp)を作成して実行します。
# ログイン不能なシステムユーザーを作成
sudo useradd -r -s /bin/false -d /opt/mybot pyapp
# ディレクトリ作成と権限設定
sudo mkdir -p /opt/mybot
sudo chown -R pyapp:pyapp /opt/mybot
Python 仮想環境の構築とスクリプトの作成:
# 仮想環境の作成
sudo -u pyapp python3 -m venv /opt/mybot/venv
# サンプルスクリプト /opt/mybot/app.py
sudo -u pyapp tee /opt/mybot/app.py << 'EOF'
import time
import sys
import os
print(f"Python Bot Started! ENV={os.getenv('APP_ENV', 'unknown')}", flush=True)
try:
while True:
print("Bot is processing heartbeat task...", flush=True)
time.sleep(30)
except KeyboardInterrupt:
print("Bot stopped gracefully.", flush=True)
EOF
ステップ2:Python専用ユニットファイルの作成
/etc/systemd/system/mybot.service を作成します。
sudo tee /etc/systemd/system/mybot.service << 'EOF'
[Unit]
Description=My Python Heartbeat Bot
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=pyapp
Group=pyapp
WorkingDirectory=/opt/mybot
# 仮想環境内の python バイナリを絶対パスで指定
ExecStart=/opt/mybot/venv/bin/python /opt/mybot/app.py
# Pythonの標準出力バッファリングを無効化(journalctl に即時反映)
Environment="PYTHONUNBUFFERED=1"
Environment="APP_ENV=production"
# クラッシュ時の自動再起動設定
Restart=always
RestartSec=5s
# 停止シグナルと猶予時間
KillSignal=SIGTERM
TimeoutStopSec=15s
[Install]
WantedBy=multi-user.target
EOF
Python特有の設定ポイント:
ExecStart=/opt/mybot/venv/bin/python: 仮想環境(venv)を activate する必要はありません。venv/bin/pythonを直接指定するだけで、その仮想環境にインストールされたライブラリが自動的に読み込まれます。Environment="PYTHONUNBUFFERED=1": Python の標準出力バッファリングをオフにします。これを指定しないと、print()の出力がjournalctlにリアルタイムで表示されない現象が発生します。
ステップ3:反映と起動テスト
sudo systemctl daemon-reload
sudo systemctl enable --now mybot.service
sudo systemctl status mybot.service
5. 【実践例3】1回だけ実行するバッチ処理(Type=oneshot)の作成
常駐するデーモンではなく、「OS起動時に1度だけ初期化スクリプトを実行したい」「日次バックアップを実行したい」という場合は Type=oneshot を使用します。
sudo tee /etc/systemd/system/system-backup.service << 'EOF'
[Unit]
Description=One-time System Daily Backup
After=network.target
[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup-script.sh
# 終了後も active (exited) 状態を維持して起動済みとみなす場合
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
EOF
RemainAfterExit=yesを指定すると、スクリプト実行が終了した後もサービスのステータスがactive (exited)として保持されます。他のサービスがRequires=system-backup.serviceで依存している場合に便利です。- 定期実行したい場合は、cron を利用するか、対応する
systemd.timer(タイマーユニット)を組み合わせることで柔軟なスケジュール実行が可能です。基本操作は cron設定の基本とよく使う書き方 も参照してください。
6. 自作サービスの反映・管理・自動起動コマンド一覧
自作サービス作成後に日常運用で頻繁に使用するコマンドのまとめです。詳細は systemctlコマンドの使い方 も併せてご確認ください。
| 操作目的 | 実行コマンド |
|---|---|
| ユニット定義の再読込 | sudo systemctl daemon-reload |
| サービスの起動 / 停止 / 再起動 | sudo systemctl start <service>sudo systemctl stop <service>sudo systemctl restart <service> |
| 設定変更の反映(Reload) | sudo systemctl reload <service> |
| 詳細ステータスの確認 | sudo systemctl status <service> |
| 自動起動の有効化(即時起動も同時に行う) | sudo systemctl enable --now <service> |
| 自動起動の無効化 | sudo systemctl disable <service> |
| 稼働中かどうかの確認(スクリプト判定用) | systemctl is-active <service> (activeなら終了コード0) |
| 自動起動が有効かの確認 | systemctl is-enabled <service> (enabledなら終了コード0) |
| ユニットファイルの構文エラー事前チェック | systemd-analyze verify /etc/systemd/system/<service>.service |
7. journalctl を使ったログ確認とデバッグ
systemd で管理されるサービスは、標準出力(stdout)および標準エラー出力(stderr)が自動的に systemd-journald に収集されます。標準エラー出力のリダイレクト を手動で行う必要はありません。
よく使うログ確認コマンド
# 1. サービスのログをリアルタイムで追尾(tail -f 相当)
journalctl -u mybot.service -f
# 2. 直近100行のログを表示(ページャーを使わず一括出力)
journalctl -u mybot.service -n 100 --no-pager
# 3. 今回のOS起動以降のログのみを表示
journalctl -u mybot.service -b
# 4. 指定した時刻以降のエラーログのみを抽出
journalctl -u mybot.service --since "1 hour ago" -p err
# 5. プロセス終了コードや詳細情報を表示
journalctl -u mybot.service -e -o verbose
8. 実務でよくあるエラーとアンチパターン解決チェックリスト
自作サービスが起動しない、または意図通りに動かない場合の代表的な原因と対処法です。
| 発生する問題・エラー | 主な原因 | 解決手順・対処法 |
|---|---|---|
status=203/EXEC |
実行ファイルのパス間違い、または実行権限(+x)不足、シバン(#!/bin/bash)の誤り。 |
ExecStart を絶対パスで記述する。chmod +x /path/to/script を実行する。 |
status=217/USER |
User= や Group= に存在しないユーザー・グループが指定されている。 |
id <username> でユーザーが存在するか確認し、スペルミスを修正する。 |
| 設定変更が反映されない | .service ファイル編集後に daemon-reload を実行していない。 |
sudo systemctl daemon-reload && sudo systemctl restart <service> を実行する。 |
| Pythonのログが出ない | 標準出力がバッファリングされている。 | [Service] に Environment="PYTHONUNBUFFERED=1" を追加する。 |
| 起動直後に再起動を繰り返す(フラッピング) | スクリプトが即終了している、またはネットワーク未接続でクラッシュ。 | After=network-online.target を指定する。RestartSec=5s で間隔を空け、スクリプトの exit コードを確認する。 |
| 相対パスのファイルが見つからない | WorkingDirectory が設定されていないため、カレントディレクトリが / になっている。 |
[Service] に WorkingDirectory=/opt/myapp を明示的に設定する。 |
Permission denied で起動失敗 |
指定した User= に対象スクリプトやログ保存先ディレクトリの読み書き権限がない。 |
sudo chown -R <user>:<group> /opt/myapp で適切な所有権を設定する(詳細は Permission deniedの直し方 参照)。 |
詳細デバッグ:systemd-analyze verify の活用
ユニットファイルを編集した際は、サービスを起動する前に systemd-analyze verify コマンドで構文チェックを行うと、記述ミスを即座に発見できます。
sudo systemd-analyze verify /etc/systemd/system/mybot.service
※何も出力されなければ構文チェック合格です。誤ったディレクティブ名や無効な設定値がある場合は警告メッセージが表示されます。
まとめ & 関連リファレンス
Linux で自作スクリプトやプログラムをデーモン化する際は、systemd ユニットファイル(/etc/systemd/system/*.service)を作成するのが最も堅牢で信頼性の高いアプローチです。
- ユニット作成の基本:
[Unit]で依存関係、[Service]で絶対パスによるExecStartとRestart=always、[Install]でmulti-user.targetを設定。 - セキュリティ対策: root 実行を避け、専用の非特権ユーザー(
User=)を指定。 - 反映と運用の鉄則: 編集後は必ず
sudo systemctl daemon-reloadを実行し、journalctl -u <service> -fでログを監視。
本番環境での再起動戦略やフラッピング防止設計についてさらに深く学びたい方は、systemdユニットの基本と再起動戦略 もあわせてご活用ください。
関連記事
- 【完全早見表】systemctlコマンドの使い方(start/stop/status/enable)とエラー対処
- systemdユニットの基本と再起動戦略|最小雛形と安全なRestart=設計
- 【Bash】ヒアドキュメントの使い方まとめ|複数行出力・変数展開の制御・sudo tee連携を徹底解説
- cron設定の基本とよく使う書き方|crontabの書き方からログ・メール通知設定まで
- 【Bash】標準エラー出力のリダイレクト完全ガイド|2>&1・個別保存・/dev/null破棄と順序の仕組み
- 【Linux】SSH 鍵認証の設定手順まとめ|ssh-keygen生成・authorized_keys登録・パスワード禁止まで徹底解説
- Permission deniedの原因と直し方|6つの切り分け手順

コメント