- Posted on
- • Artificial Intelligence
Artificial Intelligence Uptime Monitoring
- Author
-
-
- User
- linuxbash
- Posts by this author
- Posts by this author
-
Artificial Intelligence Uptime Monitoring with Bash: Smarter Alerts, Fewer False Positives
If your status page says “All systems operational” while users are still complaining, you don’t have an uptime problem—you have an observability problem. Traditional checks answer “Is it up?” AI-enhanced checks answer “Is it healthy, normal, and meeting SLOs?” This post shows how to combine Bash with a lightweight AI detector to catch real issues earlier and silence the noise.
What you’ll get:
A tiny, fast Bash probe that logs latency and availability for HTTP endpoints and hosts (ping)
A systemd timer for reliable minute-by-minute checks
A Python “AI” anomaly detector (IsolationForest) to spot unusual slowness and genuine incidents
Optional Slack notifications with context
No heavy agents. No vendor lock-in. Your data stays on your box.
Why AI for uptime?
Static thresholds break with traffic patterns. AI baselines adjust to time-of-day, deployments, and seasonality.
Uptime ≠ health. AI can flag degraded states (e.g., 200 OK but 4× slower) that traditional checks miss.
Alert fatigue kills response time. Anomaly scoring and deduplication reduce noise.
Prerequisites and installation
We’ll use:
curl and ping for checks
jq and bc for light processing
Python 3 with scikit-learn for anomaly detection (small and fast)
Install the packages using your distro’s package manager:
Debian/Ubuntu (apt):
sudo apt update
sudo apt install -y curl jq bc python3 python3-sklearn python3-numpy
# Optional if you prefer cron: sudo apt install -y cron
Fedora/RHEL/CentOS Stream (dnf):
sudo dnf install -y curl jq bc python3 python3-scikit-learn python3-numpy
# Optional if you prefer cron: sudo dnf install -y cronie
openSUSE Leap/Tumbleweed (zypper):
sudo zypper install -y curl jq bc python3 python3-scikit-learn python3-numpy
# Optional if you prefer cron: sudo zypper install -y cronie
Note: We’ll use systemd timers (recommended). Cron is optional.
Step 1 — Define your targets
Create directories and a targets file.
sudo mkdir -p /etc/ai-uptime /var/log/ai-uptime
sudo tee /etc/ai-uptime/targets.txt >/dev/null <<'EOF'
# type,name,value
http,api,https://example.com/health
http,homepage,https://example.com/
ping,dns,1.1.1.1
ping,gateway,192.168.1.1
EOF
Format: type,name,value
http: curl the URL and record status + latency
ping: ICMP a host and record success + latency
Step 2 — Bash probe to collect metrics
Create the probe script.
sudo tee /usr/local/bin/ai-uptime.sh >/dev/null <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
LOG_DIR="/var/log/ai-uptime"
LOG_FILE="$LOG_DIR/metrics.jsonl"
TARGETS_FILE="/etc/ai-uptime/targets.txt"
mkdir -p "$LOG_DIR"
touch "$LOG_FILE"
# Prevent overlap
exec 9>/var/run/ai-uptime.lock
flock -n 9 || exit 0
ts=$(date -Is)
while IFS=, read -r type name value; do
# skip comments/blank lines
[[ -z "${type:-}" || "${type:0:1}" = "#" ]] && continue
case "$type" in
http)
# time_total seconds, http_code
read -r t code < <(curl -sS -o /dev/null -w '%{time_total} %{http_code}' --max-time 10 "$value" || echo "10.000 000")
if [[ "$code" =~ ^[0-9]+$ ]] && (( code >= 200 && code < 400 )); then up=1; else up=0; fi
lat_ms=$(awk -v t="$t" 'BEGIN{printf "%.0f", t*1000}')
printf '{"ts":"%s","target":"%s","type":"http","status":%d,"latency_ms":%d,"up":%d,"url":"%s"}\n' \
"$ts" "$name" "${code:-0}" "$lat_ms" "$up" "$value" >>"$LOG_FILE"
;;
ping)
if out=$(ping -n -c 1 -W 2 "$value" 2>/dev/null); then
up=1
lat=$(printf "%s" "$out" | sed -n 's/.*time=\([0-9.]\+\) ms.*/\1/p' | head -n1)
lat_ms=${lat%.*}
[[ -z "$lat_ms" ]] && lat_ms=0
else
up=0
lat_ms=0
fi
printf '{"ts":"%s","target":"%s","type":"ping","status":%d,"latency_ms":%d,"up":%d,"host":"%s"}\n' \
"$ts" "$name" "$up" "$lat_ms" "$up" "$value" >>"$LOG_FILE"
;;
*)
echo "Unknown type $type for $name" >&2
;;
esac
done < "$TARGETS_FILE"
EOF
sudo chmod +x /usr/local/bin/ai-uptime.sh
Quick test:
/usr/local/bin/ai-uptime.sh
tail -n3 /var/log/ai-uptime/metrics.jsonl
Step 3 — Run it every minute with systemd timers
Service and timer:
sudo tee /etc/systemd/system/ai-uptime.service >/dev/null <<'EOF'
[Unit]
Description=AI Uptime probe
[Service]
Type=oneshot
ExecStart=/usr/local/bin/ai-uptime.sh
EOF
sudo tee /etc/systemd/system/ai-uptime.timer >/dev/null <<'EOF'
[Unit]
Description=Run AI Uptime probe every minute
[Timer]
OnBootSec=1min
OnUnitActiveSec=60s
AccuracySec=5s
Unit=ai-uptime.service
[Install]
WantedBy=timers.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable --now ai-uptime.timer
systemctl list-timers ai-uptime.timer
Prefer cron? Example:
(crontab -l 2>/dev/null; echo "* * * * * /usr/local/bin/ai-uptime.sh") | crontab -
Step 4 — AI anomaly detector (IsolationForest)
This small Python script learns a baseline per target and flags unusual slowness or drops. It requires python3 and scikit-learn (installed above).
sudo tee /usr/local/bin/ai-uptime-anomaly.py >/dev/null <<'EOF'
#!/usr/bin/env python3
import json, os, sys, time
from datetime import datetime, timedelta
from collections import defaultdict, deque
from urllib.request import Request, urlopen
from urllib.error import URLError
import numpy as np
from sklearn.ensemble import IsolationForest
LOG_FILE = "/var/log/ai-uptime/metrics.jsonl"
ANOMALY_LOG = "/var/log/ai-uptime/anomalies.log"
ENV_FILE = "/etc/ai-uptime/env" # optional AI_UPTIME_SLACK_WEBHOOK="..."
def load_env(path):
env = {}
if not os.path.exists(path): return env
with open(path) as f:
for line in f:
line = line.strip()
if not line or line.startswith("#") or "=" not in line: continue
k, v = line.split("=", 1)
env[k.strip()] = v.strip().strip('"').strip("'")
return env
def tail_lines(path, n=1000):
if not os.path.exists(path): return []
with open(path, 'r') as f:
return list(deque(f, maxlen=n))
def pctl(a, q):
if len(a) == 0: return 0.0
return float(np.percentile(a, q))
def post_slack(webhook, text):
if not webhook: return
data = json.dumps({"text": text}).encode("utf-8")
req = Request(webhook, data=data, headers={"Content-Type":"application/json"})
try:
urlopen(req, timeout=5).read()
except URLError:
pass
def main():
env = load_env(ENV_FILE)
webhook = env.get("AI_UPTIME_SLACK_WEBHOOK", os.getenv("AI_UPTIME_SLACK_WEBHOOK", ""))
rows = []
for line in tail_lines(LOG_FILE, n=2000):
try:
rows.append(json.loads(line))
except Exception:
continue
groups = defaultdict(list)
for r in rows:
key = f"{r.get('type')}::{r.get('target')}"
ts = r.get("ts")
try:
t = datetime.fromisoformat(ts)
except Exception:
continue
lat = float(r.get("latency_ms", 0.0))
up = int(r.get("up", 0))
groups[key].append((t, lat, up, r))
anomalies = []
for key, items in groups.items():
items.sort(key=lambda x: x[0])
if len(items) < 30:
continue # need minimal history
# features: adjusted latency (penalize failures), up flag
lats = np.array([it[1] for it in items], dtype=float)
ups = np.array([it[2] for it in items], dtype=int)
# replace failed latency with a high value to make it stand out
base = np.median(lats[lats > 0]) if np.any(lats > 0) else 1000.0
adj_lats = np.where(ups == 1, lats, np.maximum(lats, base * 2.0))
# train on older window, score latest point
X = np.vstack([adj_lats, ups]).T
if X.shape[0] < 30:
continue
train_X = X[:-1]
test_x = X[-1].reshape(1, -1)
# contamination ~ 5% expected anomalies
clf = IsolationForest(n_estimators=100, contamination=0.05, random_state=42)
clf.fit(train_X)
pred = clf.predict(test_x)[0] # -1 anomaly, 1 normal
score = clf.decision_function(test_x)[0]
last_t, last_lat, last_up, last_raw = items[-1]
# secondary heuristic: spike vs recent baseline
recent = adj_lats[-21:-1] if adj_lats.shape[0] > 21 else adj_lats[:-1]
p90 = pctl(recent, 90)
spike = (adj_lats[-1] > max(p90, 1.2 * (np.median(recent) if recent.size else base)))
if pred == -1 or spike or last_up == 0:
msg = f"[{last_t.isoformat()}] Anomaly: {key} up={last_up} latency_ms={int(last_lat)} score={score:.3f} p90={int(p90)}"
anomalies.append((msg, last_raw))
if anomalies:
with open(ANOMALY_LOG, "a") as f:
for msg, raw in anomalies:
f.write(json.dumps({"ts": datetime.utcnow().isoformat(), "msg": msg, "row": raw}) + "\n")
text = "*AI Uptime* detected anomalies:\n" + "\n".join(f"- {m}" for m, _ in anomalies[:10])
post_slack(webhook, text)
if __name__ == "__main__":
main()
EOF
sudo chmod +x /usr/local/bin/ai-uptime-anomaly.py
Optional Slack webhook:
sudo tee /etc/ai-uptime/env >/dev/null <<'EOF'
# Optional Slack Incoming Webhook URL
AI_UPTIME_SLACK_WEBHOOK="https://hooks.slack.com/services/XXX/YYY/ZZZ"
EOF
Run once to verify:
/usr/bin/python3 /usr/local/bin/ai-uptime-anomaly.py
tail -n3 /var/log/ai-uptime/anomalies.log
Step 5 — Schedule the detector
sudo tee /etc/systemd/system/ai-uptime-anomaly.service >/dev/null <<'EOF'
[Unit]
Description=AI Uptime anomaly detector
[Service]
Type=oneshot
ExecStart=/usr/bin/python3 /usr/local/bin/ai-uptime-anomaly.py
EOF
sudo tee /etc/systemd/system/ai-uptime-anomaly.timer >/dev/null <<'EOF'
[Unit]
Description=Run AI Uptime anomaly detector every 5 minutes
[Timer]
OnBootSec=2min
OnUnitActiveSec=5min
AccuracySec=15s
Unit=ai-uptime-anomaly.service
[Install]
WantedBy=timers.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable --now ai-uptime-anomaly.timer
Check status:
systemctl list-timers | grep ai-uptime
journalctl -u ai-uptime.service -u ai-uptime-anomaly.service --since "10 min ago" -n 100 -e
Real-world usage, tuning, and examples
Start with SLOs, not guesses
- Example: “Homepage p95 < 400ms, API health < 250ms.” Adjust contamination (0.05 → 0.02) to be stricter or looser.
Combine signals for confidence
- Keep both HTTP and ping checks. A ping OK + HTTP slow suggests app issue; both failing suggests network.
Reduce noise with context
- The detector compares to a rolling baseline (recent p90 + IsolationForest score). This turns deploy spikes and night traffic changes into fewer pages.
Add another check in seconds
- Append to /etc/ai-uptime/targets.txt:
http,checkout,https://example.com/checkout/health ping,public-dns,8.8.8.8- Your next run picks it up automatically.
Send the alert to the right place
- Use the Slack webhook, or modify the Python to POST to PagerDuty, Matrix, or email. The small, self-describing JSON lines make integration easy.
Optional log rotation (recommended):
sudo tee /etc/logrotate.d/ai-uptime >/dev/null <<'EOF'
/var/log/ai-uptime/metrics.jsonl /var/log/ai-uptime/anomalies.log {
daily
rotate 7
compress
missingok
notifempty
copytruncate
}
EOF
Troubleshooting
No logs? Run the probe manually:
bash -x /usr/local/bin/ai-uptime.shPermissions or SELinux denials? Check:
journalctl -xeToo many anomalies? Lower sensitivity by:
- Increasing training history (edit script to require more samples)
- Reducing contamination to 0.02
- Requiring both an IsolationForest anomaly and a spike to alert
Conclusion and next steps
You now have a lean, portable uptime monitor that goes beyond red/green, using AI to learn your baseline and flag what actually matters. From here:
Roll it out to staging, then production
Add synthetic transactions (e.g., “login” flow) as additional http targets
Ship metrics.jsonl to your time-series or log system for dashboards
Extend the Python to deduplicate repeated alerts and tag with deployment info
If this helped, try adding three of your most business-critical endpoints to the targets file and watch the anomaly log for a day. You’ll likely find “degraded-but-not-down” moments that were previously invisible.