- Posted on
- • Artificial Intelligence
Artificial Intelligence Server Maintenance with Bash
- Author
-
-
- User
- linuxbash
- Posts by this author
- Posts by this author
-
Artificial Intelligence Server Maintenance with Bash: A Practical Playbook
AI servers work hard: they crunch large models, stream predictions, and shuffle terabytes of data. When they fail at 3 a.m., you need visibility and fast recovery. You don’t need another heavyweight agent to get there—you can do a lot with Bash, systemd, and a few standard Linux tools.
This guide shows you how to keep AI servers healthy using Bash:
Why Bash is a great fit
What to install
3–5 actionable scripts and timers you can drop in today
How to schedule and alert on issues
If you run GPU inference or training nodes, these patterns will save you time and outages.
Why Bash for AI server maintenance?
It’s everywhere: no new runtime or agent. Bash + coreutils = works on almost every Linux box.
It’s composable: glue together
nvidia-smi,smartctl,nvme,df,systemctl, andcurlquickly.It’s debuggable: read the script, test one line at a time, no mystery.
It’s automatable: run from systemd timers, cron, or CI without extra services.
Prerequisites: tools you’ll use
We’ll rely on common CLI tools. Install them with your package manager.
Packages:
lm-sensors (CPU temps and fan speeds)
smartmontools (disk SMART health)
nvme-cli (NVMe health and wear)
curl (alerts/webhooks)
rsync (backups and syncs)
tmux (optional, for interactive maintenance)
Ubuntu/Debian (apt):
sudo apt update
sudo apt install -y lm-sensors smartmontools nvme-cli curl rsync tmux
sudo sensors-detect --auto
Fedora/RHEL/CentOS Stream (dnf):
sudo dnf install -y lm_sensors smartmontools nvme-cli curl rsync tmux
sudo sensors-detect --auto
openSUSE Leap/Tumbleweed (zypper):
sudo zypper refresh
sudo zypper install -y lm_sensors smartmontools nvme-cli curl rsync tmux
sudo sensors-detect --auto
Notes:
NVIDIA GPU info comes from
nvidia-smi, which ships with the NVIDIA driver. If you use AMD, similar checks can be done withrocm-smi—swap the GPU queries accordingly.These scripts assume systemd. If you don’t use systemd, adapt the timers to cron.
1) A quick, actionable health check for AI workloads
This script checks CPU/memory pressure, root disk usage, GPU temperature/utilization (if NVIDIA GPUs are present), and storage health. It logs to syslog and optionally posts to a Slack/Teams/Discord webhook if thresholds are exceeded.
Install it:
sudo tee /usr/local/bin/ai-health.sh > /dev/null <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
# Thresholds (tune to your environment)
DISK_PCT_WARN=90 # % used on /
MEM_PCT_WARN=95 # % of RAM used
GPU_TEMP_WARN=85 # Celsius
NVME_WARN_ON_CRIT=1 # Non-zero 'critical_warning' is bad
HOST="$(hostname -f 2>/dev/null || hostname)"
TS="$(date -u +'%Y-%m-%dT%H:%M:%SZ')"
ALERTS=()
LOG_PREFIX="[ai-health] $TS $HOST"
has() { command -v "$1" >/dev/null 2>&1; }
# Memory pressure
read -r MEM_TOTAL_KB MEM_AVAIL_KB < <(awk '/MemTotal:/ {t=$2} /MemAvailable:/ {a=$2} END {print t, a}' /proc/meminfo)
if [[ -n "${MEM_TOTAL_KB:-}" && -n "${MEM_AVAIL_KB:-}" && "$MEM_TOTAL_KB" -gt 0 ]]; then
MEM_USED_PCT=$(( (100 * (MEM_TOTAL_KB - MEM_AVAIL_KB)) / MEM_TOTAL_KB ))
[[ "$MEM_USED_PCT" -ge "$MEM_PCT_WARN" ]] && ALERTS+=("RAM pressure high: ${MEM_USED_PCT}% used")
fi
# Root disk usage
ROOT_PCT=$(df -P / | awk 'NR==2 {gsub(/%/,"",$5); print $5}')
[[ -n "$ROOT_PCT" && "$ROOT_PCT" -ge "$DISK_PCT_WARN" ]] && ALERTS+=("Root filesystem nearly full: ${ROOT_PCT}% used")
# GPU checks (NVIDIA)
if has nvidia-smi; then
mapfile -t GPU_LINES < <(nvidia-smi --query-gpu=index,name,temperature.gpu,utilization.gpu,memory.used,memory.total --format=csv,noheader,nounits 2>/dev/null || true)
for line in "${GPU_LINES[@]}"; do
IFS=',' read -r IDX NAME TEMP UTIL MEM_USED MEM_TOTAL <<<"$(echo "$line" | sed 's/, /,/g')"
if [[ -n "${TEMP:-}" && "$TEMP" -ge "$GPU_TEMP_WARN" ]]; then
ALERTS+=("GPU$IDX ($NAME) hot: ${TEMP}C")
fi
done
fi
# NVMe health (if any)
if has nvme; then
while read -r DEV TYPE; do
[[ "$TYPE" != "disk" ]] && continue
[[ "$DEV" != nvme* ]] && continue
CW=$(nvme smart-log "/dev/$DEV" 2>/dev/null | awk '/critical_warning/ {print $3}' || echo "")
if [[ -n "$CW" && "$CW" != "0x00" && "$NVME_WARN_ON_CRIT" -eq 1 ]]; then
ALERTS+=("NVMe /dev/$DEV critical_warning: $CW")
fi
done < <(lsblk -ndo NAME,TYPE)
fi
# SATA/other SMART quick health
if has smartctl; then
while read -r DEV TYPE; do
[[ "$TYPE" != "disk" ]] && continue
[[ "$DEV" == nvme* ]] && continue
H=$(smartctl -H "/dev/$DEV" 2>/dev/null | awk -F: '/SMART overall-health/ {gsub(/^[ \t]+/,"",$2); print $2}')
[[ "$H" == "PASSED" || -z "$H" ]] || ALERTS+=("SMART failing: /dev/$DEV ($H)")
done < <(lsblk -ndo NAME,TYPE)
fi
# Log outcome
if (( ${#ALERTS[@]} )); then
MSG="$LOG_PREFIX ALERT: $(printf '%s; ' "${ALERTS[@]}")"
echo "$MSG"
logger -t ai-health "$MSG"
# Optional webhook alert (Slack, Teams, Discord, etc.)
if [[ -n "${AI_HEALTH_WEBHOOK:-}" ]] && has curl; then
PAYLOAD=$(printf '{"text":"%s"}' "$(echo "$MSG" | sed 's/"/\"/g')")
curl -fsS -m 5 -H 'Content-Type: application/json' -d "$PAYLOAD" "$AI_HEALTH_WEBHOOK" >/dev/null || true
fi
exit 1
else
MSG="$LOG_PREFIX OK"
echo "$MSG"
logger -t ai-health "$MSG"
fi
EOF
sudo chmod +x /usr/local/bin/ai-health.sh
Usage:
Optional: export
AI_HEALTH_WEBHOOK(Slack/Teams/Discord incoming webhook URL) in the systemd service environment (shown next) or in/etc/environment.Run ad hoc:
sudo /usr/local/bin/ai-health.sh
2) Schedule the health check with systemd timers
Create a oneshot service and a 5-minute timer.
sudo tee /etc/systemd/system/ai-health.service > /dev/null <<'EOF'
[Unit]
Description=AI Server Health Check
[Service]
Type=oneshot
Environment=AI_HEALTH_WEBHOOK=
ExecStart=/usr/local/bin/ai-health.sh
EOF
sudo tee /etc/systemd/system/ai-health.timer > /dev/null <<'EOF'
[Unit]
Description=Run AI Server Health Check every 5 minutes
[Timer]
OnBootSec=2min
OnUnitActiveSec=5min
AccuracySec=30s
Persistent=true
[Install]
WantedBy=timers.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable --now ai-health.timer
sudo systemctl status ai-health.timer --no-pager
Tip:
Set
Environment=AI_HEALTH_WEBHOOK=https://...inai-health.serviceto enable alerts.Check logs:
journalctl -u ai-health.service -n 50 --no-pager
3) GPU-aware watchdog for your inference service
Some inference servers silently stall: the process is “running” but GPUs are idle for hours. This watchdog restarts a systemd unit if GPUs stay idle for too long, or if the unit stops.
Install it:
sudo tee /usr/local/bin/gpu-watchdog.sh > /dev/null <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
# Name of your service (edit this!)
SERVICE="ai-inference.service"
# Idle thresholds
IDLE_UTIL_PCT=5 # Consider GPU idle below this utilization
RESTART_AFTER_N_RUNS=6 # e.g., with a 10m timer, 6 runs = ~1 hour of idleness
STATE_DIR="/run/gpu-watchdog"
STATE_FILE="$STATE_DIR/idle.count"
mkdir -p "$STATE_DIR"
count=0
[[ -f "$STATE_FILE" ]] && read -r count < "$STATE_FILE" || true
is_active() { systemctl is-active --quiet "$SERVICE"; }
avg_gpu_util() {
if command -v nvidia-smi >/dev/null 2>&1; then
nvidia-smi --query-gpu=utilization.gpu --format=csv,noheader,nounits 2>/dev/null \
| awk '{sum+=$1; n+=1} END {if(n>0) print sum/n; else print -1}'
else
echo -1
fi
}
# Ensure service is running
if ! is_active; then
logger -t gpu-watchdog "Service $SERVICE is not active; starting"
systemctl start "$SERVICE" || true
echo 0 > "$STATE_FILE"
exit 0
fi
UTIL=$(avg_gpu_util)
if [[ "$UTIL" -ge 0 && "${UTIL%.*}" -lt "$IDLE_UTIL_PCT" ]]; then
count=$((count + 1))
else
count=0
fi
echo "$count" > "$STATE_FILE"
if [[ "$count" -ge "$RESTART_AFTER_N_RUNS" ]]; then
logger -t gpu-watchdog "GPU idle for prolonged period (avg util ~${UTIL}%). Restarting $SERVICE"
systemctl restart "$SERVICE" || true
echo 0 > "$STATE_FILE"
fi
EOF
sudo chmod +x /usr/local/bin/gpu-watchdog.sh
Timer and service:
sudo tee /etc/systemd/system/gpu-watchdog.service > /dev/null <<'EOF'
[Unit]
Description=GPU Watchdog for AI Inference Service
[Service]
Type=oneshot
ExecStart=/usr/local/bin/gpu-watchdog.sh
EOF
sudo tee /etc/systemd/system/gpu-watchdog.timer > /dev/null <<'EOF'
[Unit]
Description=Run GPU Watchdog every 10 minutes
[Timer]
OnBootSec=5min
OnUnitActiveSec=10min
AccuracySec=1min
Persistent=true
[Install]
WantedBy=timers.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable --now gpu-watchdog.timer
Customize:
Set
SERVICE=in the script to your unit (e.g.,vllm.service,textgen.service,torchserve.service).For AMD, replace the utilization query with
rocm-smi --showuse.
4) Safe, incremental backups of models and checkpoints with rsync
Models and checkpoints change frequently. Use rsync with hard-linked snapshots for space-efficient, point-in-time copies. This works best when backing up to a local path or an NFS mount. For remote SSH targets, you can still mirror with rsync (snapshots require extra remote-side logic).
Create excludes and script:
sudo tee /etc/ai-backup-excludes.txt > /dev/null <<'EOF'
# Examples to skip temp files
*.tmp
cache/
wandb/
node_modules/
__pycache__/
EOF
sudo tee /usr/local/bin/ai-backup.sh > /dev/null <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
# What to back up
SRC_DIRS=(
/srv/models
/srv/checkpoints
)
# Destination must be a local path (e.g., mounted NFS) for snapshots
DEST="/var/backups/ai"
STAMP="$(date +%F)" # e.g., 2026-07-07
mkdir -p "$DEST"
SNAP_DIR="$DEST/$STAMP"
CUR_DIR="$DEST/current"
# Create a snapshot by cloning the last state via hard links, then update
if [[ -d "$CUR_DIR" ]]; then
cp -al "$CUR_DIR" "$SNAP_DIR"
else
mkdir -p "$SNAP_DIR"
fi
# Sync latest into 'current'
mkdir -p "$CUR_DIR"
rsync -aHAX --delete --delete-excluded --exclude-from=/etc/ai-backup-excludes.txt \
"${SRC_DIRS[@]}" "$CUR_DIR/"
# Retention: keep 14 days of snapshots
find "$DEST" -maxdepth 1 -type d -name '20[0-9][0-9]-*' -mtime +14 -print -exec rm -rf {} +
logger -t ai-backup "Backup complete to $DEST ($STAMP)"
EOF
sudo chmod +x /usr/local/bin/ai-backup.sh
Timer and service (daily at 03:15):
sudo tee /etc/systemd/system/ai-backup.service > /dev/null <<'EOF'
[Unit]
Description=AI models/checkpoints backup
[Service]
Type=oneshot
ExecStart=/usr/local/bin/ai-backup.sh
EOF
sudo tee /etc/systemd/system/ai-backup.timer > /dev/null <<'EOF'
[Unit]
Description=Daily AI backup at 03:15
[Timer]
OnCalendar=*-*-* 03:15:00
Persistent=true
[Install]
WantedBy=timers.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable --now ai-backup.timer
Notes:
- If you must push to a remote SSH server, a simple mirror works:
rsync -a --delete /srv/models/ user@backup:/backups/$HOST/models/Snapshots on the remote side require server-side commands or a backup system.
5) One-liners you’ll actually use
- See which processes hold the most GPU memory:
nvidia-smi --query-compute-apps=pid,process_name,used_memory --format=csv
- Kill a runaway GPU process safely (confirm PID first):
sudo kill -15 <PID>; sleep 5; sudo kill -9 <PID> || true
- Verify CPU temps and fans:
sensors
- Quick view of NVMe wear and warnings:
nvme smart-log /dev/nvme0
- Top disk consumers under a directory:
sudo du -xh /var | sort -h | tail -40
Conclusion and next steps
With a handful of Bash scripts and systemd timers, you can:
Detect pressure early (RAM, disk, GPU temps)
Alert and self-heal stalled inference services
Protect models and checkpoints with fast, incremental backups
Your next steps:
1) Install the prerequisite tools (apt/dnf/zypper commands above).
2) Drop in ai-health.sh and enable the timer.
3) Add a webhook to get alerts where you work.
4) Configure the GPU watchdog for your inference unit.
5) Turn on daily backups and verify restore paths.
Start with health checks, then add the watchdog and backups. Small Bash habits lead to big reliability gains.