前の記事では、「動かない」ときの切り分けフローを作りました。でも、そもそも「動いていない」ことに気づけなければ、切り分けも始まりません。この記事は Lv5 の最後、そして連載の最後の記事です。サーバーが止まったら知らせてくれる死活監視のしくみを作ります。
もし、VPS で動かしている n8n が、知らないうちに止まっていたら…。ある朝ふと管理画面を開いたらつながらず、調べてみると、しばらく前から止まっていて、その間の自動化はひとつも動いていなかった、ということもありえます。そうなったら、「止まったら、すぐに知らせてくれればいいのに」と思いますよね。
この記事では、手元の練習用 Web サーバーを相手に、状態が変わったときだけ通知する小さな監視スクリプトを作ります。通知先(Webhook の URL)は空のままにして、通知はファイルに書くだけにするので、外には何も送りません。
- 死活監視(=サーバーが動いているかを、外から定期的に確かめること)とは何か、なぜ「別の場所から」見るのがよいのか
-
curlで HTTP(=ブラウザや curl が Web サーバーとやりとりするときの決まり)の状態コードを取り出して、「動いている/止まっている」を判定する方法 - 状態が変わったときだけ通知して、通知の出しすぎを防ぐ方法
- Webhook の URL を秘密として扱う方法
- n8n で外から見張って、Slack などに知らせる流れ
- シェルスクリプトの変数と
ifがわかる -
curlを使ったことがある - systemd timer か cron で、スクリプトを定期的に動かしたことがある
まだの方は、前の記事「Linuxで「動かない」ときの切り分けフロー|権限・PATH・ログ・ネットワークの順に見る【初心者向け】」から読むのがおすすめです。定期実行については「systemd timerとcronの違い|Linuxの定期実行を使い分ける【初心者向け】」でくわしく説明しています。
- VPS:大きな物理サーバーを区切って作った、自分専用の仮想サーバーを、月額などで借りられるサービス。root 権限で自由に設定できます
- n8n:画面上で「ノード」というブロックをつないで、自動化の流れ(ワークフロー)を作れるツール。自分のサーバーでも動かせます
- curl:ターミナルから URL にアクセスして、データを受け取ったり送ったりするコマンド
- 待ち受け(リッスン):プログラムが、決まったポートで通信が来るのを待っている状態
用語で迷ったら、連載のまとめ記事の「よく出てくる用語ミニ辞典」でも確かめられます。
結論:「定期的に確かめて、変わったときだけ知らせる」
| 部品 | 役割 | この記事で使うもの |
|---|---|---|
| 確かめる | 相手が返事をするか見る | curl -w "%{http_code}" |
| 覚えておく | 前回の状態を残す | state.txt |
| 知らせる | 変わったときだけ通知 | notify.log(本番は Webhook) |
| くり返す | 数分ごとに動かす | systemd timer か cron |
| 外から見る | サーバーごと止まっても気づく | 別の場所の n8n |
準備:見張る相手と、監視スクリプトを用意する
練習用のディレクトリを作り、監視スクリプト check.sh を書きます。下のブロックをまとめて貼りつけると、check.sh ができます(<< 'EOF' から EOF までがファイルの中身です)。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
mkdir -p ~/linux-practice/monitor-test
cd ~/linux-practice/monitor-test
cat > check.sh << 'EOF'
#!/bin/bash
cd "$(dirname "$0")"
URL="http://127.0.0.1:8080/"
WEBHOOK_URL=""
code=$(curl -s -o /dev/null -w "%{http_code}" --max-time 5 "$URL")
if [ "$code" = "200" ]; then now="UP"; else now="DOWN"; fi
before=$(cat state.txt 2>/dev/null || echo "UNKNOWN")
echo "$(date '+%F %T') $now (code=$code)" >> check.log
if [ "$now" != "$before" ]; then
msg="[demo] $URL が $before から $now になりました"
if [ -z "$WEBHOOK_URL" ]; then
echo "$(date '+%F %T') 通知: $msg" >> notify.log
else
curl -s -X POST -H "Content-Type: application/json" -d "{\"text\": \"$msg\"}" "$WEBHOOK_URL" > /dev/null
fi
echo "$now" > state.txt
fi
EOF
chmod u+x check.sh
ls -l check.shここから先の出力例は、Debian 13での実行例です(ユーザー名 kaito・パソコン名 demo のダミー環境)。UbuntuやWSL(=Windows の中で Linux を動かせる、Microsoft のしくみ)では、表示が少し違うことがあります。
(例)
- 1
- 2
- 3
kaito@demo:~/linux-practice/monitor-test$ chmod u+x check.sh
kaito@demo:~/linux-practice/monitor-test$ ls -l check.sh
-rwxrw-r-- 1 kaito kaito 648 Nov 1 04:19 check.shx がついて、実行できるようになりました。検証環境は新しいファイルに「グループも書きこめる」設定(umask 002)なので、-rwxrw-r-- になっています。-rwxr--r-- と表示される環境もあります。
ブロックを貼りつけると、2行目から先頭に > が表示されます。これは「続きを待っています」という印で、EOF の行まで来るとファイルができあがります。
スクリプトのしくみは、次のとおりです。
-
-w "%{http_code}"で、HTTP の状態コード(200 なら正常)だけを取り出す。つながらないときは000になる -
state.txtに前回の状態(UP か DOWN)を残しておき、今回とちがうときだけ通知する -
WEBHOOK_URLが空のあいだは、notify.logに書くだけ。外には何も送らない(systemd のEnvironmentFile=で URL を渡すときは、この行を書きかえます。STEP 5 で説明します) - 毎回の結果は
check.logに1行ずつ残す
次に、もう1つターミナルを開いて、見張られる側の Web サーバーを動かします。
- 1
- 2
cd ~/linux-practice/monitor-test
python3 -m http.server 8080 --bind 127.0.0.1(例)
- 1
- 2
- 3
kaito@demo:~$ cd ~/linux-practice/monitor-test
kaito@demo:~/linux-practice/monitor-test$ python3 -m http.server 8080 --bind 127.0.0.1
Serving HTTP on 127.0.0.1 port 8080 (http://127.0.0.1:8080/) ...このあと check.sh を動かすたびに、こちらに "GET / HTTP/1.1" 200 のようなアクセスの記録が1行ずつ出ます。
--bind 127.0.0.1 をつけているので、このサーバーには自分のパソコンの中からしかつながりません。
STEP 1:動いているときに確かめる
元のターミナルに戻って、監視スクリプトを動かします。
- 1
- 2
- 3
./check.sh
cat state.txt
cat notify.log(例)
- 1
- 2
- 3
- 4
- 5
kaito@demo:~/linux-practice/monitor-test$ ./check.sh
kaito@demo:~/linux-practice/monitor-test$ cat state.txt
UP
kaito@demo:~/linux-practice/monitor-test$ cat notify.log
2026-11-01 04:19:55 通知: [demo] http://127.0.0.1:8080/ が UNKNOWN から UP になりました./check.sh は画面には何も出しません。結果は state.txt と notify.log に書かれます。notify.log の行の先頭には、日時と「通知:」がつきます。
はじめての実行なので、「UNKNOWN(不明)から UP」に変わったとして1回通知されました。もう一度動かしてみます。
- 1
- 2
- 3
./check.sh
cat check.log
wc -l notify.log(例)
- 1
- 2
- 3
- 4
- 5
- 6
kaito@demo:~/linux-practice/monitor-test$ ./check.sh
kaito@demo:~/linux-practice/monitor-test$ cat check.log
2026-11-01 04:19:55 UP (code=200)
2026-11-01 04:20:00 UP (code=200)
kaito@demo:~/linux-practice/monitor-test$ wc -l notify.log
1 notify.log確かめた記録(check.log)は2行になりましたが、状態は変わっていないので、通知(notify.log)は増えていません。
STEP 2:止めて、通知が出るか確かめる
もう1つのターミナルで Ctrl + C を押して、Web サーバーを止めます。そのあと、元のターミナルで2回動かします。
- 1
- 2
- 3
- 4
./check.sh
./check.sh
tail -n 2 check.log
cat notify.logもう1つのターミナルで Ctrl + C を押すと、次のように表示されてサーバーが止まります。
(例)
- 1
- 2
^C
Keyboard interrupt received, exiting.(例)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
kaito@demo:~/linux-practice/monitor-test$ ./check.sh
kaito@demo:~/linux-practice/monitor-test$ ./check.sh
kaito@demo:~/linux-practice/monitor-test$ tail -n 2 check.log
2026-11-01 04:20:05 DOWN (code=000)
2026-11-01 04:20:06 DOWN (code=000)
kaito@demo:~/linux-practice/monitor-test$ cat notify.log
2026-11-01 04:19:55 通知: [demo] http://127.0.0.1:8080/ が UNKNOWN から UP になりました
2026-11-01 04:20:05 通知: [demo] http://127.0.0.1:8080/ が UP から DOWN になりました止まったあとは、状態コードが 000(つながらなかった)になりました。curl -s なので、curl のエラーの文は画面に出ません。
止まっている間に何度確かめても、通知は「止まった」の1回だけです。毎回通知していたら、通知があふれて、大事な通知を見落とすようになります。
STEP 3:もどしたら「復旧」の通知が出るか確かめる
もう1つのターミナルで、Web サーバーをもう一度動かします。
- 1
- 2
cd ~/linux-practice/monitor-test
python3 -m http.server 8080 --bind 127.0.0.1元のターミナルで、もう一度確かめます。
- 1
- 2
- 3
./check.sh
cat notify.log
cat state.txt(例)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
kaito@demo:~/linux-practice/monitor-test$ ./check.sh
kaito@demo:~/linux-practice/monitor-test$ cat notify.log
2026-11-01 04:19:55 通知: [demo] http://127.0.0.1:8080/ が UNKNOWN から UP になりました
2026-11-01 04:20:05 通知: [demo] http://127.0.0.1:8080/ が UP から DOWN になりました
2026-11-01 04:20:12 通知: [demo] http://127.0.0.1:8080/ が DOWN から UP になりました
kaito@demo:~/linux-practice/monitor-test$ cat state.txt
UP通知は3行で、「止まった」(04:20:05)と「もどった」(04:20:12)の時刻がわかります。
「止まった」と「もどった」の両方がわかるので、どのくらいの時間止まっていたかも、あとで確かめられます。
STEP 4:定期的に動かす(参考)
本番では、このスクリプトを数分ごとに動かします。やり方は「systemd timerとcronの違い|Linuxの定期実行を使い分ける【初心者向け】」で学んだとおりです。たとえば cron なら、次のような1行になります。この記事では登録しません。
- 1
*/5 * * * * /home/kaito/linux-practice/monitor-test/check.sh5分ごとくらいが目安です。自分のサーバーでも、1秒ごとのように細かくしすぎると、監視そのものが負担になります。
STEP 5:本番の通知先と、外からの見張り(文章で説明します)
Webhook の URL は秘密として扱う
Slack や Discord、n8n の Webhook の URL は、それを知っている人なら誰でもメッセージを送れる鍵のようなものです。スクリプトに直接書かず、chmod 600 にした別のファイル(たとえば ~/.config/monitor/secret.env)に書いて読みこむようにし、Git(=ファイルの変更の履歴を記録しておく道具)には入れないようにしましょう。
読みこむときは、check.sh の WEBHOOK_URL="" の行のあとに [ -f ~/.config/monitor/secret.env ] && . ~/.config/monitor/secret.env の1行を足し、secret.env には WEBHOOK_URL="…" の1行だけを書きます。ファイルの権限は、セキュリティ基礎の記事の chmod 600 と ls -l で確かめましょう。
secret.env から読みこむ(検証)
検証では、本物の Slack のかわりに、自分のパソコンの中(127.0.0.1 の 9000 番)だけで待ち受けるダミーの受け口(hook.py)を作り、そこへ送りました。Slack には何も送っていません。1行を足すのは、sed -i.bak で行いました(/^WEBHOOK_URL=""$/a … は「WEBHOOK_URL="" の行のあとに … を足す」という意味です)。このときは Web サーバーを止めていたので、結果は DOWN です。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
sed -i.bak '/^WEBHOOK_URL=""$/a [ -f ~/.config/monitor/secret.env ] && . ~/.config/monitor/secret.env' check.sh
diff check.sh.bak check.sh
mkdir -p ~/.config/monitor
echo 'WEBHOOK_URL="http://127.0.0.1:9000/dummy-hook"' > ~/.config/monitor/secret.env
chmod 600 ~/.config/monitor/secret.env
ls -l ~/.config/monitor/secret.env
./check.sh
cat check.log state.txt
ls notify.log(例)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
kaito@demo:~/linux-practice/monitor-test$ sed -i.bak '/^WEBHOOK_URL=""$/a [ -f ~/.config/monitor/secret.env ] && . ~/.config/monitor/secret.env' check.sh
kaito@demo:~/linux-practice/monitor-test$ diff check.sh.bak check.sh
4a5
> [ -f ~/.config/monitor/secret.env ] && . ~/.config/monitor/secret.env
kaito@demo:~/linux-practice/monitor-test$ mkdir -p ~/.config/monitor
kaito@demo:~/linux-practice/monitor-test$ echo 'WEBHOOK_URL="http://127.0.0.1:9000/dummy-hook"' > ~/.config/monitor/secret.env
kaito@demo:~/linux-practice/monitor-test$ chmod 600 ~/.config/monitor/secret.env
kaito@demo:~/linux-practice/monitor-test$ ls -l ~/.config/monitor/secret.env
-rw------- 1 kaito kaito 47 Nov 1 06:24 /home/kaito/.config/monitor/secret.env
kaito@demo:~/linux-practice/monitor-test$ ./check.sh
kaito@demo:~/linux-practice/monitor-test$ cat check.log state.txt
2026-11-01 06:24:31 DOWN (code=000)
DOWN
kaito@demo:~/linux-practice/monitor-test$ ls notify.log
ls: cannot access 'notify.log': No such file or directorydiff で、4行目のあとに1行が足されたことがわかります。secret.env は -rw-------(自分だけが読み書きできる)になりました。./check.sh のあと notify.log ができていないのは、URL が読みこまれて、通知が Webhook の URL のほうへ送られたからです。検証は新しいフォルダで行ったので、notify.log はまだありませんでした。この記事の STEP 2 から続けているときは、notify.log の行が増えていないことで確かめます。
受け口の hook.py は、もう1つのターミナルで動かしました。届いた通知は「受信:」の行で表示されます(下の4行は、この節とこのあとの検証で、ターミナル1の手順を進めたときに出たものです)。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
cd ~/linux-practice/monitor-test
cat > hook.py << 'EOF'
import http.server
class H(http.server.BaseHTTPRequestHandler):
def do_POST(self):
body = self.rfile.read(int(self.headers["Content-Length"])).decode()
print("受信:", self.path, body, flush=True)
self.send_response(200)
self.end_headers()
def log_message(self, *a):
pass
http.server.HTTPServer(("127.0.0.1", 9000), H).serve_forever()
EOF
python3 hook.py(例)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
kaito@demo:~$ cd ~/linux-practice/monitor-test
kaito@demo:~/linux-practice/monitor-test$ cat > hook.py << 'EOF'
> import http.server
> class H(http.server.BaseHTTPRequestHandler):
> def do_POST(self):
> body = self.rfile.read(int(self.headers["Content-Length"])).decode()
> print("受信:", self.path, body, flush=True)
> self.send_response(200)
> self.end_headers()
> def log_message(self, *a):
> pass
> http.server.HTTPServer(("127.0.0.1", 9000), H).serve_forever()
> EOF
kaito@demo:~/linux-practice/monitor-test$ python3 hook.py
受信: /dummy-hook {"text": "[demo] http://127.0.0.1:8080/ が UNKNOWN から DOWN になりました"}
受信: /dummy-hook {"text": "[demo] http://127.0.0.1:8080/ が UP から DOWN になりました"}
受信: /dummy-hook {"text": "[demo] http://127.0.0.1:8080/ が DOWN から UP になりました"}
受信: /dummy-hook {"text": "[demo] http://127.0.0.1:8080/sub が UP から DOWN になりました"}1行目が、上の ./check.sh で届いた通知です。
systemd の EnvironmentFile= で読みこむときは、WEBHOOK_URL の行を変える
systemd timer で動かすなら、systemd の記事の EnvironmentFile= で secret.env を読みこむ方法もあります。ただし、この記事の check.sh のままだと、URL が使われません。EnvironmentFile= は、スクリプトが動く前に WEBHOOK_URL を渡しますが、check.sh の4行目の WEBHOOK_URL="" が、その値を空で上書きしてしまうからです。
そこで、4行目を WEBHOOK_URL="${WEBHOOK_URL:-}" に書きかえます。「WEBHOOK_URL に値が入っていればそのまま使い、なければ空にする」という意味です。上の secret.env を読みこむ1行と組み合わせても動きます。
検証では、secret.env を読みこむ1行を足す前の check.sh(check.sh.bak)を check-envonly.sh という名前でコピーし、EnvironmentFile= だけで URL を渡すサービスと、1行を足した check.sh を動かすサービスを作って比べました。3つ目のターミナルで Web サーバーを動かしたり止めたりしながら試しています(最初の start の前に起動、4行目を書きかえたあとに止め、最後の start の前にもう一度起動)。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
cp check.sh.bak check-envonly.sh
cat > ~/.config/systemd/user/monitor-envonly.service << 'EOF'
[Unit]
Description=monitor check (EnvironmentFile only)
[Service]
Type=oneshot
EnvironmentFile=%h/.config/monitor/secret.env
ExecStart=%h/linux-practice/monitor-test/check-envonly.sh
EOF
cat > ~/.config/systemd/user/monitor-check.service << 'EOF'
[Unit]
Description=monitor check (check.sh)
[Service]
Type=oneshot
EnvironmentFile=%h/.config/monitor/secret.env
ExecStart=%h/linux-practice/monitor-test/check.sh
EOF
echo "XDG_RUNTIME_DIR=$XDG_RUNTIME_DIR"
export XDG_RUNTIME_DIR=/run/user/$(id -u)
systemctl --user daemon-reload
systemctl --user start monitor-envonly.service; echo $?
tail -n 1 check.log; cat state.txt; cat notify.log
sed -i 's/^WEBHOOK_URL=""$/WEBHOOK_URL="${WEBHOOK_URL:-}"/' check-envonly.sh
grep -n "^WEBHOOK_URL" check-envonly.sh
systemctl --user start monitor-envonly.service; echo $?
tail -n 1 check.log; cat state.txt
systemctl --user start monitor-check.service; echo $?
tail -n 1 check.log; cat state.txt
journalctl --user -u monitor-check.service --no-pager | tail -n 3(例)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
kaito@demo:~/linux-practice/monitor-test$ cp check.sh.bak check-envonly.sh
kaito@demo:~/linux-practice/monitor-test$ cat > ~/.config/systemd/user/monitor-envonly.service << 'EOF'
> [Unit]
> Description=monitor check (EnvironmentFile only)
>
> [Service]
> Type=oneshot
> EnvironmentFile=%h/.config/monitor/secret.env
> ExecStart=%h/linux-practice/monitor-test/check-envonly.sh
> EOF
kaito@demo:~/linux-practice/monitor-test$ cat > ~/.config/systemd/user/monitor-check.service << 'EOF'
> [Unit]
> Description=monitor check (check.sh)
>
> [Service]
> Type=oneshot
> EnvironmentFile=%h/.config/monitor/secret.env
> ExecStart=%h/linux-practice/monitor-test/check.sh
> EOF
kaito@demo:~/linux-practice/monitor-test$ echo "XDG_RUNTIME_DIR=$XDG_RUNTIME_DIR"
XDG_RUNTIME_DIR=/run/user/1000
kaito@demo:~/linux-practice/monitor-test$ export XDG_RUNTIME_DIR=/run/user/$(id -u)
kaito@demo:~/linux-practice/monitor-test$ systemctl --user daemon-reload
kaito@demo:~/linux-practice/monitor-test$ systemctl --user start monitor-envonly.service; echo $?
0
kaito@demo:~/linux-practice/monitor-test$ tail -n 1 check.log; cat state.txt; cat notify.log
2026-11-01 06:25:09 UP (code=200)
UP
2026-11-01 06:25:09 通知: [demo] http://127.0.0.1:8080/ が DOWN から UP になりました
kaito@demo:~/linux-practice/monitor-test$ sed -i 's/^WEBHOOK_URL=""$/WEBHOOK_URL="${WEBHOOK_URL:-}"/' check-envonly.sh
kaito@demo:~/linux-practice/monitor-test$ grep -n "^WEBHOOK_URL" check-envonly.sh
4:WEBHOOK_URL="${WEBHOOK_URL:-}"
kaito@demo:~/linux-practice/monitor-test$ systemctl --user start monitor-envonly.service; echo $?
0
kaito@demo:~/linux-practice/monitor-test$ tail -n 1 check.log; cat state.txt
2026-11-01 06:25:17 DOWN (code=000)
DOWN
kaito@demo:~/linux-practice/monitor-test$ systemctl --user start monitor-check.service; echo $?
0
kaito@demo:~/linux-practice/monitor-test$ tail -n 1 check.log; cat state.txt
2026-11-01 06:25:22 UP (code=200)
UP
kaito@demo:~/linux-practice/monitor-test$ journalctl --user -u monitor-check.service --no-pager | tail -n 3
Nov 01 06:25:22 demo systemd[1319]: Starting monitor-check.service - monitor check (check.sh)...
Nov 01 06:25:22 demo systemd[1319]: Finished monitor-check.service - monitor check (check.sh).書きかえる前の check-envonly.sh では、URL を渡したのに notify.log に「DOWN から UP」が書かれました。URL が空で上書きされた、ということです。4行目を書きかえたあとは notify.log には書かれず、受け口に「UP から DOWN」が届きました(上の受け口の2行目)。1行を足した check.sh を EnvironmentFile= つきで動かしたときも、受け口に「DOWN から UP」が届いています(3行目)。
echo "XDG_RUNTIME_DIR=…" と export の2行は、su - で入ったときに systemctl --user が使えない場合に備えたものです。検証では、すでに /run/user/1000 が入っていたので、同じ値を入れ直しただけでした。最後の journalctl … | tail -n 3 は、記録が2行しかなかったので2行だけ表示されています。
サーバーの中の監視だけでは足りない理由
この記事のスクリプトは、見張られる側と同じサーバーの中で動いています。この形だと、サーバーごと止まったとき(電源が落ちた、ネットワークが切れた)は、監視スクリプトも止まってしまい、通知が来ません。
そこで本番では、別の場所から見張るのがおすすめです。たとえば、別のパソコンや別のサービスで動かしている n8n で、次のようなワークフローを作ります。
- Schedule Trigger:5分ごとに動かす
- HTTP Request:自分のサーバーの URL にアクセスする(タイムアウトを短めに)
- IF:状態コードが 200 かどうかで分ける
- Slack(または Discord・メール):200 でなかったときに通知する
上は、検証用のパソコンの中だけ(127.0.0.1)で n8n を一時的に動かして、このワークフローを作ったときの画面です。左から順に、次のように流れます。
- Schedule Trigger:5分ごとに動かす
- HTTP Request:
GET http://127.0.0.1:8080/にアクセスする(練習なので、この記事の Web サーバーを指しています。本番では、見張りたいサーバーの URL にします) - If:状態コードが 200 以外なら
trueの道へ進む(falseの道には何もつないでいません) - Slack:
trueのときだけ、メッセージを送る(post: message)
この画面のワークフローは、作っただけで一度も実行していません(このあとの検証では、別に作ったワークフローを手動で実行しました)。右上のスイッチが Inactive(止まっている)になっているのは、そのためです。Slack のノードについている赤い警告の印は、Slack にログインするための情報(認証情報)を入れていないために出ているものです。実際に使うときは、Slack の認証情報と送り先のチャンネルを設定し、スイッチを Active にします。Webhook の URL も、どこにも入れていません。左下の「KD」は、検証用のダミーのユーザーです。
n8n の HTTP Request ノードは、ふつうの設定のままだと、状態コードが 404 や 500 のときや、つながらない(Connection refused)ときに、ノードそのものがエラーになり、ワークフローがそこで止まります。すると、うしろの If と Slack まで進まず、いちばん知りたい「止まった」ときに通知が来ません。検証(n8n 1.123.91、HTTP Request ノード 4.2)でも、404・500・つながらない、の3つとも、ここで止まりました。
実際に使う前に、HTTP Request ノードの設定を見直しておきます。設定の場所や名前は、n8n のバージョンにより表示が異なります。検証の画面では、次の場所にありました。
- 状態コードを受け取る設定:Parameters の Options → Response にある「Include Response Headers and Status」と「Never Error」をオンにします。404 や 500 でもノードが止まらず、成功の出口(Success)から状態コード(
statusCode)が If に渡ります - エラーのときも先に進む設定:Settings の「On Error」を「Continue (using error output)」にします。つながらないときは、エラー用の出口(Error)から出るので、その出口を Slack につなぎます。If は成功の出口の先にあるので、つながらないときは If を通りません
そして、本当にサーバーを止めて通知が届くかを、一度確かめてから Active にしましょう。この記事の STEP 2 で、スクリプトの「止まった」を確かめたのと同じです。もう1つ、このワークフローは「前回の状態」を覚えていないので、止まっている間は5分ごとに通知が届きます。この記事のスクリプトとのちがいとして、知っておきましょう。
自分のものではないサイトを、許可なく短い間隔で監視するのはやめましょう。相手のサーバーの負担になり、攻撃とみなされることがあります。Webhook の URL を GitHub などに公開してしまうのも危険です。もし公開してしまったら、すぐにその URL を無効にして作り直してください。また、「止まったら自動で再起動する」処理を監視スクリプトに入れるのは、慣れるまでは避けましょう。原因がわからないまま再起動をくり返し、ログも消えて、かえって直せなくなることがあります。まずは「知らせる」ところまでにして、直すのは人が切り分けフローで行います。
n8n の設定を、実際に試す(検証)
検証では、検証用のパソコンの中だけ(127.0.0.1 の 5678 番)で n8n 1.123.91 を動かし、上の形のワークフローを手動で実行(Execute workflow)して確かめました。見張る相手は、パソコンの中だけで動かした、わざと 200・404・500 を返すテスト用のサーバーと、何も動かしていないポート(つながらない)です。If は「{{ $json.statusCode }} が 200 と等しくない」ときに true へ進む形です。Slack のノードは無効(Deactivated)にし、認証情報もチャンネルも入れていないので、Slack には何も送っていません。ワークフローは Inactive のままです。
まず、ふつうの設定のままでは、404 で HTTP Request ノードがエラー(赤い印)になり、If と Slack まで進みませんでした。500 も同じです。
つながらないとき(The service refused the connection)も、同じところで止まりました。
次に、HTTP Request ノードの2つの設定を変えました。1つ目は Options → Response の「Include Response Headers and Status」と「Never Error」です。
2つ目は Settings の「On Error」です。選べるのは「Stop Workflow」(止める)、「Continue」(エラーをふつうの出口から出す)、「Continue (using error output)」(エラー用の出口から出す)の3つで、ここでは「Continue (using error output)」にしました。
If の条件は、次のとおりです。
設定を変えたあとは、404 でも止まらず、成功の出口(Success)から If へ進み、If は true になって Slack のノードまで届きました。500 も同じで、200 のときは If が false になり、Slack には進みませんでした。
つながらないときは、エラー用の出口(Error)から出て、If を通らずに Slack のノードへ進みました。
If で判断させたいときは、エラー用の出口も If につなぎます。検証では、エラーの中身には statusCode がないので「200 と等しくない」と判断され、If は true になりました。
「On Error」を「Continue」(エラーをふつうの出口から出す)にした形でも、つながらないときのエラーが If へ進み、If は true になりました。
🤖 詰まったらAIに聞こう:監視のしくみを自分の環境に合わせたい
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
Linuxの初心者です。自分のサーバーの死活監視を作りたいです。
「定期的に確かめる → 前回の状態と比べる → 変わったときだけ通知する」形で、
自分の環境に合わせた作り方を、手順ごとに教えてください。
自動で再起動する処理は入れず、通知だけにしたいです。
【見張りたいもの】例:VPSで動かしている n8n の画面(https の URL)
【監視を動かす場所】例:同じVPSの中 / 自宅のパソコンの n8n
【通知先】例:Slack
【今のスクリプトやワークフロー】ここに貼る
【困っていること】例:止まっていないのに DOWN の通知が来るWebhook の URL は、必ず「***」に置き換えてから貼ってください。URL を知っている人なら誰でも通知を送れてしまいます。サーバーの IP アドレスやドメイン(=IP アドレスの代わりに使う、example.com のような名前)、n8n のログイン情報や API キーも伏せておきましょう。スクリプトを貼るときは、WEBHOOK_URL= の行が空か、伏せてあるかを確かめてください。
後片づけ
もう1つのターミナルで Ctrl + C を押して、Web サーバーを止めます。そのあと、元のターミナルで次を打ちます。
- 1
- 2
- 3
cd ~/linux-practice
rm -r monitor-test
lsもう1つのターミナルで Ctrl + C を押すと、次のように表示されてサーバーが止まります。
(例)
- 1
- 2
^C
Keyboard interrupt received, exiting.(例)
- 1
- 2
- 3
- 4
kaito@demo:~/linux-practice/monitor-test$ cd ~/linux-practice
kaito@demo:~/linux-practice$ rm -r monitor-test
kaito@demo:~/linux-practice$ ls
archive-test chmod-test edit-test find-test grep-test link-test perm-test redirect-testmonitor-test がなくなりました。ほかのフォルダは、これまでの記事の練習で作ったものです。
よくある質問
Q. 止まっていないのに DOWN の通知が来ます。
よくある原因は2つです。1つは、相手が 200 以外を返していることです。たとえば、フォルダの URL の最後の / を忘れると、転送の 301 が返り、このスクリプトでは DOWN になります。http:// の URL が https:// に転送されるときも、301 などになります(こちらは検証では試していません)。curl -s -o /dev/null -w "%{http_code}" URL を手で打って、返ってくる数字を確かめましょう。もう1つは、一瞬だけ遅くなって --max-time 5 を超えたときです。「2回続けて DOWN のときだけ知らせる」形にする方法もあります(ここでは一例です)。
検証では、Web サーバー(python3 -m http.server 8080 --bind 127.0.0.1)の中に sub というフォルダを作り、最後の / のあり・なしを比べました。check301.sh は、check.sh の見張る URL だけを …/sub に変えたものです。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
mkdir sub
curl -s -o /dev/null -w "%{http_code}\n" http://127.0.0.1:8080/sub
curl -s -o /dev/null -w "%{http_code}\n" http://127.0.0.1:8080/sub/
sed 's#8080/"#8080/sub"#' check.sh > check301.sh
chmod u+x check301.sh
./check301.sh
tail -n 1 check.log; cat state.txt(例)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
kaito@demo:~/linux-practice/monitor-test$ mkdir sub
kaito@demo:~/linux-practice/monitor-test$ curl -s -o /dev/null -w "%{http_code}\n" http://127.0.0.1:8080/sub
301
kaito@demo:~/linux-practice/monitor-test$ curl -s -o /dev/null -w "%{http_code}\n" http://127.0.0.1:8080/sub/
200
kaito@demo:~/linux-practice/monitor-test$ sed 's#8080/"#8080/sub"#' check.sh > check301.sh
kaito@demo:~/linux-practice/monitor-test$ chmod u+x check301.sh
kaito@demo:~/linux-practice/monitor-test$ ./check301.sh
kaito@demo:~/linux-practice/monitor-test$ tail -n 1 check.log; cat state.txt
2026-11-01 06:25:34 DOWN (code=301)
DOWN/sub は 301、/sub/ は 200 でした。サーバーは動いているのに、check.log には DOWN (code=301) と記録されています。
次に、8秒待ってから答える遅いサーバー(slow.py)を、もう1つのターミナルで 8080 番で動かして試しました。time は、かかった時間を表示するコマンドです。
- 1
- 2
- 3
time curl -s -o /dev/null -w "%{http_code}\n" --max-time 5 http://127.0.0.1:8080/; echo $?
./check.sh
tail -n 1 check.log; cat state.txt(例)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
kaito@demo:~/linux-practice/monitor-test$ time curl -s -o /dev/null -w "%{http_code}\n" --max-time 5 http://127.0.0.1:8080/; echo $?
000
real 0m5.010s
user 0m0.007s
sys 0m0.000s
28
kaito@demo:~/linux-practice/monitor-test$ ./check.sh
kaito@demo:~/linux-practice/monitor-test$ tail -n 1 check.log; cat state.txt
2026-11-01 06:26:03 DOWN (code=000)
DOWN約5秒(real 0m5.010s)で --max-time 5 の時間切れになり、状態コードは 000、curl の終了コード(=コマンドが終わったときに返す数字)は 28(時間切れ)でした。check.sh でも、サーバーは動いているのに DOWN (code=000) になっています。
Q. WSL の n8n で、VPS を外から見張ってもいいですか?
「別の場所から見張る」形にはなりますが、パソコンを閉じたりスリープしたりすると、WSL も止まり、見張りも止まります。動き方は環境により異なります。練習にはよいですが、本番の見張りには、24時間動いている別の場所を使うほうが安心です。
Q. 監視の間隔は、どれくらいがよいですか?
この記事の STEP 4 のとおり、5分ごとくらいが目安です。短くすると早く気づけますが、監視そのものの負担と、通知の数が増えます。自分のものではないサイトは、この記事の🚫の箱のとおり、許可なく見張らないようにしましょう。
まとめ
- 死活監視は「定期的に確かめて、変わったときだけ知らせる」
-
curl -w "%{http_code}"で状態コードを取り出し、200 かどうかで判定する - 前回の状態をファイルに残すと、通知が「止まった」「もどった」の2回だけになる
- Webhook の URL は秘密。
chmod 600の別ファイルに置き、Git に入れない - サーバーごと止まったときに気づけるよう、本番は外からも見張る
- 知らせるところまでを自動にして、直すのは人が切り分けフローで行う
これで、Linux 入門の連載は全部終わりです。黒い画面に「いまどこ?」と聞くところから始めて、サーバーを動かし、守り、困ったときに調べ、止まったら気づけるところまで来ました。全体を見直したいときは、連載のまとめのページに戻って、気になるレベルから読み返してみてください。
次に読む記事
- 前の記事:Linuxで「動かない」ときの切り分けフロー|権限・PATH・ログ・ネットワークの順に見る【初心者向け】
- 次の記事(連載のまとめにもどる):Linux学習の進め方|黒い画面からVPSで24時間動かすまで、読む順番まとめ
関連する記事
- n8nとは?最初のワークフローを1本つくる:n8n で外から見張るワークフローを作りたい方に
- 自動化スタック総覧|GAS・n8n・Python・Notionの使い分け:監視と通知を、ほかの自動化とどう組み合わせるか知りたい方に
- Pythonで毎朝のCSV集計を自動化する(pandas+定期実行):定期実行する自動化に、監視も組み合わせたい方に