Posted on
Artificial Intelligence

Artificial Intelligence Monitoring Checklists

Author
  • User
    linuxbash
    Posts by this author
    Posts by this author

The Bash-Friendly AI Monitoring Checklist: What to Watch, Why It Matters, and How To Ship It Today

Artificial Intelligence systems rarely fail loudly. They drift. They slow down. They silently chew through GPUs while returning “successful” responses that are subtly wrong. If you run AI in production on Linux, you need a monitoring checklist that’s practical, automatable, and Bash-first.

This post gives you a compact, field-tested checklist for AI monitoring, why each item matters, and ready-to-run Bash snippets to get you started. You’ll also get package installation commands for apt, dnf, and zypper wherever tools are cited.

Why this matters

  • AI workloads are resource-hungry. A single runaway job can starve your cluster and spike costs.

  • Models degrade over time. Inputs shift, distributions drift, and performance dips without tripping errors.

  • Latency and throughput determine user experience. You need SLOs that measure reality, not hope.

  • Reproducibility is your parachute. When a model misbehaves, you’ll want the code/data/version fingerprint immediately.

A solid monitoring checklist lets you catch issues early, prove causality faster, and keep performance predictable.


Quick install: the essentials used below

Install the core tools you’ll use in this post.

  • Debian/Ubuntu (apt):
sudo apt update
# Choose Docker or Podman (either works for the containerized examples)
sudo apt install -y curl jq bc sysstat docker.io
# Or, if you prefer Podman:
# sudo apt install -y curl jq bc sysstat podman
# Optional: Python if you plan to script beyond Bash
sudo apt install -y python3 python3-pip
  • Fedora/RHEL/CentOS (dnf):
sudo dnf -y install curl jq bc sysstat docker
# Or, if you prefer Podman:
# sudo dnf -y install curl jq bc sysstat podman
sudo dnf -y install python3 python3-pip
  • openSUSE (zypper):
sudo zypper refresh
sudo zypper install -y curl jq bc sysstat docker
# Or, if you prefer Podman:
# sudo zypper install -y curl jq bc sysstat podman
sudo zypper install -y python3 python3-pip

Note on GPUs: nvidia-smi comes with the NVIDIA driver packages, which vary by distro and driver version. If present, the scripts below will use it; if not, they’ll skip GPU collection gracefully.


The AI Monitoring Checklist (with Bash you can copy/paste)

Here are five actionable checks you can run today. Each includes what to watch, why it matters, and a practical Bash snippet.

1) Watch the host and accelerators (CPU, RAM, Disk I/O, GPU, Net)

  • Why: Resource saturation is the root cause of most “my model is slow” incidents.

  • What: CPU load, memory pressure, disk I/O wait, network saturation, and GPU utilization/memory.

Try this minimal sampler. It logs system and (optionally) GPU metrics and can also emit Prometheus textfile metrics if you use node_exporter later.

#!/usr/bin/env bash
# ai-sys-sample.sh
set -euo pipefail

TS="$(date -Is)"
HOST="$(hostname)"

# CPU load averages
read -r LA1 LA5 LA15 _ < /proc/loadavg

# Memory (kB)
MEM_TOTAL=$(awk '/MemTotal/ {print $2}' /proc/meminfo)
MEM_AVAILABLE=$(awk '/MemAvailable/ {print $2}' /proc/meminfo)
MEM_USED=$((MEM_TOTAL - MEM_AVAILABLE))

# Disk I/O (requires sysstat for iostat, else skip)
if command -v iostat >/dev/null 2>&1; then
  IO=$(iostat -dx 1 1 | awk '/^[a-z]/ {printf "%s:%s:util=%s,await=%s; ", NR,$1,$NF,$(NF-3)}')
else
  IO="iostat_not_available"
fi

# Network bytes (aggregate)
NET_RX=$(awk '/:/{sum+=$2} END{print sum}' /proc/net/dev)
NET_TX=$(awk '/:/{sum+=$10} END{print sum}' /proc/net/dev)

# GPU (optional nvidia-smi)
GPU_SUMMARY="no_gpu"
if command -v nvidia-smi >/dev/null 2>&1; then
  GPU_SUMMARY=$(nvidia-smi --query-gpu=index,name,utilization.gpu,memory.used,memory.total --format=csv,noheader | tr '\n' ';' )
fi

LOG_LINE="$TS host=$HOST la1=$LA1 la5=$LA5 la15=$LA15 mem_used_kb=$MEM_USED mem_total_kb=$MEM_TOTAL net_rx_bytes=$NET_RX net_tx_bytes=$NET_TX io=[$IO] gpu=[$GPU_SUMMARY]"
echo "$LOG_LINE"

# Optional: write Prometheus textfile metrics for node_exporter textfile collector
# Uncomment and adjust path if using node_exporter
# OUT="/var/lib/node_exporter/textfile_collector/ai.prom"
# sudo mkdir -p "$(dirname "$OUT")"
# cat > /tmp/ai.prom.$$ <<EOF
# ai_la1 $LA1
# ai_la5 $LA5
# ai_la15 $LA15
# ai_mem_used_kb $MEM_USED
# ai_mem_total_kb $MEM_TOTAL
# ai_net_rx_bytes $NET_RX
# ai_net_tx_bytes $NET_TX
# EOF
# sudo mv /tmp/ai.prom.$$ "$OUT"
  • Run it periodically:
chmod +x ai-sys-sample.sh
# Every minute via cron
( crontab -l 2>/dev/null; echo '* * * * * /path/to/ai-sys-sample.sh >> /var/log/ai-sys.log 2>&1' ) | crontab -

Interpretation tip: rising la15, high %iowait (visible in iostat output), or sustained GPU utilization near 100% can all correlate with latency spikes.


2) Check your inference SLOs (availability and latency) at the edge

  • Why: Users experience availability and latency, not “service is up” booleans.

  • What: HTTP status code, total time to first byte/total time, and payload sanity.

Use this Bash health probe to log status and latency and exit non-zero on SLO violations (handy for alerting):

#!/usr/bin/env bash
# ai-http-check.sh
set -euo pipefail

URL="${URL:-http://localhost:8080/health}"
EXPECT_STATUS="${EXPECT_STATUS:-200}"
MAX_LATENCY_S="${MAX_LATENCY_S:-0.300}"   # 300ms default
TIMEOUT_S="${TIMEOUT_S:-2}"               # curl timeout

OUT=$(mktemp)
trap 'rm -f "$OUT"' EXIT

# -sS: silent but show errors; -m: max time; -o: body to file; -w: write HTTP code + time
read -r CODE TOTAL <<<"$(curl -sS -m "$TIMEOUT_S" -o "$OUT" -w "%{http_code} %{time_total}" "$URL")" || CODE=0

TS="$(date -Is)"
if [ "$CODE" != "$EXPECT_STATUS" ]; then
  echo "$TS url=$URL status=$CODE total_s=$TOTAL ERROR=bad_status"
  exit 2
fi

awk -v t="$TOTAL" -v max="$MAX_LATENCY_S" -v ts="$TS" -v url="$URL" '
BEGIN {
  if (t > max) {
    printf "%s url=%s status=OK total_s=%.3f ERROR=slow\n", ts, url, t;
    exit 1
  } else {
    printf "%s url=%s status=OK total_s=%.3f\n", ts, url, t;
    exit 0
  }
}'
  • Example runs:
URL=http://127.0.0.1:8000/v1/ping EXPECT_STATUS=200 MAX_LATENCY_S=0.200 ./ai-http-check.sh
  • Automate it:
( crontab -l 2>/dev/null; echo '* * * * * URL=https://api.your-ai.com/health MAX_LATENCY_S=0.250 /path/ai-http-check.sh >> /var/log/ai-http.log 2>&1' ) | crontab -

Pro tip: Keep per-endpoint SLOs: e.g., “P95 inference < 250ms for prompt<=1KB.” Your alerts should reflect those.


3) Add a basic data-drift sentinel (feature sanity checks)

  • Why: A healthy service with unhealthy inputs still produces bad outputs. Small, automated checks catch data skew early.

  • What: Track summary stats (min/mean/max) for key numeric inputs and compare to a known-good baseline.

This simple script compares live samples against a JSON baseline, logging a warning on deviations.

#!/usr/bin/env bash
# ai-drift-check.sh
# Expect JSON lines on stdin like: {"feature_a": 12.3, "feature_b": 7}
# Baseline file: {"feature_a":{"mean":10.0,"std":2.0},"feature_b":{"mean":6.5,"std":1.0}}

set -euo pipefail
BASELINE="${BASELINE:-/etc/ai/baseline.json}"
FEATURES="${FEATURES:-feature_a,feature_b}"
WINDOW="${WINDOW:-100}"  # number of samples to aggregate

if ! [ -f "$BASELINE" ]; then
  echo "Missing baseline at $BASELINE" >&2
  exit 1
fi

TMP=$(mktemp)
trap 'rm -f "$TMP"' EXIT

# Read up to WINDOW lines of JSON input
head -n "$WINDOW" > "$TMP"

IFS=',' read -ra FEATS <<< "$FEATURES"
TS="$(date -Is)"
for f in "${FEATS[@]}"; do
  # Compute mean using jq + awk
  COUNT=$(jq -r --arg f "$f" 'select(has($f)) | .[$f]' "$TMP" | wc -l | tr -d ' ')
  if [ "$COUNT" -eq 0 ]; then
    echo "$TS feature=$f WARNING=no_data"
    continue
  fi
  MEAN=$(jq -r --arg f "$f" 'select(has($f)) | .[$f]' "$TMP" | awk '{sum+=$1} END{if (NR>0) printf "%.6f", sum/NR; else print "nan"}')
  BASE_MEAN=$(jq -r --arg f "$f" '.[$f].mean' "$BASELINE")
  BASE_STD=$(jq -r --arg f "$f" '.[$f].std' "$BASELINE")

  # Z-score style check: flag if |mean - base_mean| > 3 * std (adjust as needed)
  DIFF=$(awk -v a="$MEAN" -v b="$BASE_MEAN" 'BEGIN{printf "%.6f", (a-b)>=0?(a-b):(b-a)}')
  THR=$(awk -v s="$BASE_STD" 'BEGIN{printf "%.6f", 3*s}')
  ALERT=$(awk -v d="$DIFF" -v t="$THR" 'BEGIN{print (d>t)?"YES":"NO"}')

  if [ "$ALERT" = "YES" ]; then
    echo "$TS feature=$f mean_now=$MEAN mean_base=$BASE_MEAN std_base=$BASE_STD ALERT=drift_suspected"
  else
    echo "$TS feature=$f mean_now=$MEAN mean_base=$BASE_MEAN std_base=$BASE_STD status=ok"
  fi
done

Usage example:

# Generate or update your baseline from a known-good window
# (example: compute mean/std offline and write /etc/ai/baseline.json)

# Then stream current traffic through the checker:
tail -n +1 -F /var/log/ai/inputs.jsonl | BASELINE=/etc/ai/baseline.json FEATURES=feature_a,feature_b WINDOW=200 ./ai-drift-check.sh >> /var/log/ai-drift.log

Start simple: a couple of high-signal features can catch many upstream issues.


4) Make every run reproducible (version, config, and evidence)

  • Why: When incidents hit, you need to answer “exactly which model/code/config was this request served by?” fast.

  • What: Embed model version, git SHA, and config hash into your process and logs.

Use a systemd unit that pins version metadata into environment variables and makes them queryable via a health endpoint or logs.

# /etc/systemd/system/ai-infer.service
[Unit]
Description=AI Inference Service
After=network.target

[Service]
User=ai
Group=ai
Environment=MODEL_VERSION=2026.07.10
Environment=GIT_SHA=abcdef1234567890
Environment=CONFIG_SHA=cfg-9988aabbcc
Environment=TZ=UTC
WorkingDirectory=/opt/ai
ExecStart=/opt/ai/bin/infer-server --port 8000
Restart=on-failure
RestartSec=2
StandardOutput=journal
StandardError=journal

[Install]
WantedBy=multi-user.target

Reload and start:

sudo systemctl daemon-reload
sudo systemctl enable --now ai-infer.service
journalctl -u ai-infer.service -f

Expose these environment variables via your /health or /version endpoint and echo them in startup logs. That alone cuts root-cause time dramatically.


5) Visualize and alert quickly (Prometheus + Grafana in containers)

  • Why: Logs are great; dashboards and alerts catch trends and page humans.

  • What: Use containers to get Prometheus, node_exporter, and Grafana up fast without distro-specific packages.

Start Docker or Podman:

  • Docker:
# apt:   sudo systemctl enable --now docker
# dnf:   sudo systemctl enable --now docker
# zypper:sudo systemctl enable --now docker
  • Or Podman (rootless example):
# apt/dnf/zypper all support:
podman --version

Run node_exporter (host metrics).

  • Docker:
docker run -d --name node_exporter --restart=unless-stopped \
  -p 9100:9100 \
  -v /proc:/host/proc:ro \
  -v /sys:/host/sys:ro \
  -v /:/rootfs:ro \
  quay.io/prometheus/node-exporter:latest \
  --path.procfs=/host/proc --path.sysfs=/host/sys --collector.filesystem.ignored-mount-points="^/(sys|proc|dev|host|etc)($|/)"
  • Podman:
podman run -d --name node_exporter --restart=always \
  -p 9100:9100 \
  -v /proc:/host/proc:ro \
  -v /sys:/host/sys:ro \
  -v /:/rootfs:ro \
  quay.io/prometheus/node-exporter:latest \
  --path.procfs=/host/proc --path.sysfs=/host/sys --collector.filesystem.ignored-mount-points="^/(sys|proc|dev|host|etc)($|/)"

Create a minimal Prometheus config:

mkdir -p ~/prometheus
cat > ~/prometheus/prometheus.yml <<'EOF'
global:
  scrape_interval: 15s

scrape_configs:
  - job_name: 'node'
    static_configs:
      - targets: ['host.docker.internal:9100', 'localhost:9100']
  - job_name: 'ai-http-check'
    static_configs:
      - targets: ['localhost:9101']  # example if you later expose custom metrics
EOF

Run Prometheus:

  • Docker:
docker run -d --name prometheus --restart=unless-stopped \
  -p 9090:9090 \
  -v ~/prometheus:/etc/prometheus \
  prom/prometheus:latest \
  --config.file=/etc/prometheus/prometheus.yml
  • Podman:
podman run -d --name prometheus --restart=always \
  -p 9090:9090 \
  -v ~/prometheus:/etc/prometheus \
  docker.io/prom/prometheus:latest \
  --config.file=/etc/prometheus/prometheus.yml

Run Grafana:

  • Docker:
docker run -d --name grafana --restart=unless-stopped -p 3000:3000 grafana/grafana
  • Podman:
podman run -d --name grafana --restart=always -p 3000:3000 docker.io/grafana/grafana

Then:

  • Open Grafana on http://localhost:3000 (default admin/admin).

  • Add Prometheus at http://localhost:9090 as a data source.

  • Import a Node Exporter dashboard (search gallery) and add your custom panels for:

    • ai_sys: load, memory, I/O wait
    • GPU utilization (via textfile exporter if you enable it)
    • Inference latency from logs (exported through a small sidecar or custom metrics endpoint)

Optional: simple alert with Prometheus rules (save as ~/prometheus/alerts.yml and reference via --rule.files):

groups:

- name: ai-latency
  rules:
  - alert: InferenceLatencyHigh
    expr: histogram_quantile(0.95, sum(rate(http_request_duration_seconds_bucket{service="ai"}[5m])) by (le)) > 0.25
    for: 10m
    labels:
      severity: page
    annotations:
      summary: "High p95 inference latency"
      description: "p95 latency > 250ms for 10m"

Hook this to email/Slack/PagerDuty via Alertmanager when you’re ready.


A compact, real-world AI monitoring checklist

Use this before and after every deploy:

  • Capacity and hardware

    • CPU load and %iowait are within historical bands
    • Memory headroom > 20%, no swapping under load
    • GPU util < 85% sustained unless intended; GPU mem headroom > 10%
    • Disk I/O latency stable; network egress not throttled
  • Service SLOs

    • Health endpoint status=200
    • p95 latency within target for realistic payload sizes
    • Error rate and timeouts near zero
  • Data sanity

    • Key input features within baseline ranges
    • Payload shapes/sizes within expected bounds
    • Spike detection on traffic volume
  • Reproducibility

    • Model version, git SHA, and config hash embedded and visible
    • Artifact checksums recorded
    • Change log links from dashboards to releases
  • Observability plumbing

    • Node and app metrics scraping confirmed in Prometheus
    • Dashboards pinned to SLOs
    • Alerts tested with known-bad scenarios

Conclusion and next step

AI systems don’t fail in one place—they fade across systems, data, and code. A Bash-first checklist, coupled with a few lightweight tools, gives you fast visibility and a reliable early-warning system.

Your next step:

  • Put ai-sys-sample.sh and ai-http-check.sh on one production host today.

  • Stand up Prometheus and Grafana with Docker/Podman.

  • In a week, capture baselines and wire one alert.

Small steps compound. Start monitoring what matters and make silent failures loud—before your users find them.