Skip to content

Instantly share code, notes, and snippets.

@dejurin
Created September 25, 2025 18:47
Show Gist options
  • Save dejurin/8fa49aebd11e254f29ad29bcc525ba87 to your computer and use it in GitHub Desktop.
Save dejurin/8fa49aebd11e254f29ad29bcc525ba87 to your computer and use it in GitHub Desktop.
Small, dependency-light Linux server snapshot tool for quick health checks. Prints host info, time, load, CPU/memory/disk, basic I/O & network stats, TCP summary, top processes, and (optionally) Docker usage. Safe to run as non-root; gracefully degrades when tools are missing.
#!/usr/bin/env bash
# server-snapshot.sh
# Purpose: Print a concise, human-readable snapshot of a Linux server state.
# Safe defaults: exits on errors/undefined vars; degrades when optional tools are missing.
# Intended usage: run manually or via cron; pipe to file for later inspection.
set -euo pipefail
# Ensure predictable output regardless of system locale
export LC_ALL=C
export LANG=C
export PATH="/usr/sbin:/usr/bin:/sbin:/bin:$PATH"
print_section() {
# Prints a section header with blank line separation
printf "\n## %s\n" "$1"
}
echo "## Host & time"
if command -v hostnamectl >/dev/null 2>&1; then
# First lines include static hostname, icon name, chassis, machine ID (on most distros)
hostnamectl 2>/dev/null | sed -n '1,6p' || true
else
printf "Hostname: %s\n" "$(hostname || echo "unknown")"
printf "Kernel: %s\n" "$(uname -srmo 2>/dev/null || echo "unknown")"
fi
date
print_section "Load & uptime"
# uptime includes current time, up time, users, and load averages
uptime || true
# /proc/loadavg exists on Linux; print raw numbers for machine parsing
[ -r /proc/loadavg ] && cat /proc/loadavg || true
print_section "CPU"
if command -v mpstat >/dev/null 2>&1; then
# Per-CPU one-shot sample
mpstat -P ALL 1 1 || true
else
# Minimal fallback when sysstat/mpstat is not installed
top -bn1 | head -5 || true
fi
print_section "Memory"
if command -v free >/dev/null 2>&1; then
free -h || true
fi
# Key meminfo lines for quick glance and machine parsing
if [ -r /proc/meminfo ]; then
grep -E '^(MemTotal|MemFree|MemAvailable|SwapTotal|SwapFree):' /proc/meminfo || true
fi
print_section "Disk usage"
# Exclude tmpfs/devtmpfs; include filesystem type column
df -hT -x tmpfs -x devtmpfs || true
print_section "IO (1s sample)"
# Prefer iostat extended stats; fallback to vmstat
if command -v iostat >/dev/null 2>&1; then
# Two samples (discard first), extended, all devices
iostat -xz 1 2 | tail -n +7 || true
elif command -v vmstat >/dev/null 2>&1; then
vmstat 1 2 | tail -1 || true
fi
print_section "Network (rx/tx totals)"
# Prefer 'ip -s link'; fallback to /proc/net/dev
if command -v ip >/dev/null 2>&1; then
# Print RX/TX bytes per interface compactly
# Note: ip -s link groups stats in blocks; awk stitches iface with RX/TX bytes
ip -s link 2>/dev/null | awk '
BEGIN{iface=""}
/^[0-9]+: /{gsub(":","",$2); iface=$2}
/RX: bytes/ {rx=$3}
/TX: bytes/ {tx=$3; if (iface!="") {printf "%s RX:%s TX:%s\n", iface, rx, tx}}
' || true
elif [ -r /proc/net/dev ]; then
# /proc/net/dev format: face: bytes ... | bytes ...
awk -F'[: ]+' 'NR>2 {printf "%s RX:%s TX:%s\n", $1, $(2+1), $(10+1)}' /proc/net/dev || true
fi
print_section "TCP summary"
# ss is preferred; netstat as fallback if available
if command -v ss >/dev/null 2>&1; then
ss -s || true
elif command -v netstat >/dev/null 2>&1; then
netstat -s || true
fi
print_section "Top CPU processes"
ps -eo pid,ppid,cmd,%cpu,%mem --sort=-%cpu | head -n 15 || true
print_section "Top MEM processes"
ps -eo pid,ppid,cmd,%mem,%cpu --sort=-%mem | head -n 15 || true
print_section "Docker"
if command -v docker >/dev/null 2>&1; then
# List running containers
docker ps || true
# One-shot stats if the daemon is responsive
docker stats --no-stream --format 'table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.MemPerc}}' || true
else
echo "docker: not installed" || true
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment