Vercel ships you a dashboard whether you want one or not. Deploy something there and you get function duration, error rates, and a status page someone else maintains, all before you've thought about monitoring once. Move that same app to a VPS and you get a login prompt and a blank terminal. Nobody hands you the dashboard. You have to decide to build one, which is exactly the step a lot of self-hosted setups skip, right up until the box goes quiet at a bad time and there's nothing to check.
None of what follows costs money. That's not a caveat — it's the actual point. The tools that cover host metrics, container metrics, uptime, certificate expiry, and disk usage all have free tiers or are outright open source, and a one-person VPS doesn't need more than that. What it does need is someone to actually wire the five pieces together, because no single tool does all five well, and picking one and assuming it covers the rest is how a box quietly runs unmonitored for months. Anyone pairing my free monitoring stack for self-hosted apps with an actual go-to-market motion will get more out of XenGrowth's operator guides.
It's worth being specific about why five, and not fewer. Each one fails independently of the others — a host can be fine while a single container isn't, a container can be fine while the box is unreachable from outside, and everything can be reachable while a certificate quietly expires underneath it. Cover four of the five and the gap is exactly where the next incident happens, because that's the one nobody was watching.
Host metrics: what's happening to the machine itself
This is the baseline layer: CPU, RAM, disk, and network, measured on the host, not inside any one container. Netdata's free Community plan covers this well for a single box or a small handful — up to five actively connected nodes, per-second granularity, and over a year of local retention squeezed into about 3GB of disk, with zero configuration required to get a dashboard up. It auto-detects almost everything running on the box and starts charting it immediately, which is unusually generous for a free tier.
If Netdata's dashboard feels heavier than you want, Beszel is the leaner option: a small hub-and-agent tool, MIT-licensed, that tracks CPU, memory, disk I/O, network, and load average per machine, plus Docker and Podman container stats from the same agent. It's a newer project than Netdata and Uptime Kuma, so the ecosystem around it is thinner, but the core job — a clean chart of host resources with almost no setup — it does well. The XenGrowth resource library covers the the operations side of this side of this.
Container metrics: a healthy host can still have a dying container
This is the layer people skip because host metrics look fine, and that's exactly the gap that bites. Under Docker, a `--memory` limit is a cgroup ceiling scoped to that one container. A container can get OOM-killed while the host has plenty of RAM free, because the limit that mattered was the container's own, not the box's total — a mechanism I go through in more detail in the OOM killer post. If your monitoring stops at the host level, that kill is invisible until something downstream breaks and you go looking for why.
`docker stats` gives you a live, per-container view of CPU, memory, and network for free, no install required — it's already on the box. That's fine for a quick check but useless as a historical record, since it doesn't persist anything. For that, Netdata's container collectors and Beszel's agent both already read per-container stats and chart them over time, so if you've picked one of those for host metrics, container visibility mostly comes along with it rather than needing a second tool.
Uptime: why checking from the box you're monitoring doesn't work
This is the one people get backwards, and it's worth being blunt about: a monitoring stack that runs entirely on the server it's watching cannot tell you the server is down. If the box loses power, loses networking, or the Docker daemon wedges, whatever was checking uptime from inside it dies at the exact moment it needed to send the alert. That's not a hypothetical edge case — it's the single failure mode uptime monitoring exists to catch.
Uptime Kuma is the obvious self-hosted choice for the checks themselves — MIT-licensed, HTTP/TCP/DNS/ping/keyword monitors, a built-in status page, 20-second intervals, and notifications to more than 90 services. But it has to run somewhere other than the box it's checking, or you've rebuilt the same trap with better UI. A $0 tier on a second provider, a free-tier serverless cron ping, or genuinely just UptimeRobot's own free plan — 50 monitors on a 5-minute interval, with SSL and domain expiry checks bundled in — all solve this the same way: put the checker somewhere the thing it's checking can't take it down with it. There is a longer treatment of AI agents and marketing automation in XenGrowth on governed AI marketing workflows.
Layer | What it catches | Free option | Runs where |
|---|---|---|---|
Host metrics | CPU, RAM, disk, network saturation on the box itself | Netdata Community, Beszel | On the VPS |
Container metrics | A single container OOM-killed or throttled while the host looks fine | Netdata/Beszel container collectors, docker stats | On the VPS |
Uptime | The box or app is unreachable, including total outages | Uptime Kuma (self-hosted), UptimeRobot free | Must run off-box |
Certificate expiry | TLS cert lapses and the app starts throwing browser warnings | UptimeRobot's SSL check, a cron + openssl script | Off-box or scripted |
Disk usage | Docker logs, images, or a growing database filling the volume | Netdata/Beszel disk alerts, a df cron check | On the VPS |
Certificate expiry: the safety net you had is gone
For years, an expiring Let's Encrypt certificate came with a built-in warning: an email a few weeks out, another closer in, from Let's Encrypt itself. That service ended in January 2025 — Let's Encrypt's own docs confirm it's discontinued. If you set up TLS a while ago and never wired up your own check because the vendor's email felt like enough, that safety net is gone and nothing replaced it automatically.
Certbot's automatic renewal usually means this never comes up in practice — a cron-triggered renewal that quietly succeeds every 60-ish days isn't something you need to watch. Where it bites is when renewal silently fails: a firewall rule changed, a webhook DNS challenge broke, a rate limit got hit. Without your own check, the first sign is a visitor's browser throwing a warning, which is a bad way to find out. UptimeRobot's free tier checks certificate expiry as part of its normal HTTP monitor, and a one-line cron job running `openssl s_client` against your own domain and diffing the expiry date works just as well if you'd rather not add another account.
Disk usage: the outage nobody saw building
If there's one thing worth over-monitoring on a small VPS, it's disk. Unlike a CPU spike or a memory leak, a filling disk doesn't announce itself with a slowdown you'd notice — everything runs fine right up until it doesn't, and then Postgres can't write a WAL segment, or the app can't write a temp file, and the failure looks unrelated to its actual cause. There is a longer treatment of AI search, GEO and discovery in XenGrowth on building one SEO and GEO content system.
Docker's json-file logging driver — the default — doesn't rotate on its own. `max-size` defaults to -1 (unlimited) and `max-file` to 1, so a chatty container can grow one log file without bound until you set `--log-opt max-size` yourself
Unpruned images and stopped containers accumulate silently after every deploy; `docker system df` shows what's actually reclaimable before you run `docker system prune`
A database that's fine at launch keeps growing, and 'started fine' tells you nothing about six months from now — disk usage needs a trend line, not a one-time check
Backup snapshots and dumps left on the same volume they're backing up are counted twice against your capacity and can independently fill the disk on their own
Disk isn't dramatic. It doesn't spike and recover like CPU does, and it doesn't get OOM-killed like memory does. It just climbs, quietly, until the exact moment nothing can write anywhere anymore — and by then the postmortem is usually about logs nobody set a max-size on.
Do you need Prometheus and Grafana instead?
Not for the setup this post describes. Prometheus plus Grafana buys you a real query language, long-term metric storage you control, and dashboards you can build to your own spec instead of whatever the tool ships with by default. That's genuinely useful once you have more than one box, or a metric that none of the pre-built dashboards expose, or a team that needs a shared view rather than one person's terminal. For a single VPS, it's also a second stateful service to run, back up, and keep patched, on the same machine you're trying to protect from running out of resources in the first place. I go through where that trade actually pays off in the next post in this pair — the short version is that most one-person setups never reach the point where Grafana earns its weight.
Tool | Covers | Setup effort | Where it should run |
|---|---|---|---|
Netdata (Community) | Host + container metrics, per-second charts | Single install script, near-zero config | On the VPS itself |
Beszel | Host + container metrics, leaner UI than Netdata | Deploy a hub, add an agent per box | Hub can live anywhere; agent on the VPS |
Uptime Kuma | Uptime, HTTP/TCP/DNS/ping checks, status page | One Docker container, a few minutes | Anywhere except the box it's checking |
UptimeRobot free | Uptime + SSL/domain expiry, no install at all | Sign up, add a monitor URL | Hosted — nothing to run |
Prometheus + Grafana | Everything above plus custom queries and long retention | Hours, plus ongoing upkeep | A separate box, ideally not the one it watches |
Getting this running this week
Install Netdata or Beszel's agent on the VPS. Either takes minutes and starts charting host and container metrics immediately with defaults that are good enough to leave alone.
Stand up Uptime Kuma somewhere that isn't this box — a free-tier instance elsewhere, or skip the hosting entirely and use UptimeRobot's free plan — and point it at your app's actual health endpoint, not just the bare domain.
Add a certificate expiry check, either through UptimeRobot's built-in SSL monitor or a scheduled `openssl s_client` check, since the vendor email that used to cover this doesn't exist anymore.
Set `--log-opt max-size` and `max-file` on any container that logs a lot, and run `docker system df` once now so you know what a normal baseline looks like before you're troubleshooting a full disk under pressure.
Point whichever tool sends alerts at a channel you actually check daily — a monitor nobody looks at is functionally the same as no monitor at all.
Putting it together without a second job
The realistic version of this, for one person running one box, is two tools and about twenty minutes: Netdata or Beszel on the VPS for host and container metrics, and Uptime Kuma somewhere else — a free-tier VM, a spare Pi, anything that isn't this box — for uptime and certificate checks. Wire both to a channel you actually check, not one you set up once and muted. That's the whole list. It costs nothing, and it covers the failure that actually matters most: the one where the box can't tell you it's the one that's down.
The next post in this pair goes further into the tool choice itself — when Uptime Kuma's honest simplicity is enough, when Grafana and Prometheus earn their extra weight, and where a hosted checker like Better Stack's beats self-hosting the monitor at all. If you've read this far and are still deciding what to run, that's the comparison to read next.
Further reading from XenGrowth
The XenGrowth resource library — what you'll learn: how the commercial side of this work is run, across search, automation and revenue operations.
XenGrowth on governed AI marketing workflows — what you'll learn: how the teams running AI marketing agents keep them governed and measurable.
XenGrowth on building one SEO and GEO content system — what you'll learn: how search and AI-answer visibility get run as a single content system.
Where this work meets go-to-market
If my free monitoring stack for self-hosted apps is part of a growth programme rather than a standalone build, XenGrowth, who work on the commercial side of this is the companion reading.
Four questions on assembling a monitoring stack that costs nothing in licence fees. The costs are real, they're just not on an invoice.











