「スクリプトが落ちたとき、自動で再起動させたい」
定期実行ならcronで済みますが、「常に動かし続けたい」用途にはcronは向いていません。そこで登場するのが、systemdのServiceユニットです。Serviceとして登録することで、OS起動時の自動起動・異常終了時の自動復旧・journaldによるログの一元管理が実現します。
この記事では、自作シェルスクリプトをsystemdサービスとして常駐運用する手順を解説します。Unitファイルの基本構造、ExecStartPre/ExecStartPost、Restart設定、journalctlによるログ確認、よくあるエラーと対処法、そしてバックアップスクリプトのサービス化まで、RHEL 9.4 / Ubuntu 24.04 LTSで動作確認した手順をお伝えします。
この記事のポイント
・.serviceファイルはUnit/Service/Installの3セクションで構成する
・ExecStartには必ず絶対パスを指定する(相対パスはNG)
・Restart=on-failure + RestartSec=10 で自動復旧を設計できる
・journalctl -u サービス名 -f でリアルタイムログを確認できる
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
なぜシェルスクリプトをsystemdサービスにするのか
シェルスクリプトを「常に動かし続ける」手段として、バックグラウンド実行(&やnohup)やcronの@rebootを使っている現場も多いでしょう。しかしこれらには共通の弱点があります。
・バックグラウンド実行(&): ログアウトや再起動でプロセスが消える
・nohup: 再起動後は手動で再度起動が必要。ログ管理も別途必要
・cron @reboot: プロセスが落ちても自動で再起動されない
systemdのServiceユニットとして登録すると、これらの問題がすべて解決します。
・OS起動時に自動でサービスが立ち上がる(systemctl enable)
・プロセスが異常終了しても自動で再起動される(Restart=on-failure)
・ログがjournaldに統合され、journalctl で一元確認できる
・systemctl status で起動状態・終了コードを即座に確認できる
「ずっと動かし続けたい処理」には、cronではなくsystemd Serviceが正解です。
Unitファイル(.service)の基本構造と作成手順
1. Unitファイルの配置場所
自作サービスのUnitファイルは /etc/systemd/system/ 配下に配置します。ファイル名は サービス名.service の形式です。システム標準のサービス(/lib/systemd/system/)とは別の場所に置くことで、パッケージ更新の影響を受けません。
# Unitファイルを新規作成する sudo vi /etc/systemd/system/myapp.service
2. Unitファイルの基本構造
Unitファイルは3つのセクションで構成します。以下が最小構成の例です。
[Unit] Description=My Application Service After=network.target [Service] Type=simple ExecStart=/usr/local/bin/myapp.sh Restart=on-failure RestartSec=10 [Install] WantedBy=multi-user.target
各セクションの役割を説明します。
・[Unit]: サービスの説明と起動順序の依存関係を定義する。After=network.target はネットワーク起動後に本サービスを起動することを意味する
・[Service]: 実行するコマンドや再起動挙動を指定する。Type=simple はExecStartで起動したプロセスをメインプロセスとして扱う設定
・[Install]: systemctl enable 時にどのターゲットにリンクするかを定義する。multi-user.target は通常のマルチユーザーモード起動時に有効化される
3. Unitファイル登録とサービス起動
ファイルを作成したら、以下の順序でサービスを有効化します。
# systemdにUnitファイルの変更を通知する(必須) sudo systemctl daemon-reload # OS起動時の自動起動を有効化する sudo systemctl enable myapp.service # サービスをすぐに起動する sudo systemctl start myapp.service # 起動状態を確認する sudo systemctl status myapp.service
実行結果の例を示します。
# myapp.service - My Application Service # Loaded: loaded (/etc/systemd/system/myapp.service; enabled; vendor preset: disabled) # Active: active (running) since Sat 2026-07-19 10:00:00 JST; 3s ago # Main PID: 12345 (myapp.sh) # CGroup: /system.slice/myapp.service # `-12345 /bin/bash /usr/local/bin/myapp.sh
Active: active (running) と表示されていればサービスは正常に動作しています。
ExecStartPreとExecStartPostで前後処理を制御する
本体スクリプトの実行前後に処理を挟みたい場合は、ExecStartPre(起動前)と ExecStartPost(起動後)を使います。
[Service] Type=simple # 起動前: 古いロックファイルを削除する(失敗しても続行する) ExecStartPre=-/bin/rm -f /var/run/myapp.lock # 本体スクリプト(絶対パスで指定する) ExecStart=/usr/local/bin/myapp.sh # 起動後: 起動完了をsyslogに記録する ExecStartPost=/bin/logger "myapp.service started successfully" Restart=on-failure RestartSec=10
注意点があります。ExecStartPreが失敗(終了コード0以外)すると、ExecStartは実行されません。起動前チェックとして活用できますが、エラーを無視して続行させたい前処理は ExecStartPre=-/bin/rm ... のようにコマンドの先頭に - を付けてエラーを無視させます。
Restart=on-failureとRestartSecで自動復旧を設計する
常駐サービスを運用する上で最も重要なのが、プロセス異常終了時の自動復旧設計です。
1. Restartのオプション比較
・Restart=no: 終了しても再起動しない(デフォルト値)
・Restart=on-failure: 終了コードが0以外のとき再起動する
・Restart=always: 正常終了・異常終了を問わず常に再起動する
・Restart=on-abnormal: シグナルで強制終了されたときも再起動する
バックアップや監視スクリプトのように「落ちたときだけ復旧させたい」用途には Restart=on-failure が適切です。Restart=always は、スクリプトが正常終了しても再起動し続けるため、常時ループしないスクリプトには向いていません。
2. RestartSecとStartLimitの設定
再起動の間隔と試行上限も制御できます。
[Unit] Description=My Application Service After=network.target # 60秒以内に5回失敗したら再起動を諦める StartLimitIntervalSec=60 StartLimitBurst=5 [Service] Type=simple ExecStart=/usr/local/bin/myapp.sh Restart=on-failure # 再起動前に10秒待つ RestartSec=10
RestartSec で再起動までの待機時間を設けることで、外部リソース(データベースやネットワーク)が復旧するまでの時間を確保できます。StartLimitBurst の設定は、スクリプトにバグがあったときの無限ループ再起動を防止する安全弁として機能します。
journalctlによるサービスログの確認方法
systemdでサービス化したシェルスクリプトの標準出力・エラー出力は、journaldが自動的に収集します。ログ確認には journalctl コマンドを使います。
# サービスの全ログを表示する sudo journalctl -u myapp.service # リアルタイムでログを追いかける(tail -f 相当) sudo journalctl -u myapp.service -f # 直近50行だけ表示する sudo journalctl -u myapp.service -n 50 # 今日のログのみ表示する sudo journalctl -u myapp.service --since today # エラーレベル以上のみ表示する sudo journalctl -u myapp.service -p err
実際のログ出力例を示します。
# Jul 19 10:00:00 web01.example.com myapp.sh[12345]: [INFO] バックアップ処理を開始します # Jul 19 10:00:05 web01.example.com myapp.sh[12345]: [INFO] /var/data のバックアップ完了 # Jul 19 10:00:05 web01.example.com myapp.sh[12345]: [INFO] 処理が完了しました # Jul 19 10:00:05 web01.example.com systemd[1]: myapp.service: Deactivated successfully.
スクリプト内で echo や logger コマンドを使った出力はすべてjournaldに記録されます。なお、systemdサービス全体の起動時間を解析したい場合は、systemd-analyze で起動時間を計測・分析する方法も参考にしてください。
よくあるエラーと対処法(Permission denied・ExecStart絶対パス忘れ等)
「Permission denied」が出た場合
スクリプトファイルに実行権限がないとサービスが起動しません。journalctlでエラーを確認してから対処します。
# エラー内容を確認する sudo journalctl -u myapp.service -n 20 # ... Permission denied: '/usr/local/bin/myapp.sh' ... # 実行権限を付与する sudo chmod +x /usr/local/bin/myapp.sh # サービスを再起動する sudo systemctl restart myapp.service
ExecStartに相対パスを指定した場合
ExecStartに ./myapp.sh や myapp.sh のような相対パスを指定するとサービスが起動しません。systemdはシェルを経由せずにコマンドを実行するため、カレントディレクトリの概念がないからです。必ず絶対パスを使ってください。
# NG: 相対パス(サービスが起動しない) ExecStart=./myapp.sh # OK: 絶対パス(必ずこの形式にする) ExecStart=/usr/local/bin/myapp.sh
daemon-reloadを忘れた場合
Unitファイルを編集・作成した後に systemctl daemon-reload を実行しないと、古い設定が使われ続けます。Unitファイルを変更したときは必ず実行してください。
# Unitファイル変更後に必ず実行する sudo systemctl daemon-reload # その後サービスを再起動する sudo systemctl restart myapp.service
EnvironmentFileで変数が読み込まれない場合
スクリプト内で環境変数を使う場合、EnvironmentFile ディレクティブで別ファイルから読み込む方法が安全です。ただしファイルが存在しない場合はサービスが起動しないため、先頭に - を付けてオプション扱いにしておきましょう。
[Service] # ファイルが存在しなくてもエラーにしない EnvironmentFile=-/etc/myapp/env ExecStart=/usr/local/bin/myapp.sh
実践例:バックアップスクリプトをsystemdサービスとして常駐させる
ここでは、検証サーバー上で動作するバックアップスクリプトをsystemdサービス化する実践例を示します。
1. バックアップスクリプトの作成
#!/bin/bash # /usr/local/bin/backup-watch.sh # /var/data を定期的にバックアップする常駐スクリプト BACKUP_SRC="/var/data" BACKUP_DEST="/backup/data" LOG_TAG="backup-watch" INTERVAL=300 # 5分ごとに実行する logger -t "$LOG_TAG" "サービス起動: 監視開始 ($BACKUP_SRC)" while true; do if rsync -a --checksum "$BACKUP_SRC/" "$BACKUP_DEST/"; then logger -t "$LOG_TAG" "バックアップ完了" else logger -t "$LOG_TAG" "バックアップ失敗。サービスを終了します" exit 1 fi sleep "$INTERVAL" done
# 実行権限を付与する sudo chmod +x /usr/local/bin/backup-watch.sh
2. Unitファイルの作成
# /etc/systemd/system/backup-watch.service [Unit] Description=Backup Watch Service After=network.target StartLimitIntervalSec=120 StartLimitBurst=5 [Service] Type=simple # 起動前にバックアップ先ディレクトリを作成する ExecStartPre=/bin/mkdir -p /backup/data ExecStart=/usr/local/bin/backup-watch.sh Restart=on-failure RestartSec=15 # 標準出力・エラーをjournaldに記録する StandardOutput=journal StandardError=journal [Install] WantedBy=multi-user.target
3. サービスの有効化と動作確認
sudo systemctl daemon-reload sudo systemctl enable backup-watch.service sudo systemctl start backup-watch.service sudo systemctl status backup-watch.service
正常起動時の出力例を示します。
# backup-watch.service - Backup Watch Service # Loaded: loaded (/etc/systemd/system/backup-watch.service; enabled; vendor preset: disabled) # Active: active (running) since Sat 2026-07-19 10:05:00 JST; 10s ago # Main PID: 23456 (backup-watch.sh) # Tasks: 2 (limit: 4674) # Memory: 2.1M # CGroup: /system.slice/backup-watch.service # `-23456 /bin/bash /usr/local/bin/backup-watch.sh # # Jul 19 10:05:00 web01.example.com backup-watch[23456]: サービス起動: 監視開始 (/var/data) # Jul 19 10:05:03 web01.example.com backup-watch[23456]: バックアップ完了
本記事のまとめ
自作シェルスクリプトをsystemdサービス化する方法を解説しました。要点を以下にまとめます。
| やりたいこと | 設定・コマンド |
|---|---|
| スクリプトをサービスとして登録する | systemctl enable サービス名 (Unitファイルを作成後に実行) |
| OS起動時に自動起動させる | systemctl enable サービス名 |
| 異常終了時に自動復旧させる | Restart=on-failure を [Service] に記述する |
| 再起動の待機時間を設定する | RestartSec=10(秒数を指定する) |
| 起動前処理を実行する | ExecStartPre=/絶対パス/コマンド |
| サービスのログを確認する | journalctl -u サービス名 -f |
| Unitファイル変更後に反映する | systemctl daemon-reload を実行する |
スクリプトをサービスとして動かすには、Linux基盤の「型」が必要です
systemdでスクリプトを常駐させるには、Linuxの起動プロセス・プロセス管理・ログ設計を体系的に理解することが重要です。独学で断片的に覚えるより、現場で実際に使われる設計パターンを一度体系的に身につけることで、安定した常駐サービスが構築できるようになります。
現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、『Linuxサーバー構築入門マニュアル(図解60P)』を完全無料でプレゼントしています。
「独学の時間がもったいない」「プロから直接、現場の技術を最短で学びたい」という本気の方には、2日で実務レベルのスキルが身につく【初心者向けハンズオンセミナー】も開催しています。
3,100名以上が実践した「型」を無料で公開中
プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。
登録10秒/合わなければ解除3秒 / 詳細はこちら

無料メルマガで学習を続ける
Linuxの実践スキルをメールで毎週お届け。
登録は1分、解除もいつでも可。
登録無料・いつでも解除できます