Skip to content
Commitpedia
Log in Join & edit

Server health check script: disk, memory and failed units

A dependency-free bash script that checks disk and inode usage, available memory, load, failed systemd units and pending reboots, and exits non-zero on any warning.

Drafted with AI from official documentation. Scripts were run in a test environment and repository links verified. Awaiting a human check. Did it work for you? Last edited Outdated?

Edit history of Server health check script: disk, memory and failed units: 1 recorded edit
Edit history · 1 edit
Type
Script
Edits
1
Editors
1
First issued
15 Oct 2026
Last edit
15 Oct 2026
Language
bash
Licence
CC0-1.0

What this changes

Read-only: it reads disk, inode, memory, load, systemd and reboot status and prints OK or WARN lines; nothing is changed. The optional install step copies it to /usr/local/bin/healthcheck.

How to undo it

Nothing to undo for the checks. If you installed it, remove it with sudo rm /usr/local/bin/healthcheck.

Run this on any Debian, Ubuntu, RHEL, Rocky or AlmaLinux server for a quick “is anything wrong?” answer. It only reads information, needs no root and uses only tools that are always installed (GNU coreutils, awk, systemctl, journalctl). The exit code is 0 when everything is fine and 1 when there are warnings, so it also works from a monitoring system or a systemd timer.

The script

#!/usr/bin/env bash
set -uo pipefail

DISK_WARN=${DISK_WARN:-85}
INODE_WARN=${INODE_WARN:-85}
MEM_WARN=${MEM_WARN:-10}

problems=0
ok()   { printf '  OK    %s\n' "$*"; }
warn() { printf '  WARN  %s\n' "$*"; problems=$((problems + 1)); }

echo "Health check for $(hostname) at $(date '+%Y-%m-%d %H:%M:%S')"

echo "Disk space (warn at ${DISK_WARN}%):"
while read -r fs size used avail pct mount; do
  [[ $pct == "-" ]] && continue
  p=${pct%\%}
  if (( p >= DISK_WARN )); then
    warn "$mount is ${pct} full (${avail} free of ${size}) on $fs"
  else
    ok "$mount ${pct} used"
  fi
done < <(df -hP -x tmpfs -x devtmpfs -x squashfs -x overlay | tail -n +2)

echo "Inodes (warn at ${INODE_WARN}%):"
inode_problems=0
while read -r fs inodes iused ifree ipct mount; do
  [[ $ipct == "-" ]] && continue
  p=${ipct%\%}
  if (( p >= INODE_WARN )); then
    warn "$mount uses ${ipct} of its inodes"
    inode_problems=$((inode_problems + 1))
  fi
done < <(df -iP -x tmpfs -x devtmpfs -x squashfs -x overlay | tail -n +2)
(( inode_problems == 0 )) && ok "inode usage below ${INODE_WARN}% everywhere"

echo "Memory (warn below ${MEM_WARN}% available):"
mem_total=$(awk '/^MemTotal:/ {print $2}' /proc/meminfo)
mem_avail=$(awk '/^MemAvailable:/ {print $2}' /proc/meminfo)
mem_pct=$(( mem_avail * 100 / mem_total ))
if (( mem_pct < MEM_WARN )); then
  warn "only ${mem_pct}% of memory available ($((mem_avail / 1024)) MiB)"
else
  ok "${mem_pct}% of memory available ($((mem_avail / 1024)) MiB)"
fi
swap_total=$(awk '/^SwapTotal:/ {print $2}' /proc/meminfo)
swap_free=$(awk '/^SwapFree:/ {print $2}' /proc/meminfo)
if (( swap_total > 0 )); then
  swap_used_pct=$(( (swap_total - swap_free) * 100 / swap_total ))
  if (( swap_used_pct >= 50 )); then
    warn "swap is ${swap_used_pct}% used"
  else
    ok "swap ${swap_used_pct}% used"
  fi
fi

echo "Load:"
cpus=$(nproc)
read -r load1 load5 load15 _ < /proc/loadavg
if awk -v l="$load5" -v c="$cpus" 'BEGIN { exit !(l > c) }'; then
  warn "5-minute load $load5 is higher than CPU count $cpus"
else
  ok "load $load1 $load5 $load15 on $cpus CPUs"
fi

echo "systemd units:"
if command -v systemctl >/dev/null 2>&1; then
  failed=$(systemctl list-units --failed --no-legend --plain 2>/dev/null | awk '{print $1}')
  if [[ -n $failed ]]; then
    while read -r unit; do warn "failed unit: $unit"; done <<< "$failed"
  else
    ok "no failed units"
  fi
  if command -v journalctl >/dev/null 2>&1; then
    ok "$(journalctl --disk-usage 2>/dev/null | tail -n 1)"
  fi
else
  ok "systemctl not found, skipped"
fi

echo "Pending reboot:"
if [[ -f /var/run/reboot-required ]]; then
  warn "reboot required (/var/run/reboot-required exists)"
elif command -v needs-restarting >/dev/null 2>&1; then
  if needs-restarting -r >/dev/null 2>&1; then
    ok "no reboot needed"
  else
    warn "needs-restarting -r reports a reboot is needed"
  fi
else
  ok "no reboot marker found"
fi

echo
if (( problems > 0 )); then
  echo "RESULT: $problems warning(s)"
  exit 1
fi
echo "RESULT: all checks passed"

How to use it

Check before you run this

  • Runs with administrator rights. Make sure you understand every line.
sudo install -m 755 healthcheck.sh /usr/local/bin/healthcheck
healthcheck
DISK_WARN=90 MEM_WARN=5 healthcheck      # custom thresholds
healthcheck || echo "something needs attention"

What it checks

  • Disk space on real filesystems (tmpfs, squashfs and overlay mounts are skipped) against DISK_WARN, default 85%.
  • Inodes: a filesystem can report “No space left on device” with free space left if it ran out of inodes, usually because of millions of tiny files.
  • Memory using MemAvailable from /proc/meminfo, which estimates how much memory new programs can get without swapping. “Free” memory alone is misleading because Linux uses idle RAM for cache.
  • Swap usage above 50%.
  • Load: the 5-minute load average compared with the CPU count.
  • Failed units from systemctl list-units --failed, and how much space the journal uses.
  • Pending reboot: /var/run/reboot-required on Debian and Ubuntu, or needs-restarting -r (from dnf-utils / dnf-plugins-core) on RHEL family systems, which exits 1 when a reboot is needed.

Running it on a schedule

Pair it with a systemd timer (see the timer guide) or call it from your monitoring agent. Because the output is plain text, the journal keeps a history of every run:

[Unit]
Description=Server health check

[Service]
Type=oneshot
ExecStart=/usr/local/bin/healthcheck

Official documentation

Did this work for you?

No votes yet. Be the first.

If it did not work, the report form below tells the editors what went wrong.

See also

  • 0102 Proxmox VE storage types: local, LVM-thin, ZFS, NFS and Ceph Proxmox VE stores disks, ISOs, templates and backups in storage pools defined in /etc/pve/storage.cfg. Learn the main storage types, what local and… Intermediate · Explains an idea · Linux Article Disks, LVM & filesystems · Storage 15 Oct 2026
  • 0164 systemd services: systemctl and journalctl basics systemd starts and supervises services on most Linux servers. systemctl starts, stops and enables them; journalctl shows their logs. Beginner · Explains an idea · Linux Article Services & systemd 15 Oct 2026
  • 0165 Write a systemd service unit for your own app Turn any long-running program into a proper service: create a unit file, run it as an unprivileged user, restart it on failure… Intermediate · Step by step · Linux Article Services & systemd 15 Oct 2026
  • 0166 Replace a cron job with a systemd timer A systemd timer runs a service on a schedule like cron, but logs every run to the journal, can catch up missed… Intermediate · Step by step · Linux Article Services & systemd 15 Oct 2026
  • 0167 Fix: Job for nginx.service failed because the control process exited with error code systemctl prints this when a service's start command exits with an error. The real reason is in journalctl; for nginx it is… Intermediate · Fixes an error · Linux Article Networking & firewalls · Services & systemd 15 Oct 2026
  • 0169 Add a new disk: partition, format and mount it by UUID Find a new disk with lsblk, partition it with parted, create an ext4 or XFS filesystem and mount it permanently through /etc/fstab… Intermediate · Step by step · Linux Article Disks, LVM & filesystems 15 Oct 2026

Repos in the wild

History

Every saved edit is kept. The plot on the sleeve draws one line per edit. Open any edit to see exactly what changed.

  1. Dominik +5,390 chars

Report a problem

Something wrong, unsafe or copied? Tell the editors.
What is wrong?