Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

vps-healthcheck

CI License: MIT Write-up

A single bash script that runs twelve daily health checks against a remote VPS over SSH, aggregates the result into one notification push, and exits 0 / 1 / 2 so cron mails on regression. Designed for self-hosted operators with one or two VPSes, where running a full observability stack (Prometheus + Alertmanager + Grafana) is overkill.

Born out of running sovgrid.org on a no-KYC VPS and wanting morning-coffee confidence that nothing rotted overnight. The reference deployment is documented in the engineering log at sovgrid.org/projects and sovgrid.org/stack.


What it checks

# Check Threshold
1 SSH reachability + public-URL 200 OK binary
2 /var/run/reboot-required flag warn
3 Pending Debian-Security updates warn ≥1, crit ≥5
4 Failed systemd units any → crit
5 UFW active inactive → crit
6 Fail2ban currently-banned (sshd jail) informational
7 Disk usage / and /boot/efi warn ≥85%, crit ≥95%
8 Memory available warn <200 MiB
9 OOM kills in last 24h via journalctl -k any → crit
10 Let's Encrypt cert expiry per domain warn <14d, crit <3d
11 Expected docker containers all Up missing → crit
12 Optional freshness file (e.g. nightly job output) stale ≥26h → warn

Everything is bundled into a single SSH session for efficiency — the script issues exactly one outbound ssh, parses the structured response, and pushes one notification on the way out. No retries, no auto-fix. Read the script, it fits on one screen with comments.

Usage

# Clone
git clone https://github.com/cipherfoxie/vps-healthcheck.git
cd vps-healthcheck

# Configure (option A: env vars)
export HC_SSH_HOST=my-vps
export HC_REACH_URL=https://my-domain.example/
export HC_CERT_DOMAINS="my-domain.example www.my-domain.example"
export HC_EXPECTED_CONTAINERS="caddy app"

# (option B: persistent config file alongside the script)
cp vps-healthcheck.conf.example vps-healthcheck.conf
$EDITOR vps-healthcheck.conf

# Test
bash vps-healthcheck.sh --dry-run

# Cron (daily 07:30 local)
crontab -e
# 30 7 * * * /usr/bin/bash /path/to/vps-healthcheck.sh --quiet >> /var/log/vps-healthcheck.log 2>&1

Exit codes: 0 = clean, 1 = warnings, 2 = critical. Cron mails on non-zero by default. The --quiet flag suppresses notification pushes when there's nothing to report.

Notifications

The script calls "$HC_NOTIFY" <title> <body> if HC_NOTIFY points to an executable. Use any one-shot notify helper:

  • A Matrix bridge (curl POST to a homeserver)
  • ntfy.sh (curl -d "$2" -H "Title: $1" https://ntfy.sh/your-topic)
  • A Telegram bot
  • Signal-cli
  • Plain notify-send if you run cron on a workstation with a display

If HC_NOTIFY is unset, the script prints to stdout only. Combined with cron's "MAILTO=you@host", that's enough for a one-VPS setup.

Why not Prometheus / Netdata / Monit / Uptime Kuma?

Tool Best at Why it's overkill for one VPS
Prometheus + Alertmanager + Grafana Fleet observability with PromQL, multi-day trends, complex alert rules Three services to run, learn, maintain. Itself a new fleet to monitor.
Netdata Real-time per-host dashboards with subsecond resolution Agent must run on the monitored host with broad permissions. Web UI surface adds attack area.
Monit / Munin Long-history graphs + service-level restarts Either daemonized on the host (Monit) or pull-based with a server (Munin).
Uptime Kuma / Healthchecks.io Pull/push uptime monitoring, status pages Excellent for external uptime but doesn't see disk, memory, OOM, or cert expiry from the inside.

Where vps-healthcheck fits: you already trust your SSH access to the box, you don't want a daemon, and you want the morning glance — "is anything red, is anything yellow, otherwise leave me alone."

Property vps-healthcheck Prometheus stack Netdata Uptime Kuma
Lines of code (operator-readable) ~250 ~50k+ ~100k+ ~10k+
Daemons to run 0 (cron only) 3+ 1 1
Attack surface on the VPS none added exporter port agent + web UI none
Per-host trend data none yes yes partial
Cert-expiry, security-update, OOM, fail2ban built-in yes requires custom exporters / queries partial URL only
Single-SSH bundling yes n/a n/a n/a
Setup time 5 min hours 30 min 30 min

This is not a replacement for any of the above when you actually need trends, real-time data, or multi-host fleet management. It's the right amount of monitoring for a one-or-two-VPS sovereign-stack operator who does not want to monitor the monitoring.

Companion: the Spark-side reference deployment

In the reference deployment, vps-healthcheck.sh runs on the operator's workstation, audits a remote no-KYC VPS, and pushes results to a private Matrix room via a one-line notify-matrix.sh helper. The full story (including the JSON sidecar that feeds the live /insights/ dashboard at sovgrid.org) lives at:

Requirements

On your workstation: bash, ssh, curl, optional jq (for the JSON sidecar). On the VPS: bash, sudo-able access to apt-get, ufw, fail2ban-client, journalctl, docker (only if container checks are enabled), openssl (only if cert checks are enabled).

Why public

If you self-host on a Bitcoin-aligned no-KYC stack and you don't want a full observability tower, you should be able to copy-paste this and have a daily audit running in 5 minutes. The script is small enough to read before you trust it. Issues + PRs welcome.

License

MIT. Take what's useful. Engineering log of the wider operator stack is at sovgrid.org.

About

Daily SSH-based VPS audit + one-shot notify (12 checks, no daemon, 250 lines). Reference deployment at sovgrid.org.

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages