シェルスクリプトでプロセスを監視して自動再起動する方法|kill -0チェックとwatchdog設計の実践

宮崎智広 この記事の監修:宮崎智広(Linux実務・教育歴20年以上・受講者3,100名超)
HOMELinux技術 リナックスマスター.JP(Linuxマスター.JP)シェルスクリプト > シェルスクリプトでプロセスを監視して自動再起動する方法|kill -0チェックとwatchdog設計の実践
「気づいたらサービスが落ちていた」という経験はありませんか?
バッチ処理や自前デーモンが途中でクラッシュしても、systemdのRestartが使えないケースでは誰も検知できません。

この記事では、シェルスクリプトで kill -0 を使いプロセスの死活を確認し、停止時に自動再起動するwatchdog設計の実践手順を解説します。
再起動回数の上限管理、メールアラート通知、cronでの定期実行と二重起動防止を組み合わせた堅牢な実装パターンを、実機ログを交えて紹介します。

この記事のポイント

・kill -0 $PID でプロセスの存在を確認し、停止時に自動再起動できる
・再起動回数をファイルで上限管理し、連続失敗時はアラートを送って自動収束させる
・cron実行時はflock -n でwatchdog自身の二重起動を防ぐ設計が必須
・systemd Restart設定で代替できる場合は必ずそちらを優先する


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

なぜシェルスクリプトのwatchdogが必要なのか|systemdで代替できないケースとは

Linuxサーバーでプロセスを常時稼働させる手段として、systemdのUnit設定が最もシンプルです。Restart=always を記述するだけで、プロセスが終了するたびに自動再起動してくれます。

しかし、次のようなケースではsystemdだけでは対応が難しく、シェルスクリプトによるwatchdogが現場で必要になります。

サードパーティのデーモンがsystemdで管理されていない場合:古いSysVスクリプト起動のプロセスや、単純に nohup で動かしているバッチ
プロセスが「終了」ではなく「フリーズ」する場合:プロセスは生きているが応答しない状態では、systemd Restartは発動しない
外部ベンダーのスクリプトをラップして監視したい場合:Unitファイルを変更できない環境
複数プロセスを横断して一括監視したい場合:1本のwatchdogスクリプトで5つのバッチを監視するなど

これらのケースでは、cronで定期的に起動するwatchdogスクリプトが有効な選択肢になります。

プロセスの死活確認を実装する|kill -0・pgrep・pidfileの使い分け

死活確認の手段はいくつかあります。それぞれの特性を理解した上で使い分けることが設計の第一歩です。

1. kill -0 によるPID確認

kill -0 はシグナルを実際には送らず、「そのPIDのプロセスが存在し、かつ自分にkillできる権限があるか」だけを確認するコマンドです。

# PID 12345 のプロセスが存在するか確認 kill -0 12345 2>/dev/null && echo "running" || echo "not running" # スクリプト内での使い方 is_running() { local pid pid=$(cat "${PIDFILE}" 2>/dev/null) || return 1 kill -0 "${pid}" 2>/dev/null }

プロセスが存在すれば終了コード0、存在しなければ1を返します。2>/dev/null で「No such process」のエラーメッセージを抑制するのが定石です。

2. pgrep によるプロセス名確認

PIDファイルを持たないプロセスの場合、プロセス名で死活を確認できます。

# プロセス名 myapp が存在するか確認 pgrep -x myapp > /dev/null 2>&1 && echo "running" || echo "not running" # -x はプロセス名の完全一致。部分一致だと誤検知する pgrep -f "myapp --daemon" > /dev/null 2>&1

ただし、同名プロセスが複数起動している場合は意図しないものを検知する恐れがあります。可能であればPIDファイル方式と組み合わせてください。

3. systemctl is-active によるサービス確認

systemd管理下のサービスであれば、より正確な確認ができます。

# サービスが active かどうか確認 systemctl is-active --quiet myapp.service && echo "active" || echo "inactive" # 終了コード: 0=active, 1~3=inactive/failed/activating

4. 手法の比較表

手法 確認方法 適した場面 注意点
kill -0 kill -0 $PID 2>/dev/null PIDファイルがある場合 権限が必要、PID再利用リスク
pgrep pgrep -x プロセス名 PIDファイルなし 同名プロセス複数時に誤検知
systemctl systemctl is-active サービス名 systemd管理プロセス 非systemdには使えない

基本的なwatchdogスクリプトの設計|最小構成から始める

まずは「プロセスが落ちていたら再起動する」最小構成を作ります。

#!/bin/bash # /usr/local/sbin/watchdog-myapp.sh # myapp プロセスを監視して自動再起動する watchdog スクリプト set -euo pipefail PROCESS_NAME="myapp" START_CMD="/opt/myapp/bin/myapp --daemon --config /etc/myapp/myapp.conf" PIDFILE="/var/run/myapp.pid" LOGFILE="/var/log/watchdog-myapp.log" log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" >> "${LOGFILE}" } is_running() { local pid pid=$(cat "${PIDFILE}" 2>/dev/null) || return 1 kill -0 "${pid}" 2>/dev/null } if is_running; then log "OK: ${PROCESS_NAME} is running (PID=$(cat "${PIDFILE}"))" exit 0 fi log "WARN: ${PROCESS_NAME} is not running. Restarting..." ${START_CMD} log "INFO: ${PROCESS_NAME} restart command executed."

このスクリプトのポイントは is_running() 関数です。cat "${PIDFILE}" が失敗した場合(PIDファイルなし)は即座に return 1(停止とみなす)し、成功した場合は kill -0 でPIDのプロセスが本当に存在するか確認します。

PIDファイルが残ったままプロセスが消えた「ゾンビPIDファイル」状態も、この設計ならきちんと「停止」と判定できます。
現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、20年以上の運用経験を持つ現役エンジニアが基礎から教えます。
シェルスクリプト講座を見る >>

再起動回数の上限とアラート通知を実装する

最小構成では「何度でも再起動し続ける」ため、本当に壊れているプロセスがある場合に無限に再起動しようとして状況が悪化します。再起動上限とアラート通知を組み込んだ実装が現場では必須です。

#!/bin/bash # /usr/local/sbin/watchdog-myapp.sh(上限・アラート付き版) set -euo pipefail PROCESS_NAME="myapp" START_CMD="/opt/myapp/bin/myapp --daemon --config /etc/myapp/myapp.conf" PIDFILE="/var/run/myapp.pid" LOGFILE="/var/log/watchdog-myapp.log" COUNTER_FILE="/tmp/watchdog-myapp-restarts" MAX_RESTARTS=3 ALERT_TO="ops-team@example.com" log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "${LOGFILE}" } send_alert() { local subject="$1" local body="$2" echo "${body}" | mail -s "${subject}" "${ALERT_TO}" 2>/dev/null || \ log "WARN: mail command failed. Install mailx or configure sendmail." } is_running() { local pid pid=$(cat "${PIDFILE}" 2>/dev/null) || return 1 kill -0 "${pid}" 2>/dev/null } if is_running; then # 正常稼働中 → カウンタをリセット echo 0 > "${COUNTER_FILE}" log "OK: ${PROCESS_NAME} is running (PID=$(cat "${PIDFILE}"))" exit 0 fi # 再起動カウンタを読み取り、インクリメント count=$(cat "${COUNTER_FILE}" 2>/dev/null || echo 0) count=$(( count + 1 )) echo "${count}" > "${COUNTER_FILE}" # 上限超過 → アラート送信して終了 if (( count > MAX_RESTARTS )); then log "ERROR: ${PROCESS_NAME} has failed ${count} times. Giving up. Manual check required." send_alert \ "[ALERT] ${PROCESS_NAME} on $(hostname) repeated failures" \ "${PROCESS_NAME} stopped and failed to recover ${count} times on $(hostname). Please check immediately." exit 1 fi log "WARN: ${PROCESS_NAME} stopped. Restart attempt ${count}/${MAX_RESTARTS}..." ${START_CMD} log "INFO: ${PROCESS_NAME} restart command executed (attempt ${count})."

このスクリプトでの設計の核心は2点です。

カウンタファイル(COUNTER_FILE)の使い方:プロセスが正常稼働している間はカウンタを0にリセットし続けます。再起動が必要になるたびにインクリメントし、MAX_RESTARTSを超えたら「これ以上は人間が対処すべき状態」と判断してアラートを送ります。
アラート後のexit 1:上限超過後はスクリプト自身もエラー終了します。cronのMAILTO設定と合わせることで、cronからもメール通知が届く二重通知になります。

実機ログの出力例を示します。

# 実際のログ出力例(/var/log/watchdog-myapp.log) [2026-09-01 03:00:01] WARN: myapp stopped. Restart attempt 1/3... [2026-09-01 03:00:03] INFO: myapp restart command executed (attempt 1). [2026-09-01 03:05:01] OK: myapp is running (PID=18234) [2026-09-01 03:10:01] OK: myapp is running (PID=18234) [2026-09-01 03:15:01] WARN: myapp stopped. Restart attempt 1/3... [2026-09-01 03:15:02] INFO: myapp restart command executed (attempt 1). [2026-09-01 03:20:01] WARN: myapp stopped. Restart attempt 2/3... [2026-09-01 03:20:02] INFO: myapp restart command executed (attempt 2). [2026-09-01 03:25:01] WARN: myapp stopped. Restart attempt 3/3... [2026-09-01 03:25:02] INFO: myapp restart command executed (attempt 3). [2026-09-01 03:30:01] ERROR: myapp has failed 4 times. Giving up. Manual check required.

cronで定期実行するwatchdog構成|flock と PATH の設定

watchdogスクリプトをcronに登録する際には、次の2点に必ず対処してください。

1. flock で二重起動を防ぐ

cronは「前回の実行が終わっているかどうかに関係なく」スケジュール通り起動します。watchdogスクリプト自身の実行時間が5分を超えた場合(再起動待ちで長時間かかるケースなど)、複数インスタンスが同時に走る危険があります。flock -n で防ぎます。

# /etc/cron.d/watchdog-myapp # 5分ごとに watchdog を実行する MAILTO="ops-team@example.com" PATH=/usr/local/sbin:/usr/sbin:/sbin:/usr/local/bin:/usr/bin:/bin */5 * * * * root /usr/bin/flock -n /tmp/watchdog-myapp.lock /usr/local/sbin/watchdog-myapp.sh

flock -n-n は「ロックが取れなければ即座に終了(待機しない)」オプションです。前回の実行が終わっていなければ今回の起動をスキップします。

2. PATH をスクリプト内でも明示する

cronの実行環境はログインシェルと異なり、PATHが非常に短くなっています。crontabの PATH= に加えて、スクリプト冒頭でも明示しておくと確実です。

#!/bin/bash # スクリプト冒頭に PATH を明示(cron 環境では /usr/local/sbin 等が通っていないことが多い) export PATH=/usr/local/sbin:/usr/sbin:/sbin:/usr/local/bin:/usr/bin:/bin set -euo pipefail # ... 以降 watchdog 本体

3. MAILTO の設定

MAILTO= の設定が空だとcronの標準出力・標準エラーがどこにも届きません。watchdogスクリプトのexit 1(異常終了)をcronメールで受け取るために、運用チームのメールアドレスを必ず設定してください。

トラブルシュート|watchdogが誤動作する典型パターン

1. PIDファイルが残ったまま別のプロセスが同じPIDを使っている

Linuxカーネルはプロセス終了後、そのPIDを再利用することがあります。古いPIDファイルを持つwatchdogが kill -0 を実行すると、まったく関係ないプロセスが「生きている」と誤判定します。

対策として、PIDファイルの更新タイムスタンプとプロセス起動時間を合わせて確認する方法があります。

is_running() { local pid pid=$(cat "${PIDFILE}" 2>/dev/null) || return 1 # プロセスが存在するか kill -0 "${pid}" 2>/dev/null || return 1 # プロセス名も確認(PID再利用対策) local proc_name proc_name=$(ps -p "${pid}" -o comm= 2>/dev/null) || return 1 [[ "${proc_name}" == "${PROCESS_NAME}" ]] }

2. watchdogスクリプト自体が途中でエラー終了する

set -euo pipefail を設定していると、意図しないコマンドエラーでwatchdog自身が終了することがあります。is_running() 内の return 1 は正常動作ですが、ログ書き込みに失敗した場合なども含めてテストしてください。

# watchdog スクリプトを手動でデバッグ実行する bash -x /usr/local/sbin/watchdog-myapp.sh 2>&1 | head -50 # cron 経由での動作確認(run-parts 相当) /usr/bin/flock -n /tmp/watchdog-myapp.lock /usr/local/sbin/watchdog-myapp.sh echo "Exit code: $?"

3. pgrep で同名プロセスを誤検知する

監視対象とは別に同名プロセスが走っている場合、pgrep方式では正常と誤判定します。PIDファイル方式に切り替えるか、pgrep -f でコマンドライン全体(オプションを含む)をパターン指定することで誤検知を減らせます。

# プロセス名の完全一致(デフォルト) pgrep -x myapp # コマンドライン全体でのパターンマッチ(-f オプション) pgrep -f "myapp --daemon --config /etc/myapp/myapp.conf"

現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、20年以上の運用経験を持つ現役エンジニアが基礎から教えます。
シェルスクリプト講座を見る >>

本記事のまとめ

シェルスクリプトでwatchdogを設計するポイントは、単に「再起動する」だけでなく「いつ諦めるか」と「誰に知らせるか」を最初から設計に組み込むことです。再起動が成功しても根本原因が残っていれば何度でも繰り返すため、カウンタ上限を超えたらアラートを送って人間に判断を委ねる設計が現場での鉄則になります。
やりたいこと コマンド・設計パターン
PIDでプロセス存在確認 kill -0 $PID 2>/dev/null
プロセス名で確認 pgrep -x プロセス名
コマンドライン全体で確認 pgrep -f "プロセス名 オプション"
systemdサービスの確認 systemctl is-active --quiet サービス名
再起動回数の上限管理 カウンタファイルをインクリメント、正常時は0にリセット
連続失敗時のアラート echo 本文 | mail -s 件名 宛先
cron二重起動防止 flock -n /tmp/lock.file スクリプト
cron環境のパス問題 crontabとスクリプト冒頭の両方でPATHを明示

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

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

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

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

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

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

姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

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

この記事を書いた人

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

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

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