How to Monitor a Self-Hosted Business Stack
Set up uptime checks, metrics, and silent job alerts for a self-hosted stack with Uptime Kuma, Prometheus, and Grafana. Real costs, config examples, and the mistakes that leave you blind.

A self-hosted stack that nobody watches is a stack that fails silently, usually on a weekend, usually discovered by a customer. The fix is a three-layer setup: uptime checks that tell you when something is down, metrics that tell you when something is dying, and dead-man's-switch monitors that tell you when scheduled jobs like backups stop running. You can build all three on open-source tools for the cost of one small VPS, about $6 to $12 a month, and this post walks through exactly how, what to alert on, and when paying for a monitoring SaaS is the smarter move.
This is written for teams running tools like Chatwoot, Twenty, Listmonk, Plausible, Cal.com, or Formbricks on their own servers, the stack we deploy and run for clients as a managed open-source stack. The tools below are what a competent setup looks like whether you run it yourself or hand it to someone else.
The three questions monitoring has to answer
Most monitoring guides jump straight to tools. Start with the questions instead, because each one maps to a different tool and skipping one leaves a hole.
- Is it up? A synthetic check hits your login page, API, or port from the outside and alerts when it stops responding. This is uptime monitoring, and the self-hosted answer is Uptime Kuma.
- Is it dying? Disk filling up, memory leaking, CPU pinned for an hour. Nothing is "down" yet, but it will be. This is metrics, and the standard answer is Prometheus plus Grafana.
- Did the jobs run? Backups, email queue flushes, certificate renewals, imports. These fail silently because nothing is watching a process that only runs at 2am. This is cron monitoring, done with a dead man's switch: the job pings a URL when it finishes, and silence triggers the alert.
Homelab guides usually cover the first question well, mention the second, and forget the third. For a business stack, the third is the one that hurts most. A failed backup job discovered during a restore attempt is the classic self-hosting disaster, and it happens to teams who assumed a green dashboard meant their backup jobs were still running.
Layer 1: uptime checks with Uptime Kuma
Uptime Kuma is an MIT-licensed, self-hosted monitoring tool with roughly 92,000 GitHub stars and an active maintainer base. One Docker container gives you checks every 20 seconds or slower, more than 90 notification integrations, public status pages, and certificate expiry tracking built into every HTTPS monitor.
Installation is genuinely five minutes:
mkdir uptime-kuma && cd uptime-kuma
curl -o compose.yaml https://raw.githubusercontent.com/louislam/uptime-kuma/master/compose.yaml
docker compose up -d
One warning from the project README that matters in production: do not put Kuma's data directory on NFS or any network filesystem. Its SQLite database needs a local disk or Docker volume. If you skip Docker and run it on Node.js directly, you need Node 20.4 or newer.
The checks that matter for a business stack
The default instinct is to point one HTTP monitor at each app's homepage. That catches total outages and nothing else. Design checks per app around how each one actually fails:
- Chatwoot: HTTPS monitor on the login page with a keyword check for text that only appears when the app rendered correctly. A Rails app that boots but cannot reach its database often returns a 200 with an error page, and a plain status-code check calls that "up." Add a second TCP monitor on the Postgres port. If you recently migrated from Zendesk to a self-hosted Chatwoot, these two checks are the proof that the move worked.
- Twenty CRM: HTTPS monitor on the sign-in page, plus one on the GraphQL endpoint if your team lives in the API. If you recently migrated from HubSpot to Twenty, monitor it like the revenue system it now is, not like a side project.
- Listmonk: HTTPS on the admin login, and an SMTP monitor on the submission port. A Listmonk instance that is up but cannot send is the failure mode that quietly kills a campaign. Deliverability problems look identical to outages from the subscriber side, so pair this with the practices in our email deliverability guide. Teams coming from Mailchimp to a self-hosted Listmonk should treat the SMTP check as non-negotiable.
- Plausible and Cal.com: HTTPS with keyword checks. For Cal.com, check the actual booking page, not the marketing homepage.
- Every HTTPS monitor: turn on the certificate expiry notification. An expired cert takes your whole app down as far as users are concerned, and Kuma warns you weeks ahead if you let it.
Set retries to 2 or 3 with a short gap before alerting. A single dropped packet is not an incident, and a monitor that pages you for one will train your team to ignore the channel. Intervals: 30 to 60 seconds for anything customer-facing, 5 minutes for internal tools. Kuma supports checks down to 20 seconds, but faster polling on everything just multiplies noise and log volume.
Notifications that actually reach you
A notification that travels through the thing being monitored is not a notification. If your mail server or your whole VPS is down, an SMTP alert sent through that same server never arrives.
Two rules follow. First, route alerts to a channel that lives outside your infrastructure: Telegram, Slack, Discord, or a push service. ntfy is open source and self-hostable if you want push notifications you own, with a hosted free tier and paid plans from $5 a month if you would rather not run it. Second, configure two channels on every critical monitor, a chat channel for speed and email or push as the fallback. Kuma lets you attach multiple notification targets per monitor, so use it.
Alert routing is also where monitoring meets workflow automation. Kuma supports generic webhooks, so an alert can trigger an n8n workflow that opens a ticket, posts to the on-call chat, and pages a phone, rather than just dropping a message in a channel. That kind of glue is a core part of the business automation work we do alongside the stack itself.
Monitor the monitor
Here is the uncomfortable part: Uptime Kuma cannot tell you when Uptime Kuma dies. If the VPS running your monitoring goes down, or Kuma's container crashes, your alerting goes silent and everything looks green.
The standard fix is an external watchman, and it costs nothing. UptimeRobot's free tier gives you 50 monitors at a 5-minute interval, which is plenty to watch your Kuma status page URL, your reverse proxy, and each app's public domain from an independent network. Set it up once, point it at a different notification channel than Kuma uses, and you have covered the "who watches the watcher" problem for $0. The free tier also includes SSL and domain expiry monitoring, so it works as a genuine second opinion rather than a partial one.
Paid tiers on UptimeRobot start at $13 a month ($12 on annual billing) for 60-second intervals and 3 status pages, rising to $46 a month for a team plan with 30-second checks and webhook integrations. Worth knowing for the build-versus-buy comparison below.
Layer 2: dead man's switches for backups and cron jobs
Uptime checks watch things that should always be running. Cron jobs are the opposite: they should run occasionally, and the failure mode is silence. The monitoring pattern is inverted. The job pings a unique URL when it completes, and the monitor alerts when the ping does not arrive on schedule.
Two ways to do it:
- Uptime Kuma push monitors. Create a monitor of type Push, copy its URL, and add a curl call to the end of your backup script. If the ping misses its window, Kuma alerts through your normal channels. Free, already running, nothing new to maintain.
- Healthchecks.io, the dedicated service for this. The free Hobbyist plan covers 20 jobs, which handles most small stacks. The Business plan at $20 a month ($16 on annual billing) raises that to 100 jobs and adds SMS, WhatsApp, and phone-call credits for escalation.
One detail from Healthchecks' own documentation worth internalizing: it keeps 100 log entries per job on the free plan. For a weekly backup that is two years of history; for a job that runs every 5 minutes it is 8 hours. Match your ping cadence to how much history you need.
Monitor every scheduled job that would hurt if it silently stopped: nightly backups, certificate renewals, email queue flushes, data imports. This is the cheapest insurance in the whole setup, and the most commonly skipped.
Layer 3: metrics with Prometheus and Grafana
Uptime checks tell you a service died. Metrics tell you it was dying for two weeks first. This layer is optional for a very small stack and essential once downtime has real cost.
Prometheus is the open-source standard: originally built at SoundCloud in 2012, it joined the Cloud Native Computing Foundation in 2016 as the second hosted project after Kubernetes. It pulls metrics over HTTP from your hosts and apps on a schedule, stores them as time series, and answers queries in PromQL. Each server runs node_exporter, an Apache-2.0 agent that exposes CPU, memory, disk, and network metrics. Apps that expose their own /metrics endpoint get scraped directly, and short-lived jobs can push through a gateway instead.
Grafana sits on top for dashboards and, more importantly, alerting: you define a query, a threshold, and a contact point, and it notifies you when the query crosses the line. The alerts worth setting on day one:
- Disk usage above 85% on every mounted filesystem. Full disks are the single most common cause of self-hosted outages, and they give you days of warning if you watch them.
- Sustained high load: load average above the core count for 10 minutes. Spikes are normal; a plateau means something is stuck.
- Memory that climbs and never comes back after a process restart, the classic slow-leak signature.
- Certificate expiry under 14 days, as a backstop to Kuma's built-in tracking.
A disk alert in PromQL looks like this:
100 - (node_filesystem_avail_bytes{mountpoint="/"} * 100
/ node_filesystem_size_bytes{mountpoint="/"}) > 85
Two operational rules. First, set a retention flag on Prometheus (--storage.tsdb.retention.time=30d is a sane default) or its time-series database will eventually eat the disk it was supposed to be watching. Second, never expose Prometheus to the public internet. It has no built-in authentication; put it behind your reverse proxy with auth or bind it to localhost and reach it over a VPN or SSH tunnel.
If all of this sounds like more moving parts than your team wants, that is a legitimate position, and it is exactly the maintenance burden a managed service exists to absorb.
Status pages: when customers need to see it
Once customers depend on your stack, "we are looking into it" needs a URL. Uptime Kuma builds status pages in for free: multiple pages, mappable to your own domain, showing current status and uptime history per monitor. For most internal tools and small customer bases, that is enough.
The paid alternatives exist for polish and workflow. Better Stack bundles status pages with on-call scheduling and phone-call escalation from $34 per license per month ($29 annually), with a free tier of 10 monitors and one status page. One status page is included per responder license; additional public pages cost $15 a month ($12 on annual billing) and custom CSS and JavaScript another $15 per page, while enterprise extras like audit logs run $250 a month. If all you need is a public "is it up" page, Kuma's built-in one saves real money.
What it costs: build versus buy
The self-hosted build:
- VPS: a 1 GB droplet on DigitalOcean costs $6 a month and runs Uptime Kuma with room to spare. Add Prometheus, Grafana, and node_exporter and you want the 2 GB size at $12 a month. Put the monitoring VPS in a different region or provider than your production stack, or a provider-level incident takes both down together.
- External watchman: UptimeRobot free tier or Healthchecks.io free tier, $0.
- Push notifications: ntfy hosted free tier, Telegram, or Slack webhooks, $0.
- Total: $6 to $12 a month, plus a day of setup and an hour a month of care.
The SaaS equivalent:
- UptimeRobot Solo at $13 a month covers 60-second checks, SSL monitoring, and status pages for one person; Team at $46 a month adds seats and 30-second intervals.
- Better Stack from $34 per license adds on-call rotations and unlimited phone and SMS alerts, which self-hosted Kuma cannot do without bolting on more services.
- Healthchecks.io Business at $20 a month if you want managed cron monitoring with SMS and phone escalation.
The honest trade-off: the self-hosted build is cheaper in cash and more flexible, but you own the maintenance, and your alerting is only as good as the channels you wired up. If your team needs phone-call escalation at 3am with rotation scheduling, that is the feature SaaS monitoring actually sells, and it is worth paying for.
Common mistakes
- Monitoring from inside the house only. Kuma on the same VPS as production, with no external watchman. One outage takes down the stack and the alarms together.
- Alerting on the first failed check. Set 2 to 3 retries. A monitor that cries wolf gets muted, and muted monitoring is worse than none because it feels like coverage.
- Status-code-only checks. Apps return 200 on error pages. Use keyword checks against text that only appears on a healthy render.
- No dead man's switch on backups. The backup script fails in month two; you find out in month nine, during the restore.
- Prometheus without retention limits or authentication. It fills its own disk, and anyone who finds the port can read your infrastructure's vital signs.
- Monitoring everything on day one. Start with the five checks whose failure would page you anyway. Add the rest after your first real incident shows you the gaps.
- Kuma's data on NFS. The project explicitly does not support network filesystems; use a local volume.
When SaaS monitoring is the wrong choice to skip
Be honest about the other direction too. Skip the self-hosted build and pay for Better Stack, UptimeRobot, or a managed service when any of these hold: nobody on the team will own the monitoring box itself; you need on-call rotations with phone escalation; you have compliance requirements that want SOC 2 paperwork from your vendors (UptimeRobot's enterprise tier exists for exactly this); or your "stack" is three apps and your time is worth more than $12 a month plus maintenance. Monitoring is the layer that has to work when everything else is broken. Boring and paid beats clever and neglected.
Frequently asked questions
Is Uptime Kuma free for business use? Yes. It is MIT-licensed open source, so commercial use costs nothing beyond the server it runs on. A $6-a-month VPS handles it comfortably.
Can Uptime Kuma replace Prometheus and Grafana? Not really. Kuma answers "is it up" with checks as fast as every 20 seconds, but it is not a time-series metrics system. Prometheus and Grafana answer "is it dying" with historical CPU, memory, and disk trends. Small stacks can start with Kuma alone and add metrics later.
How do I monitor the monitoring tool itself? Point an external service at it. UptimeRobot's free tier watches 50 URLs at 5-minute intervals for $0, which is enough to cover your Kuma status page, reverse proxy, and every public app domain from an independent network.
What should I alert on first in a self-hosted stack? In order: the public URL of each customer-facing app with a keyword check, disk usage above 85%, a dead man's switch on your backup job, and certificate expiry. Those four catch the large majority of real incidents.
Is Prometheus free, and how hard is it to run? Prometheus and node_exporter are open source under the CNCF and Apache-2.0 respectively, free for any use. A basic setup is three containers plus a scrape config; the learning curve is PromQL and alert rule design, not installation.
How much does monitoring SaaS cost compared to self-hosting? Self-hosted runs $6 to $12 a month in VPS costs. Comparable SaaS starts around $13 a month for UptimeRobot Solo and $29 to $34 per license for Better Stack, with on-call phone escalation as the main feature self-hosting lacks.
Where to go from here
Start with Uptime Kuma and an external free watchman this week, add push monitors to your backup jobs, and layer on Prometheus and Grafana when downtime starts costing real money. If you would rather have the whole stack, monitoring included, deployed and run for you, that is exactly what our managed open-source stack service does: we run the tools, watch them around the clock, and hand you the status page.
Related reading
About AI Agents Plus Editorial
AI automation expert and thought leader in business transformation through artificial intelligence.



