- Posted on
- • Artificial Intelligence
Artificial Intelligence Uptime Reporting
- Author
-
-
- User
- linuxbash
- Posts by this author
- Posts by this author
-
Artificial Intelligence Uptime Reporting with Bash: From Pings to Production-Grade Proof
If your AI service is down, it doesn’t matter how accurate your model is—your users will churn. The catch: AI services are uniquely fragile. They sit on GPUs, containers, and networked model backends that can be flaky under load, and “port is open” is not the same as “inference works.” In this guide, you’ll build a simple, Linux-native uptime reporting pipeline in Bash that measures availability, latency, and GPU context—then turns that into daily, auditable reports you can ship to your team.
You’ll get:
A minimal, dependency-light checker that exercises your AI endpoint the way your users do
A one-minute systemd timer or cron job to collect data
A reporting script to compute 24h/7d uptime and latency percentiles
Optional Prometheus textfile metrics output (works with node_exporter if you already run it)
Why AI uptime deserves special treatment
Health != Inference: A 200 OK on “/health” might not mean the model can generate a token. You need an application-level probe.
GPUs are finite: Memory pressure and utilization spikes can cause timeouts and OOMs without crashing the process.
Cold starts and scaling: Autoscaling, model swaps, and container restarts introduce transient errors that pings miss.
SLAs matter: If you can’t calculate “what percent of requests succeeded” across a defined window, you don’t have an SLO—you have a hope.
What you’ll build
A Bash script that hits your AI service endpoint, records success/failure, HTTP status, latency, and (if available) GPU utilization and memory.
Log storage in CSV under /var/log/ai-uptime/.
Optional Prometheus textfile metrics output for easy dashboards.
A systemd timer (or cron) to run it every minute.
A report generator to compute uptime and latency stats over a window.
Install prerequisites
We’ll use curl, jq, and bc. Install with your package manager:
Debian/Ubuntu (apt):
sudo apt update sudo apt install -y curl jq bc cronFedora/RHEL/CentOS Stream (dnf):
sudo dnf install -y curl jq bc cronieopenSUSE (zypper):
sudo zypper refresh sudo zypper install -y curl jq bc cron
Notes:
cron/cronie is only needed if you prefer cron over systemd timers.
If you run node_exporter with the textfile collector, ensure its directory (often /var/lib/node_exporter/textfile_collector) exists and is writable by your uptime user.
Create a dedicated user and directories
sudo useradd --system --no-create-home --shell /usr/sbin/nologin ai-uptime
sudo install -d -o ai-uptime -g ai-uptime -m 750 /opt/ai-uptime
sudo install -d -o ai-uptime -g ai-uptime -m 750 /var/log/ai-uptime
If you plan to output Prometheus textfiles and already run node_exporter with the textfile collector, also:
# Example path; adjust to your environment
sudo install -d -o ai-uptime -g ai-uptime -m 750 /var/lib/node_exporter/textfile_collector
Step 1: The AI health check script
Save this as /opt/ai-uptime/check_ai_health.sh and make it executable.
#!/usr/bin/env bash
set -euo pipefail
# Config via env or defaults
SERVICE_NAME="${SERVICE_NAME:-model-a}"
URL="${URL:-http://127.0.0.1:8000/health}"
METHOD="${METHOD:-GET}" # GET or POST
TIMEOUT="${TIMEOUT:-8}" # seconds
HEADERS="${HEADERS:-Content-Type: application/json}"
PAYLOAD="${PAYLOAD:-}" # e.g. {"model":"local","prompt":"ping","max_tokens":1}
LOG_DIR="${LOG_DIR:-/var/log/ai-uptime}"
CSV="${CSV:-$LOG_DIR/ai-uptime.csv}"
# Optional Prometheus textfile output (if this dir exists)
PROM_DIR="${PROM_DIR:-/var/lib/node_exporter/textfile_collector}"
# Optional: also check a systemd unit or docker container
SYSTEMD_SERVICE="${SYSTEMD_SERVICE:-}" # e.g., your-ai.service
DOCKER_CONTAINER="${DOCKER_CONTAINER:-}" # e.g., ai-server-1
TS="$(date +%s)"
TMP="$(mktemp)"
cleanup() { rm -f "$TMP"; }
trap cleanup EXIT
# Run request and capture status code + latency
HTTP_CODE=""
LATENCY=""
CURL_ARGS=( -sS -m "$TIMEOUT" -o "$TMP" -w '%{http_code} %{time_total}' -X "$METHOD" )
if [[ -n "$HEADERS" ]]; then
IFS=$'\n' read -r -d '' -a HDR_ARR < <(printf '%s\0' $HEADERS)
for h in "${HDR_ARR[@]}"; do CURL_ARGS+=( -H "$h" ); done
fi
if [[ -n "$PAYLOAD" ]]; then
CURL_ARGS+=( --data "$PAYLOAD" )
fi
CURL_ARGS+=( "$URL" )
if OUT="$("curl" "${CURL_ARGS[@]}" 2>/dev/null || true)"; then
HTTP_CODE="$(awk '{print $1}' <<<"$OUT")"
LATENCY="$(awk '{print $2}' <<<"$OUT")"
else
HTTP_CODE="000"
LATENCY=""
fi
# Decide "UP" based on HTTP 2xx and optional application-level signal
UP=0
if [[ "$HTTP_CODE" =~ ^2[0-9][0-9]$ ]]; then
UP=1
# If JSON body includes "error" or explicit failure, mark down
if command -v jq >/dev/null 2>&1; then
if jq -e '(.error? // .status? // .ok?) as $s | (type=="object")' "$TMP" >/dev/null 2>&1; then
# Prefer explicit fields if present
if jq -e 'has("error") and (.error != null and .error != false)' "$TMP" >/dev/null 2>&1; then
UP=0
elif jq -e 'has("status") and (.status|tostring|ascii_downcase) != "ok"' "$TMP" >/dev/null 2>&1; then
UP=0
elif jq -e 'has("ok") and (.ok|tostring|ascii_downcase)=="false"' "$TMP" >/dev/null 2>&1; then
UP=0
fi
fi
fi
fi
# Optional: capture service/container state
SVC_ACTIVE=""
if [[ -n "$SYSTEMD_SERVICE" ]]; then
if systemctl is-active --quiet "$SYSTEMD_SERVICE"; then SVC_ACTIVE="active"; else SVC_ACTIVE="inactive"; fi
fi
CTR_HEALTH=""
if [[ -n "$DOCKER_CONTAINER" ]] && command -v docker >/dev/null 2>&1; then
CTR_HEALTH="$(docker inspect --format='{{.State.Health.Status}}' "$DOCKER_CONTAINER" 2>/dev/null || true)"
fi
# Optional: capture GPU stats if nvidia-smi exists
GPU_UTIL=""
GPU_MEM=""
if command -v nvidia-smi >/dev/null 2>&1; then
IFS=',' read -r GPU_UTIL GPU_MEM < <(nvidia-smi --query-gpu=utilization.gpu,memory.used --format=csv,noheader,nounits 2>/dev/null | head -n1 | tr -d ' %' | tr ' ' ',')
fi
# Prepare CSV
mkdir -p "$LOG_DIR"
if [[ ! -f "$CSV" ]]; then
echo "timestamp,service,up,http_code,latency_seconds,gpu_util_percent,gpu_mem_mb,systemd_active,docker_health" > "$CSV"
fi
LATENCY="${LATENCY:-NaN}"
HTTP_CODE="${HTTP_CODE:-000}"
echo "$TS,$SERVICE_NAME,$UP,$HTTP_CODE,$LATENCY,${GPU_UTIL:-},${GPU_MEM:-},${SVC_ACTIVE:-},${CTR_HEALTH:-}" >> "$CSV"
# Optional: Prometheus textfile
if [[ -d "$PROM_DIR" ]]; then
SAFE_SERVICE="$(echo "$SERVICE_NAME" | tr -c 'a-zA-Z0-9:_-' '_')"
OUTFILE="$PROM_DIR/ai_uptime_${SAFE_SERVICE}.prom"
{
echo "ai_service_up{service=\"$SERVICE_NAME\"} $UP"
echo "ai_service_latency_seconds{service=\"$SERVICE_NAME\"} ${LATENCY/NaN/0}"
echo "ai_service_http_code{service=\"$SERVICE_NAME\",code=\"$HTTP_CODE\"} 1"
if [[ -n "$GPU_UTIL" ]]; then
echo "ai_service_gpu_utilization_percent{service=\"$SERVICE_NAME\"} $GPU_UTIL"
fi
if [[ -n "$GPU_MEM" ]]; then
echo "ai_service_gpu_memory_used_megabytes{service=\"$SERVICE_NAME\"} $GPU_MEM"
fi
} > "$OUTFILE.$$"
mv "$OUTFILE.$$" "$OUTFILE"
fi
# Log a human message
logger -t ai-uptime "service=$SERVICE_NAME up=$UP code=$HTTP_CODE latency=$LATENCY gpu_util=${GPU_UTIL:-} gpu_mem=${GPU_MEM:-} url=$URL"
Make it executable and owned by our user:
sudo chown ai-uptime:ai-uptime /opt/ai-uptime/check_ai_health.sh
sudo chmod 750 /opt/ai-uptime/check_ai_health.sh
Quick test:
sudo -u ai-uptime SERVICE_NAME=model-a URL=http://127.0.0.1:8000/health /opt/ai-uptime/check_ai_health.sh
tail -n1 /var/log/ai-uptime/ai-uptime.csv
Tip: To probe real inference, set:
METHOD=POST
HEADERS=$'Content-Type: application/json\nAuthorization: Bearer YOUR_TOKEN' # if needed
PAYLOAD='{"model":"local","prompt":"ping","max_tokens":1}'
Step 2: Schedule the checker
Systemd timer (recommended for minute-precision and logging):
Create /etc/systemd/system/ai-uptime.service
[Unit]
Description=AI Uptime Check
[Service]
Type=oneshot
User=ai-uptime
Group=ai-uptime
Environment=SERVICE_NAME=model-a
Environment=URL=http://127.0.0.1:8000/health
# Example for inference POST:
# Environment=METHOD=POST
# Environment=HEADERS=Content-Type: application/json
# Environment=PAYLOAD={"model":"local","prompt":"ping","max_tokens":1}
ExecStart=/opt/ai-uptime/check_ai_health.sh
Create /etc/systemd/system/ai-uptime.timer
[Unit]
Description=Run AI Uptime Check every minute
[Timer]
OnBootSec=30s
OnUnitActiveSec=60s
AccuracySec=1s
Unit=ai-uptime.service
[Install]
WantedBy=timers.target
Enable it:
sudo systemctl daemon-reload
sudo systemctl enable --now ai-uptime.timer
sudo systemctl list-timers | grep ai-uptime
Prefer cron? Add this line (runs every minute):
# Debian/Ubuntu: ensure "cron" service is running; Fedora/RHEL: "crond" from cronie
* * * * * SERVICE_NAME=model-a URL=http://127.0.0.1:8000/health /opt/ai-uptime/check_ai_health.sh >/dev/null 2>&1
Service per model: Duplicate the .service file with different Environment= blocks or create multiple unit/timer pairs (e.g., ai-uptime@model-a.service, ai-uptime@model-b.service) with different URLs.
Step 3: Reporting script (24h/7d uptime and latency)
Save as /opt/ai-uptime/report_ai_uptime.sh.
#!/usr/bin/env bash
set -euo pipefail
CSV="${CSV:-/var/log/ai-uptime/ai-uptime.csv}"
SERVICE="${SERVICE:-}" # filter by service name; empty = all
WINDOW="${WINDOW:-86400}" # seconds; 86400=24h, 604800=7d
now="$(date +%s)"
start="$(( now - WINDOW ))"
if [[ ! -f "$CSV" ]]; then
echo "No data at $CSV" >&2
exit 1
fi
# Filter rows in window and (optionally) service
awk -F, -v s="$SERVICE" -v start="$start" 'NR>1 && $1>=start && (s=="" || $2==s)' "$CSV" > /tmp/ai_report.$$ || true
rows=$(wc -l < /tmp/ai_report.$$ | tr -d ' ')
if (( rows == 0 )); then
echo "No samples in window."
rm -f /tmp/ai_report.$$
exit 0
fi
# Compute uptime
up=$(awk -F, '{sum+=$3} END{print sum+0}' /tmp/ai_report.$$)
pct=$(awk -v u="$up" -v r="$rows" 'BEGIN{ if (r==0) {print 0} else {printf "%.4f", (u/r*100)} }')
# Latency stats
lat_file="/tmp/ai_report_lat.$$"
awk -F, '$5 ~ /^[0-9.]+$/ {print $5}' /tmp/ai_report.$$ | sort -n > "$lat_file"
count=$(wc -l < "$lat_file" | tr -d ' ')
p50_idx=$(( (count*50 + 99)/100 ))
p95_idx=$(( (count*95 + 99)/100 ))
avg=$(awk '{sum+=$1} END{ if (NR==0) print 0; else printf "%.6f", sum/NR }' "$lat_file")
p50=$(awk -v i="$p50_idx" 'NR==i{print $1}' "$lat_file")
p95=$(awk -v i="$p95_idx" 'NR==i{print $1}' "$lat_file")
# Longest outage (consecutive down samples)
longest=0
streak=0
while IFS=, read -r ts svc upflag _; do
if [[ -n "$SERVICE" && "$svc" != "$SERVICE" ]]; then continue; fi
if [[ "$upflag" == "0" ]]; then
((streak++))
((streak > longest)) && longest=$streak
else
streak=0
fi
done < <(sort -t, -k1,1n /tmp/ai_report.$$)
# Print report
echo "AI Uptime Report"
echo "Window: last $WINDOW seconds (~$((WINDOW/3600))h)"
if [[ -n "$SERVICE" ]]; then echo "Service: $SERVICE"; fi
echo "Samples: $rows"
echo "Uptime: $pct % (Up=$up, Down=$((rows-up)))"
echo "Latency: avg=${avg}s, p50=${p50:-N/A}s, p95=${p95:-N/A}s"
if (( longest > 0 )); then
echo "Longest consecutive outage: $longest samples (~$((longest)) min if 1-min schedule)"
else
echo "No outages detected."
fi
rm -f /tmp/ai_report.$$ "$lat_file"
Make it executable:
sudo chown ai-uptime:ai-uptime /opt/ai-uptime/report_ai_uptime.sh
sudo chmod 750 /opt/ai-uptime/report_ai_uptime.sh
Example usage:
# 24h report for model-a
sudo -u ai-uptime SERVICE=model-a /opt/ai-uptime/report_ai_uptime.sh
# 7d all-services rollup
sudo -u ai-uptime WINDOW=604800 /opt/ai-uptime/report_ai_uptime.sh
Optional daily report via systemd:
Create /etc/systemd/system/ai-uptime-report.service
[Unit] Description=AI Uptime Daily Report [Service] Type=oneshot User=ai-uptime Group=ai-uptime Environment=WINDOW=86400 ExecStart=/opt/ai-uptime/report_ai_uptime.shCreate /etc/systemd/system/ai-uptime-report.timer
[Unit] Description=Run AI Uptime Daily Report at 08:00 [Timer] OnCalendar=*-*-* 08:00:00 Persistent=true [Install] WantedBy=timers.targetEnable it:
sudo systemctl daemon-reload sudo systemctl enable --now ai-uptime-report.timer
Step 4: Real-world target examples
Simple health endpoint:
URL=http://127.0.0.1:8000/health METHOD=GETIf the body returns {"status":"ok"}, the script will treat it as up.
OpenAI-compatible inference POST (local or remote):
URL=http://127.0.0.1:8000/v1/completions METHOD=POST HEADERS=$'Content-Type: application/json\nAuthorization: Bearer YOUR_TOKEN' PAYLOAD='{"model":"local","prompt":"ping","max_tokens":1}'Containerized deployment checks (optional):
SYSTEMD_SERVICE=your-ai.service DOCKER_CONTAINER=ai-server-1These add context but don’t affect UP unless you act on them downstream.
GPU-enabled servers: The script automatically captures GPU utilization and memory if nvidia-smi exists. No config needed.
Tip: Monitor multiple services by creating multiple systemd service/timer pairs with different Environment= blocks, or call the script in a loop from a wrapper.
Step 5: Alerts and integrations (minimal but effective)
Start simple:
Page on consecutive failures:
# 5 consecutive downs within a minute cadence tail -n5 /var/log/ai-uptime/ai-uptime.csv | awk -F, '$3==0' | wc -lIf result is 5, send a webhook:
curl -X POST -H 'Content-Type: application/json' \ -d '{"text":"ALERT: model-a down 5x in a row"}' \ https://hooks.slack.com/services/XXX/YYY/ZZZGrafana-friendly metrics: If you already run node_exporter with a textfile collector, the script writes ai_service_up and latency metrics into .prom files. Point Prometheus at node_exporter and build dashboards or alerts.
3–5 actionable practices to keep you honest
1) Probe what users do, not just ports
Always run a tiny inference (e.g., prompt “ping”, max_tokens=1). A 200 on /health can hide broken model backends.
2) Track SLI inputs you can explain
Record up/down, HTTP code, and latency at 1-minute resolution. Add GPU memory/utilization to correlate slowdowns with load.
3) Separate network and app errors
000 status (curl failure) vs 5xx (server error) vs 2xx with an “error” field tell very different stories when you negotiate SLAs.
4) Automate a daily SLA report
A rolling 24h and 7d report keeps your team honest. Post it in a Slack channel every morning before stand-up.
5) Keep dependencies boring
Bash + curl + jq + systemd is universally available on Linux servers and easy to audit.
Troubleshooting tips
Empty CSV: Your timer/cron may not be running. Check:
systemctl status ai-uptime.timer journalctl -u ai-uptime.service --since "10 min ago"Non-JSON endpoints: That’s fine. The script only uses jq if present and JSON-like; otherwise it falls back to HTTP status.
SSL/TLS issues: Add curl options in HEADERS or extend CURL_ARGS (e.g., --cacert, --resolve).
High-latency spikes only: Check GPU mem/util and container health columns in the CSV to correlate.
Conclusion and next steps
You now have a production-ready, Bash-first AI uptime reporter that does more than ping—it proves that inference works, quantifies reliability, and gives you metrics to improve. Next steps:
Put this behind a Grafana dashboard if you run Prometheus/node_exporter.
Add a webhook alert on streaks of failures or p95 latency breaches.
Extend the checker to hit multiple endpoints per model (health, inference, embeddings) and tag them separately.
If you found this useful, instrument one service today, share the 24h report with your team, and iterate. Reliability is a feature—ship it.