- Posted on
- • Artificial Intelligence
Artificial Intelligence Linux Health Checks
- Author
-
-
- User
- linuxbash
- Posts by this author
- Posts by this author
-
Artificial Intelligence Linux Health Checks: Smarter Bash For Your Servers
Your server looked fine two minutes ago. Now it’s paging you. Traditional health checks tell you what is broken after it breaks. What if your checks could also notice weirdness early, weigh what matters most, and propose next steps? That’s the promise of bringing an AI mindset to Linux health checks—without abandoning your beloved Bash.
In this post you’ll:
Set up a portable health-check toolchain on any major Linux distro
Build a single Bash script that emits clean JSON and a metrics CSV
Schedule it with systemd timers
Add a lightweight anomaly detector to catch issues before they bite
See real-world examples and triage hints
The goal: actionable, privacy-safe, distro-agnostic checks that run locally and evolve with your systems.
Why “AI” belongs in your health checks
Noise reduction: Static thresholds create alert fatigue. Simple anomaly detection helps flag unusual behavior relative to your system’s own baseline.
Prioritization: Not every spike is equal. A model can weigh multi-metric context (CPU + memory + disk) to gauge urgency.
Explainability: Pair AI scoring with human-friendly heuristics to suggest likely fixes.
Practicality: You don’t need GPUs or cloud APIs—just Bash plus a tiny Python helper.
1) Install the toolchain
We’ll use these packages:
sysstat (mpstat, iostat) for CPU and I/O
smartmontools for disk health
lm-sensors for temperatures and voltages
jq for JSON
python3 and pip for a small anomaly detector
bc for some shell math
Debian/Ubuntu (apt):
sudo apt update
sudo apt install -y sysstat smartmontools lm-sensors jq python3 python3-pip bc
Fedora/RHEL/CentOS Stream (dnf):
sudo dnf install -y sysstat smartmontools lm_sensors jq python3 python3-pip bc
openSUSE/SLE (zypper):
sudo zypper refresh
sudo zypper install -y sysstat smartmontools lm_sensors jq python3 python3-pip bc
Optional but recommended (detect sensors):
sudo sensors-detect
# Follow prompts; generally safe to accept defaults
2) A single Bash snapshot that writes JSON and CSV
Save this as /usr/local/bin/health-snapshot.sh and make it executable.
#!/usr/bin/env bash
set -euo pipefail
OUTDIR="/var/log/ai-health"
mkdir -p "$OUTDIR"
TS="$(date -Is)"
HOST="$(hostname -f 2>/dev/null || hostname)"
JSON_FILE="$OUTDIR/health-$TS.json"
CSV_FILE="$OUTDIR/metrics.csv"
# Load averages (1, 5, 15)
read LOAD1 LOAD5 LOAD15 _ < /proc/loadavg
# Memory (kB)
MEM_TOTAL=$(awk '/MemTotal:/ {print $2}' /proc/meminfo)
MEM_AVAIL=$(awk '/MemAvailable:/ {print $2}' /proc/meminfo)
SWAP_TOTAL=$(awk '/SwapTotal:/ {print $2}' /proc/meminfo)
SWAP_FREE=$(awk '/SwapFree:/ {print $2}' /proc/meminfo)
MEM_USED_KB=$((MEM_TOTAL - MEM_AVAIL))
MEM_USED_PCT=$(awk -v u="$MEM_USED_KB" -v t="$MEM_TOTAL" 'BEGIN{printf "%.2f", (u/t)*100}')
SWAP_USED_PCT=$(awk -v t="$SWAP_TOTAL" -v f="$SWAP_FREE" 'BEGIN{ if (t==0) {print 0} else {printf "%.2f", ((t-f)/t)*100}}')
# CPU usage via mpstat
CPU_IDLE=$(mpstat 1 1 | awk '/Average:/ && $3 ~ /all|CPU/ {print $(NF)}')
CPU_IDLE=${CPU_IDLE:-0}
CPU_USED_PCT=$(awk -v idle="$CPU_IDLE" 'BEGIN{printf "%.2f", 100-idle}')
# Disks usage (per mount)
DISKS_JSON=$(df -P -B1 | awk 'NR>1 {printf "{\"fs\":\"%s\",\"mount\":\"%s\",\"size\":%s,\"used\":%s,\"avail\":%s,\"usep\":\"%s\"}\n",$1,$6,$2,$3,$4,$5}' \
| jq -s 'map(.usep=(.usep|sub("%$";"")|tonumber))')
# Temperatures (best effort)
SENSORS_JSON="{}"
if command -v sensors >/dev/null; then
if sensors -j >/dev/null 2>&1; then
SENSORS_JSON=$(sensors -j || echo "{}")
else
SENSORS_JSON=$(sensors 2>/dev/null | jq -R -s 'split("\n")' 2>/dev/null || echo "{}")
fi
fi
# Top CPU processes
TOPPROCS=$(ps -eo pid,comm,%cpu,%mem --no-headers --sort=-%cpu | head -n 5 \
| awk '{printf "{\"pid\":%s,\"comm\":\"%s\",\"cpu\":%s,\"mem\":%s}\n",$1,$2,$3,$4}' | jq -s '.')
# Network summary (sockets)
NET_SUMMARY=$(ss -s 2>/dev/null || echo "ss not available")
# Simple heuristics
DISK_ALERT=$(echo "$DISKS_JSON" | jq '[.[]|select(.usep>=85)]|length>0')
MEM_ALERT=$(awk -v p="$MEM_USED_PCT" 'BEGIN{print (p>=90)?"true":"false"}')
CPU_ALERT=$(awk -v p="$CPU_USED_PCT" 'BEGIN{print (p>=90)?"true":"false"}')
# Build JSON
jq -n \
--arg ts "$TS" \
--arg host "$HOST" \
--arg load1 "$LOAD1" --arg load5 "$LOAD5" --arg load15 "$LOAD15" \
--arg cpu_used "$CPU_USED_PCT" \
--arg mem_used "$MEM_USED_PCT" --arg swap_used "$SWAP_USED_PCT" \
--arg net_summary "$NET_SUMMARY" \
--argjson disks "$DISKS_JSON" \
--argjson sensors "$SENSORS_JSON" \
--argjson top "$TOPPROCS" \
--argjson disk_alert "$DISK_ALERT" \
--argjson mem_alert "$MEM_ALERT" \
--argjson cpu_alert "$CPU_ALERT" \
'{
ts:$ts, host:$host,
load:{l1:($load1|tonumber), l5:($load5|tonumber), l15:($load15|tonumber)},
cpu:{used:($cpu_used|tonumber)},
memory:{used_pct:($mem_used|tonumber), swap_used_pct:($swap_used|tonumber)},
disks:$disks,
sensors:$sensors,
top_processes:$top,
net_summary:$net_summary,
alerts:{cpu:$cpu_alert, memory:$mem_alert, disk:$disk_alert}
}' | tee "$JSON_FILE" >/dev/null
# Append CSV row (for anomaly detection)
MAX_DISK=$(echo "$DISKS_JSON" | jq '[.[].usep]|max // 0')
if [[ ! -f "$CSV_FILE" ]]; then
echo "ts,load1,cpu_used,mem_used_pct,swap_used_pct,max_disk_usep" > "$CSV_FILE"
fi
echo "$TS,$LOAD1,$CPU_USED_PCT,$MEM_USED_PCT,$SWAP_USED_PCT,$MAX_DISK" >> "$CSV_FILE"
echo "Wrote $JSON_FILE and updated $CSV_FILE"
Make it executable:
sudo install -m 0755 /usr/local/bin/health-snapshot.sh /usr/local/bin/health-snapshot.sh
Optional: If you also want disk SMART health, add this snippet inside the script (after DISKS_JSON), but note parsing can vary by device:
# SMART quick overall health check (optional)
if command -v smartctl >/dev/null; then
for d in $(lsblk -ndo NAME,TYPE | awk '$2=="disk"{print "/dev/"$1}'); do
smartctl -H "$d" 2>/dev/null | awk -v dev="$d" '/SMART overall-health/ {print dev": "$0}'
done
fi
3) Schedule it with systemd timers (every 5 minutes)
Create a one-shot service: /etc/systemd/system/ai-health-snapshot.service
[Unit]
Description=AI Health Snapshot
[Service]
Type=oneshot
ExecStart=/usr/local/bin/health-snapshot.sh
Create a timer: /etc/systemd/system/ai-health-snapshot.timer
[Unit]
Description=Run AI Health Snapshot every 5 minutes
[Timer]
OnCalendar=*:0/5
Persistent=true
[Install]
WantedBy=timers.target
Enable and test:
sudo systemctl daemon-reload
sudo systemctl enable --now ai-health-snapshot.timer
sudo systemctl start ai-health-snapshot.service
journalctl -u ai-health-snapshot.service -n 50 --no-pager
ls -1 /var/log/ai-health/
4) Add a lightweight AI anomaly detector
We’ll train an Isolation Forest on your own history to flag unusual states and suggest triage steps.
Install Python deps (via pip):
python3 -m pip install --user numpy scikit-learn
Create /usr/local/bin/anomaly-detect.py:
#!/usr/bin/env python3
import csv, sys, os
import numpy as np
from sklearn.ensemble import IsolationForest
csv_file = sys.argv[1] if len(sys.argv) > 1 else "/var/log/ai-health/metrics.csv"
with open(csv_file) as f:
rows = list(csv.DictReader(f))
if len(rows) < 16:
print("Not enough history (need >=16 rows) for anomaly detection.")
sys.exit(0)
X = np.array([[float(r['load1']),
float(r['cpu_used']),
float(r['mem_used_pct']),
float(r['swap_used_pct']),
float(r['max_disk_usep'])] for r in rows])
# Train on all but the latest row, score the latest
train, latest = X[:-1], X[-1].reshape(1, -1)
clf = IsolationForest(n_estimators=200, contamination='auto', random_state=42)
clf.fit(train)
score = clf.decision_function(latest)[0] # higher = more normal
pred = clf.predict(latest)[0] # -1 anomalous, 1 normal
# Explain with simple z-scores
means = train.mean(axis=0)
stds = train.std(axis=0)
zs = (latest[0] - means) / np.where(stds == 0, 1, stds)
labels = ['load1', 'cpu_used', 'mem_used_pct', 'swap_used_pct', 'max_disk_usep']
top_idx = int(np.argmax(np.abs(zs)))
print(f"Anomaly={'YES' if pred == -1 else 'no'} score={score:.3f}")
print("Top deviating metric:", labels[top_idx], f"z={zs[top_idx]:.2f}")
if pred == -1:
hints = []
if labels[top_idx] == 'max_disk_usep' and latest[0][4] > 85:
hints.append("Disk nearing capacity. Clean large logs (journalctl --disk-usage, logrotate), prune containers, or expand the filesystem.")
if labels[top_idx] == 'mem_used_pct' and latest[0][2] > 90:
hints.append("High memory pressure. Check top processes (ps/top), tune services, or add RAM.")
if labels[top_idx] == 'cpu_used' and latest[0][1] > 90:
hints.append("Sustained CPU saturation. Find hot processes, consider cgroup limits, or scale out.")
if labels[top_idx] == 'swap_used_pct' and latest[0][3] > 20:
hints.append("Swap usage rising. Investigate memory leaks, tune vm.swappiness, reduce overcommit, or add RAM.")
if labels[top_idx] == 'load1' and latest[0][0] > (os.cpu_count() or 1):
hints.append("Load exceeds CPU cores. Check I/O wait (iostat), run queue length, and lock contention.")
for h in hints:
print("Hint:", h)
Make it executable:
sudo install -m 0755 /usr/local/bin/anomaly-detect.py /usr/local/bin/anomaly-detect.py
Add a timer to run it hourly and log results: /etc/systemd/system/ai-health-anomaly.service
[Unit]
Description=AI Health Anomaly Detection
[Service]
Type=oneshot
ExecStart=/usr/local/bin/anomaly-detect.py
/etc/systemd/system/ai-health-anomaly.timer
[Unit]
Description=Run AI Health Anomaly Detection hourly
[Timer]
OnCalendar=hourly
Persistent=true
[Install]
WantedBy=timers.target
Enable it:
sudo systemctl daemon-reload
sudo systemctl enable --now ai-health-anomaly.timer
journalctl -u ai-health-anomaly.service -n 20 --no-pager
5) Real-world playbook (what to look for)
Disk fills up “out of nowhere”
- Symptom: anomaly flags max_disk_usep; JSON shows a mount > 90%.
- Triage:
- Check large files:
sudo du -xhd1 / | sort -h | tail - Journal size:
journalctl --disk-usage - Containers/images:
docker system dforpodman system df - Consider
logrotateand alert thresholds at 80/85/90%
Memory pressure and swap creep
- Symptom: mem_used_pct high, swap_used_pct trending up
- Triage:
- Top processes:
ps -eo pid,comm,rss,%mem --sort=-rss | head - Page activity:
vmstat 1 5 - Tune: lower
vm.swappiness, fix leaks, right-size caches
CPU saturation or run queue spikes
- Symptom: cpu_used > 90, load1 > cores
- Triage:
- Hot processes:
top -Horpidstat -u 1 - I/O wait:
iostat -xz 1 3(from sysstat) - Consider cgroups, concurrency limits, or scaling out
Thermal throttling (hidden performance killer)
- Symptom: sensors show high temps
- Triage:
- Clean airflow, verify fans
- Laptop/edge devices: governers,
tlp(where applicable) - Sustained heat often correlates with CPU slowdowns
Conclusion and Call To Action
You don’t need a data science team to make your Linux health checks smarter. Start with the Bash snapshot, schedule it, and layer in the anomaly detector. As your metrics CSV grows, your system’s own baseline becomes its best defense against surprise outages.
Next steps:
Deploy the snapshot script and timers to a staging server today
Watch /var/log/ai-health for a day or two
Enable the anomaly timer and review the logs
Iterate: add service-specific checks (databases, queues), and expand heuristics
If you want a follow-up, I can share examples for service-specific rules (PostgreSQL, NGINX, Kubernetes nodes) and guidance for alerting via email, Slack, or Prometheus-compatible exporters.
Stay proactive. Let Bash do the collecting—and let a tiny bit of AI help you see the story in the data.