A few weeks ago I noticed some things I couldn't explain on one of my VPS instances — nothing catastrophic, just enough "that's not right" to make me nervous. I went looking for a lightweight tool to audit and harden a fresh Linux server and mostly found two things: hand-rolled bash scripts of wildly varying quality, or heavyweight compliance scanners built for enterprises running hundreds of servers, not someone with a couple of $5/mo boxes.
So I built vpsguard — a single Go binary that audits, hardens, and monitors a Linux VPS, with no dependencies beyond what's already on the box.
What it actually does
That's a real recording — audit finding real issues on a stock Ubuntu box, harden fixing them, and monitor catching a simulated attacker adding their own SSH key. No staged screenshots.
Twelve checks in total: SSH config, firewall (ufw and firewalld — more on that below), fail2ban, suspicious user accounts, SSH key hygiene, cron, pending updates, exposed database ports, Docker socket permissions, kernel/patch status, and (as a beta check) AWS EC2's IMDSv1 exposure — the same SSRF vector behind the Capital One breach.
Every harden remediation is idempotent — run it ten times, get the same end state, no duplicated config lines — and every config file it touches gets backed up first.
The part that actually matters: monitor
Audit is nice, but the thing that motivated this whole project was catching change — someone adding an SSH key you didn't add, a new UID-0 account, a new process running as root. vpsguard monitor snapshots server state and diffs it against the last run — that's the second half of the GIF above, where it catches the CRIT the instant the attacker's key gets added.
Wire it to cron (vpsguard install-cron) and point it at a Slack/Discord webhook or email, and you get pinged instead of finding out three weeks later when you happen to check a log.
Running it from CI/CD
If you'd rather bake this into a pipeline than remember to run it yourself, there's a GitHub Action that audits a whole fleet on a schedule:
name: VPS security audit
on:
schedule:
- cron: "0 6 * * *" # daily at 06:00 UTC
workflow_dispatch:
jobs:
audit:
runs-on: ubuntu-latest
steps:
- uses: salamancacm/vpsguard-action@v1
with:
hosts: web-1.example.com,db-1.example.com
ssh-user: root
ssh-key: ${{ secrets.VPS_SSH_KEY }}
ssh-known-hosts: ${{ secrets.VPS_KNOWN_HOSTS }}
fail-on: CRIT
It installs vpsguard on the runner, connects to each host with your own SSH key (never vpsguard's — it doesn't handle credentials), runs the audit, and writes a summary table straight into the workflow's job summary. Set fail-on to CRIT, WARN, or NONE depending on whether you want a bad finding to actually fail the build or just get reported; it also exposes crit-count, warn-count, and unreachable-count as outputs if you want to branch on them yourself.
A few things I'm actually proud of
-
It's honest about what it hasn't verified. The AWS IMDSv1 check is tagged
[BETA]in its own output, because I built and unit-tested it but never ran it against a real EC2 instance (that would mean spending real money on infra I didn't need). Rather than pretend otherwise, findings from unverified checks are marked, everywhere — table output and--jsonboth. -
Real bugs got caught by testing against real systems, not mocks. Validating the RHEL/
dnfcode path against an actual Rocky Linux container surfaced two genuine bugs:hardenwas trying todnf install ufw(not in RHEL's repos at all) and the audit checks had no concept offirewalld— RHEL's actual default firewall — so a properly-secured RHEL box would've been reported as having no firewall at all. Fixed, and now covered by real Rocky Linux testing. -
vpsguard fleetaudits multiple hosts over SSH at once, using your own SSH config/agent — the same mechanism the GitHub Action above wraps.
Installing it
curl -fsSL https://raw.githubusercontent.com/salamancacm/vpsguard/main/install.sh | sh
(Checksum-verified against the release, refuses to install on a mismatch — read the script first if piping curl to sh isn't your thing, which is fair.) Also available via go install, and Homebrew-on-Linux.
What's next
Kernel/patch checking on RHEL is solid; broader cloud-provider coverage (Security Group/VPC-level blindness is a real gap right now — vpsguard only sees the OS firewall, not AWS/GCP's network layer) is on the list. If you run a VPS and have five minutes, vpsguard audit is read-only and won't touch anything — I'd genuinely like to know what it gets wrong on your setup.
Repo's here: https://github.com/salamancacm/vpsguard — issues, PRs, and "this check is wrong" reports all welcome.

Top comments (0)