自作シェルスクリプトをsystemdサービス化して常駐運用する方法|Unitファイル作成・Restart設定・ログ確認

宮崎智広 この記事の監修:宮崎智広(Linux実務・教育歴20年以上・受講者3,100名超)
HOMELinux技術 リナックスマスター.JP(Linuxマスター.JP)シェルスクリプト > 自作シェルスクリプトをsystemdサービス化して常駐運用する方法|Unitファイル作成・Restart設定・ログ確認
「シェルスクリプトを書いたのに、サーバーを再起動するたびに手動で起動し直すのが面倒だ」
「スクリプトが落ちたとき、自動で再起動させたい」

定期実行なら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 でリアルタイムログを確認できる


「このままじゃマズい」と感じていませんか?
参考書を開く気力もない、同年代に取り残される不安——
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
図解60P/登録10秒/解除も3秒 / 詳細はこちら

なぜシェルスクリプトを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.

スクリプト内で echologger コマンドを使った出力はすべて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.shmyapp.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日で実務レベルのスキルが身につく【初心者向けハンズオンセミナー】も開催しています。

無料メルマガで学習を続ける

Linuxの実践スキルをメールで毎週お届け。
登録は1分、解除もいつでも可。

登録無料・いつでも解除できます

暗記不要・1時間後にはサーバーが動く

3,100名以上が実践した「型」を無料で公開中

プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。

登録10秒/合わなければ解除3秒 / 詳細はこちら

Linux無料マニュアル(図解60P) 名前とメールで30秒登録
宮崎 智広

この記事を書いた人

宮崎 智広(みやざき ともひろ)

株式会社イーネットマーキュリー代表。現役のLinuxサーバー管理者として20年以上の実務経験を持ち、これまでに累計3,100名以上のエンジニアを指導してきたLinux教育のプロフェッショナル。「現場で本当に使える技術」を体系的に伝えることをモットーに、実践型のLinuxセミナーの開催や無料マニュアルの配布を通じてLinux人材の育成に取り組んでいる。

趣味は、キャンプにカメラ、トラウト釣り。好きな食べ物は、ラーメンにお酒。休肝日が作れない、酒量を減らせないのが悩み。最近、ドラマ「フライトエンジェル」を観て涙腺が崩壊しました。