- Posted on
- • Artificial Intelligence
Automating Network Health Reports
- Author
-
-
- User
- linuxbash
- Posts by this author
- Posts by this author
-
Automating Network Health Reports with Bash: Set-and-Forget Visibility for Your Linux Hosts
Ever had users say “the network feels slow” while your ad‑hoc pings look fine? Or woken up to an outage with no baseline to compare against? Automating network health reports gives you objective, time-stamped snapshots of connectivity, latency, DNS health, and interface errors—so you can spot trends before they become incidents.
This post shows how to build a lightweight, Bash-based reporting pipeline that:
Collects actionable network metrics
Stores and sends readable reports
Runs automatically on a schedule (systemd timers or cron)
No heavy agents. Just standard Linux tools, scripts, and a webhook (optional) for alerts.
Why automate network health reports?
Baselines beat hunches: A daily/hourly report establishes “normal” latency/loss, helping you detect regressions quickly.
Faster triage: When something breaks, you’ll already have the “last known good” path, DNS resolution times, and interface error counters.
Low overhead and portable: Bash + ubiquitous CLI tools means minimal dependencies, easy to maintain, and works across distros.
Auditable operations: Plain-text, timestamped logs help explain outages and support postmortems.
What you’ll build
A single Bash script that captures:
- Ping loss/latency to multiple targets
- DNS resolution time
- Default route and interface status/error counters
- Optional path-quality summary (mtr)
- External IP and quick connectivity check
Scheduled runs with systemd timers or cron
Optional notification to a webhook (e.g., Slack)
1) Install the required tools
You’ll need iproute2 (ip and ss), mtr for path quality, dig for DNS timing, and curl for webhooks/external checks.
Apt (Debian/Ubuntu):
sudo apt update
sudo apt install -y iproute2 mtr-tiny dnsutils curl
DNF (Fedora/RHEL/CentOS Stream):
sudo dnf install -y iproute mtr bind-utils curl
Zypper (openSUSE Leap/Tumbleweed):
sudo zypper refresh
sudo zypper install -y iproute2 mtr bind-utils curl
Optional (recommended):
vnStat (traffic history)
- apt:
sudo apt install -y vnstat - dnf:
sudo dnf install -y vnstat - zypper:
sudo zypper install -y vnstat
- apt:
iperf3 (controlled throughput tests)
- apt:
sudo apt install -y iperf3 - dnf:
sudo dnf install -y iperf3 - zypper:
sudo zypper install -y iperf3
- apt:
If you plan to use cron (instead of systemd timers), install and enable it:
apt:
sudo apt install -y cron sudo systemctl enable --now crondnf:
sudo dnf install -y cronie sudo systemctl enable --now crondzypper:
sudo zypper install -y cron sudo systemctl enable --now cron
2) Create the Bash script
Save this as /usr/local/bin/network-health.sh and make it executable.
#!/usr/bin/env bash
set -euo pipefail
# Configuration (override via /etc/network-health.env if using systemd EnvironmentFile)
TARGETS=("1.1.1.1" "8.8.8.8" "github.com")
PACKETS=20
LOSS_WARN=1.0 # % packet loss warning threshold
AVG_RTT_WARN=150.0 # ms average RTT warning threshold
DNS_WARN_MS=200 # ms DNS query time warning threshold
MTR_PROBES=50
LOG_DIR="/var/log/network-health"
WEBHOOK_URL="${WEBHOOK_URL:-}" # Optional: export WEBHOOK_URL for notifications
mkdir -p "$LOG_DIR"
timestamp="$(date -u +'%Y-%m-%dT%H:%M:%SZ')"
report="$LOG_DIR/report-$(date -u +'%Y%m%d%H%M').txt"
alerts=()
gt() { awk -v a="$1" -v b="$2" 'BEGIN{exit (a>b)?0:1}'; }
section() {
printf "\n===== %s =====\n" "$1" | tee -a "$report"
}
log() {
printf "%s\n" "$1" | tee -a "$report"
}
ping_summary() {
local host="$1"
local out loss avg
out="$(ping -c "$PACKETS" -i 0.2 -q "$host" 2>&1 || true)"
loss="$(printf "%s\n" "$out" | grep -oE '[0-9.]+% packet loss' | awk '{print $1}' | tr -d '%')"
avg="$(printf "%s\n" "$out" | awk -F'/' '/rtt|round-trip/ {print $5}')"
[ -z "${loss:-}" ] && loss=100
[ -z "${avg:-}" ] && avg=0
printf "Host: %s | Loss: %s%% | Avg RTT: %s ms\n" "$host" "$loss" "$avg" | tee -a "$report"
if gt "$loss" "$LOSS_WARN"; then
alerts+=("High loss to $host: ${loss}% > ${LOSS_WARN}%")
fi
if gt "$avg" "$AVG_RTT_WARN"; then
alerts+=("High latency to $host: ${avg} ms > ${AVG_RTT_WARN} ms")
fi
}
dns_check() {
if command -v dig >/dev/null 2>&1; then
local qtime
qtime="$(dig +stats +time=2 +tries=1 example.com 2>/dev/null | awk '/Query time:/ {print $4}')"
[ -z "${qtime:-}" ] && qtime=0
printf "DNS: Query time to resolve example.com: %s ms\n" "$qtime" | tee -a "$report"
if gt "$qtime" "$DNS_WARN_MS"; then
alerts+=("Slow DNS: ${qtime} ms > ${DNS_WARN_MS} ms")
fi
else
printf "DNS: dig not installed, skipping\n" | tee -a "$report"
end
}
path_quality() {
if command -v mtr >/dev/null 2>&1; then
for host in "${TARGETS[@]}"; do
printf "\n-- mtr %s (last hop summary) --\n" "$host" | tee -a "$report"
# Report mode, wide format, show both DNS/IP, no dns resolution delay (-n is opposite), cycles = MTR_PROBES
mtr -rwzc "$MTR_PROBES" "$host" 2>/dev/null | tail -n 5 | tee -a "$report" || \
printf "mtr failed for %s\n" "$host" | tee -a "$report"
done
else
printf "mtr: not installed, skipping path quality\n" | tee -a "$report"
fi
}
interface_status() {
section "Interfaces"
ip -br addr show | tee -a "$report"
printf "\n-- Interface error counters --\n" | tee -a "$report"
ip -s link | tee -a "$report"
}
routing_and_extip() {
section "Routing and External Reachability"
ip route show default | tee -a "$report"
printf "\n-- Route to 1.1.1.1 --\n" | tee -a "$report"
ip route get 1.1.1.1 2>&1 | tee -a "$report"
printf "\n-- External IP --\n" | tee -a "$report"
curl -fsS --max-time 5 https://api.ipify.org | tee -a "$report" || echo "Unavailable" | tee -a "$report"
}
webhook_notify() {
[ -z "${WEBHOOK_URL:-}" ] && return 0
local summary
if [ "${#alerts[@]}" -gt 0 ]; then
summary="Network report ${timestamp}: $(printf '%s; ' "${alerts[@]}")"
else
summary="Network report ${timestamp}: OK"
fi
# Minimal JSON payload (Slack-compatible)
local payload
payload=$(printf '{"text":"%s"}' "$(printf "%s" "$summary" | sed 's/"/\\"/g')")
curl -fsS -X POST -H 'Content-Type: application/json' -d "$payload" "$WEBHOOK_URL" >/dev/null || true
}
main() {
: > "$report"
printf "Network Health Report - %s (UTC)\n" "$timestamp" | tee -a "$report"
routing_and_extip
dns_check
section "Ping summary"
for t in "${TARGETS[@]}"; do
ping_summary "$t"
done
section "Path quality (mtr)"
path_quality
interface_status
printf "\n===== Result =====\n" | tee -a "$report"
if [ "${#alerts[@]}" -gt 0 ]; then
printf "WARNINGS:\n" | tee -a "$report"
printf ' - %s\n' "${alerts[@]}" | tee -a "$report"
else
printf "All checks within thresholds.\n" | tee -a "$report"
fi
ln -sfn "$report" "$LOG_DIR/latest.txt"
webhook_notify
}
main "$@"
Make it executable:
sudo install -m 0755 /usr/local/bin/network-health.sh /usr/local/bin/network-health.sh
sudo mkdir -p /var/log/network-health
Optional: Put configuration (including a webhook URL) in /etc/network-health.env:
sudo tee /etc/network-health.env >/dev/null <<'EOF'
WEBHOOK_URL="https://hooks.slack.com/services/XXXXX/XXXXX/XXXXX"
TARGETS=("1.1.1.1" "8.8.8.8" "github.com")
PACKETS=20
LOSS_WARN=1.0
AVG_RTT_WARN=150.0
DNS_WARN_MS=200
MTR_PROBES=50
EOF
Note: If you run as a non-root user, most commands work fine. mtr may require elevated capability; distro packages often set the needed capability automatically.
3) Schedule it (systemd timer or cron)
Systemd timer (recommended):
Service unit /etc/systemd/system/network-health.service:
[Unit]
Description=Network Health Report
[Service]
Type=oneshot
EnvironmentFile=-/etc/network-health.env
ExecStart=/usr/local/bin/network-health.sh
Timer unit /etc/systemd/system/network-health.timer:
[Unit]
Description=Run Network Health Report hourly
[Timer]
OnCalendar=hourly
RandomizedDelaySec=300
Persistent=true
[Install]
WantedBy=timers.target
Enable and start the timer:
sudo systemctl daemon-reload
sudo systemctl enable --now network-health.timer
systemctl list-timers | grep network-health
Classic cron (alternative):
System-wide hourly:
sudo tee /etc/cron.hourly/network-health >/dev/null <<'EOF'
#!/bin/sh
/usr/local/bin/network-health.sh
EOF
sudo chmod +x /etc/cron.hourly/network-health
Or via root’s crontab (every hour at minute 0):
sudo crontab -e
# Add:
0 * * * * /usr/local/bin/network-health.sh
4) How to read and use the reports (real-world examples)
Detect DNS issues:
- If “DNS: Query time” spikes above your normal (<50 ms typical on LAN), check resolvers, upstream reachability, or DoH/DoT overhead.
Identify path/regional issues:
- If loss/latency rise only to certain domains, use the mtr hop list to see where the degradation begins (peering vs local link).
Catch flaky links early:
- Interface “error” or “dropped” counters increasing between reports often indicate cabling, NIC, or driver problems.
Validate changes:
- After firewall or routing updates, compare the latest report to the previous hour/day for regression.
Reports are stored under /var/log/network-health/ with a latest.txt symlink for convenience. You can ship these logs with your usual log pipeline, or tail and diff them during incidents.
5) Extend and harden (optional ideas)
Persist traffic stats with vnStat:
- Install vnStat (see above), enable its service, and append
vnstat --onelineto the report for hourly totals.
- Install vnStat (see above), enable its service, and append
Throughput tests with iperf3:
- Schedule a small-window iperf3 test to a controlled server in another site or cloud region.
Per-site configs:
- Maintain different
/etc/network-health.envper role or location (e.g., branch, DC, cloud) with tailored targets and thresholds.
- Maintain different
Alert routing:
- Use different webhooks or mention channels for WARN vs OK. You can add logic to suppress “OK” messages except on change.
Quick smoke test
Run once manually to verify:
/usr/local/bin/network-health.sh
less /var/log/network-health/latest.txt
If you set a webhook, you should see a short summary message. Tune thresholds in /etc/network-health.env based on a day or two of baseline data.
Conclusion and next steps
Automated, text-based network health reports give you low-noise, high-signal insights with almost no operational burden. Start with the script above, schedule it hourly, and iterate:
Add or remove targets
Calibrate thresholds to your environment
Plug the reports into your chat or ticketing tools
Your next step:
Install the tools
Drop in the script
Turn on the timer
Review your first 24 hours of baselines
Small investment, big operational clarity—before the next “it feels slow” hits your inbox.