My Free Monitoring Stack for Self-Hosted Apps
Cloud

My Free Monitoring Stack for Self-Hosted Apps

Vercel gives you monitoring whether you ask for it or not. A VPS gives you a blank terminal and the assumption you'll figure it out. Here's what to actually watch on a self-hosted box, with tools that cost nothing, and why watching from the box itself is the one setup that will lie to you.

Published June 24, 202611 min readUpdated Sep 6, 2026

Written by · Full-Stack Agentic AI Software Engineer — AI Agents, Automation & Revenue Systems for GTM/RevOps teams

In brief

What should you actually monitor on a self-hosted VPS, and which free tools cover each piece?

Five things matter and none of them cost money: host metrics (CPU, RAM, disk, network) from something like Beszel or Netdata's free tier, container metrics from Docker's own stats or a lightweight agent, uptime checked from a machine that isn't the one you're watching, certificate expiry tracked explicitly now that Let's Encrypt no longer emails you about it, and disk usage watched closely because a full disk is the most common way a self-managed box takes itself down. The one rule that matters more than tool choice: an uptime check running on the server it's checking is a smoke detector wired to the thing it's supposed to warn you is on fire.

  • Host metrics and container metrics are different signals — a host with headroom can still have one container OOM-killed inside its own cgroup limit
  • Uptime has to be checked from outside the box, or the one failure mode that matters most (the box is unreachable) is exactly the one your monitor can't report
  • Let's Encrypt ended its certificate expiration email service in January 2025 — there's no longer a vendor safety net for silently expired certs, you own that check now
  • Disk-full is the single most common self-inflicted outage on a small VPS, usually from unrotated Docker logs or unpruned images, not from a slow, visible growth curve
  • None of this requires a paid tier: Uptime Kuma, Beszel, Netdata's community plan, and UptimeRobot's free plan cover the whole list without a credit card

Evidence notes

Let's Encrypt retired expiration emails

Let's Encrypt's own documentation confirms the certificate expiration email service ended, following a January 2025 announcement — a change that quietly removed a safety net a lot of self-hosters were relying on without realizing it.

Docker's json-file logging driver doesn't rotate by default

Docker's own docs on the json-file driver state max-size defaults to -1 (unlimited) and max-file to 1 — rotation only happens if you configure it, which is why unrotated container logs are such a common way to fill a disk.

Uptime Kuma

MIT-licensed, self-hosted status and uptime monitoring tool with HTTP/TCP/DNS/ping/Docker checks and 20-second monitoring intervals, run via Docker or Node.

Netdata's free Community plan

Netdata's own pricing page lists the free Community tier at up to 5 actively connected nodes with over a year of local retention on 3GB of disk; the core agent is GPLv3-licensed and open source.

UptimeRobot's free plan

UptimeRobot's pricing page lists 50 monitors on a 5-minute check interval on the free tier, with email, SMS, voice call and other notification channels, plus SSL/domain expiry checks included.

Continue with purpose

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

  1. 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.

  2. 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.

  3. 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.

  4. 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.

  5. 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

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.

What does free monitoring actually cost?

Four questions on assembling a monitoring stack that costs nothing in licence fees. The costs are real, they're just not on an invoice.

1 / 4
Where should the monitoring stack itself run?

Apply this article

How to turn insights into execution

A practical sequence for teams turning concepts into production outcomes.

monitoringself-hostinguptimeVPSDockerobservabilityCoolifycloud

Audit your current state

Map the bottlenecks and constraints connected to the article’s core problem.

Choose one bounded change

Test the most useful recommendation on one workflow before widening the scope.

Measure what changed

Keep the parts that improve the work, document what failed, and make the next decision from evidence.

Next step

Need help applying this in your stack?

I can translate these patterns into a concrete implementation plan for your team.

Discuss implementationBack to blog

Replies usually within 24 hours.

Next Steps

Continue reading

10 Mistakes That Break a Self-Hosted SaaS

None of these ten show up as a single dramatic outage. They show up as a disk that quietly fills, a rollback that turns out to be impossible, a backup nobody ever restored. Here's the mechanism behind each one, and the fix.

Navigate

The Simplest Production Architecture for a Bootstrapped SaaS

One box, a handful of managed pieces around the edges, and a very short list of things you're not allowed to build yet. Here's the architecture, priced out to $20 a month, and the exact signal that tells you when to add each thing you skipped.

Navigate

Moving a SaaS Off Vercel to a €5.50 VPS: What the Numbers Actually Look Like

Nobody publishes their real Vercel invoice, so most migration posts trade in vibes instead of arithmetic. This one builds the comparison from Vercel's and Contabo's own published rates, states every assumption out loud, and shows exactly where the two lines cross.

Navigate

Should Your Database Live on the Same VPS as Your App?

The pitch for co-location is real: no network hop, no egress bill, one box to back up. So is the failure mode — one OOM event takes the app and the database down together, because they were never separate to begin with.

Navigate

Best VPS Providers for Coolify: Contabo, Hetzner and the 2026 Price Shift

Every 'best VPS for Coolify' post still says Hetzner is the cheap option. That stopped being reliably true in June 2026 for two of its most popular tiers, and nobody's updated the recommendation yet.

Navigate

Zero-Downtime Deploys and Instant Rollbacks on a Cheap VPS

docker compose up -d looks like a deploy and behaves like an outage — there's a gap between the old container stopping and the new one answering requests, and on a small box that gap is exactly where a real user lands.

Navigate

The 80/20 Self-Hosting Stack for Indie Hackers

Most self-hosting advice is all-or-nothing: run everything yourself, or don't bother. Neither is right. Here's the specific 20% of self-hosting effort that actually delivers 80% of the savings, and the parts not worth touching.

Navigate

Uptime Kuma vs Better Uptime vs Grafana: How Much Monitoring Do You Need?

Three tools, three different answers to a question most people never actually ask themselves: how much monitoring does this app need, versus how much monitoring would feel satisfying to build? Here's the honest comparison, including the part where self-hosting your own uptime monitor is a slightly absurd idea.

Navigate

Coolify vs Dokploy: Which Self-Hosted PaaS Should You Run in 2026?

I run Coolify on my own box and I'd still tell most people starting today to look hard at Dokploy first. Here's the actual decision, not a feature-for-feature tie.

Navigate

What Happens When Your VPS Runs Out of RAM: The OOM Killer, Explained

There's no warning banner before the kernel kills something. One moment the box is fine, the next a process is dead mid-request — and the process that dies is often not the one that caused the spike. Here's the actual mechanism, and how to read the wreckage afterward.

Navigate