
Here's a scene that plays out at companies of every size, every week: a customer tweets that your app is throwing errors. Then another one emails support. Then your CEO forwards you a screenshot with three question marks.
By the time someone on your team actually opens the dashboard, the outage has already been running for twenty, thirty, sometimes ninety minutes โ and you found out about it from the people you were supposed to be serving, not from your own systems.
That's the entire reason uptime monitoring exists. Not to make your architecture diagram look impressive, not to tick a compliance box, but to make sure you are the first to know when something breaks, not the last.
It sounds almost too simple to write a whole article about. Ping the server, get an email if it doesn't answer, done. But the gap between "I have a monitor" and "I actually get useful, timely, trustworthy alerts" is where most teams quietly fail โ usually right up until the moment it costs them a very bad day.
What "down" actually means
The naive version of uptime monitoring is: does the homepage load? That's a start, but it misses almost everything that actually breaks in production.
Your homepage can return a perfect 200 status code while your checkout flow is silently failing because a payment API changed its response format. Your server can be "up" while the database connection pool is exhausted and every real request times out.
Your app can look fine from your office in Lucknow and be completely unreachable for users in Sรฃo Paulo because of a routing issue at your CDN.
A useful uptime monitor checks the things your users actually depend on, not just the things that are easiest to check. That usually means going a layer deeper than "is the server responding" into "can a real transaction complete."
How the checks actually work
Most monitoring tools lean on a handful of check types, and knowing the difference matters when you're deciding what to watch:
HTTP/HTTPS checks hit a URL and look at the status code, response time, and sometimes the page content itself (to catch a "200 OK" page that's actually showing an error message).
Ping and ICMP checks confirm a server exists on the network at all โ useful for infrastructure, less useful for telling you if an application is actually working.
TCP/port checks confirm a specific service (a database, a mail server, an API gateway) is accepting connections on its expected port.
DNS monitoring watches whether your domain still resolves correctly โ an often-overlooked failure point, since a broken DNS record takes everything down at once, no matter how healthy your servers are.
SSL certificate and domain expiry monitoring catches the embarrassingly common failure mode of an expired certificate turning your entire site into a security warning overnight.
Heartbeat monitoring flips the model: instead of your monitor pinging your service, your service pings the monitor on a schedule (great for cron jobs and background workers โ if the heartbeat doesn't arrive, something silently stopped running).
Synthetic transaction monitoring scripts an actual user journey โ log in, add an item to a cart, complete checkout โ and flags it when any step breaks, which is the closest thing to "a real customer just tried this and it failed."
Good monitoring setups usually combine several of these rather than betting everything on one URL check.
Internal checks vs. external monitoring
There's a distinction that trips people up constantly: the health checks running inside your infrastructure are not the same thing as uptime monitoring, and one doesn't substitute for the other.
A Kubernetes liveness probe, a readiness probe, or a load balancer health check exists to answer one narrow question for your own infrastructure: "should traffic keep going to this specific instance, or should it be restarted or pulled out of rotation?"
These checks run from inside your network, fire every few seconds, and are wired directly into automatic recovery โ an unhealthy instance gets replaced before a human ever hears about it.
External uptime monitoring answers a completely different question: "can the outside world actually reach this service at all?" It runs from data centers you don't control, over the same public internet your users are on.
That's the only way to catch failures that never show up internally โ a DNS problem, a firewall misconfiguration, a certificate expiring, or your entire cloud region losing external connectivity while every instance inside it reports itself as perfectly healthy.
Teams that only have internal probes get blindsided by exactly this kind of failure, because nothing inside the cluster ever looked unhealthy. Teams that only have external monitoring lose the fast, automatic recovery that internal probes provide. You want both, doing two different jobs.
What a basic check actually looks like in code
All of this sounds abstract until you see how little code it takes to do the simplest version yourself. Here's a small Python script that checks a URL on a loop, waits for a couple of consecutive failures before crying wolf, and posts an alert to Slack when it does:
import time
from datetime import datetime, timezone
import requests
URL = "https://yourapp.com/health"
SLACK_WEBHOOK = "https://hooks.slack.com/services/XXX/YYY/ZZZ"
CHECK_INTERVAL_SECONDS = 60
TIMEOUT_SECONDS = 10
FAILURE_THRESHOLD = 2 # consecutive failures before alerting
def check_url():
start = time.time()
try:
response = requests.get(URL, timeout=TIMEOUT_SECONDS)
elapsed_ms = round((time.time() - start) * 1000)
return response.status_code == 200, response.status_code, elapsed_ms
except requests.RequestException as exc:
return False, str(exc), None
def send_alert(status, elapsed_ms):
timestamp = datetime.now(timezone.utc).isoformat()
text = f":red_circle: {URL} looks down โ status: {status}, checked at {timestamp}"
requests.post(SLACK_WEBHOOK, json={"text": text}, timeout=TIMEOUT_SECONDS)
def monitor():
consecutive_failures = 0
while True:
is_up, status, elapsed_ms = check_url()
now = datetime.now(timezone.utc).isoformat()
if is_up:
if consecutive_failures >= FAILURE_THRESHOLD:
print(f"{now} recovered after {consecutive_failures} failed checks")
consecutive_failures = 0
print(f"{now} OK โ {elapsed_ms}ms")
else:
consecutive_failures += 1
print(f"{now} FAIL ({consecutive_failures}/{FAILURE_THRESHOLD}) โ {status}")
if consecutive_failures == FAILURE_THRESHOLD:
send_alert(status, elapsed_ms)
time.sleep(CHECK_INTERVAL_SECONDS)
if __name__ == "__main__":
monitor()If Node.js is more your stack, the same idea is just as short using the built-in fetch (Node 18+):
const URL = "https://yourapp.com/health";
const SLACK_WEBHOOK = "https://hooks.slack.com/services/XXX/YYY/ZZZ";
const CHECK_INTERVAL_MS = 60_000;
const FAILURE_THRESHOLD = 2;
let consecutiveFailures = 0;
async function checkUrl() {
const start = Date.now();
try {
const res = await fetch(URL, { signal: AbortSignal.timeout(10_000) });
return { isUp: res.status === 200, status: res.status, elapsedMs: Date.now() - start };
} catch (err) {
return { isUp: false, status: err.message, elapsedMs: null };
}
}
async function sendAlert(status) {
const text = `:red_circle: ${URL} looks down โ status: ${status}, checked at ${new Date().toISOString()}`;
await fetch(SLACK_WEBHOOK, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ text }),
});
}
async function monitor() {
const { isUp, status, elapsedMs } = await checkUrl();
const now = new Date().toISOString();
if (isUp) {
if (consecutiveFailures >= FAILURE_THRESHOLD) console.log(`${now} recovered`);
consecutiveFailures = 0;
console.log(`${now} OK โ ${elapsedMs}ms`);
} else {
consecutiveFailures += 1;
console.log(`${now} FAIL (${consecutiveFailures}/${FAILURE_THRESHOLD}) โ ${status}`);
if (consecutiveFailures === FAILURE_THRESHOLD) await sendAlert(status);
}
}
setInterval(monitor, CHECK_INTERVAL_MS);Two things worth noticing about both scripts, because they double as a preview of everything the rest of this article gets into: they only check from wherever the script happens to be running, and they only handle one failure mode (a bad status code or a timeout). That's exactly the gap that dedicated tools close โ checking from several regions before deciding something is really down, and covering DNS, SSL expiry, TCP ports, and full user journeys, not just a single HTTP request.
A script like this is a perfectly good starting point for a side project or a single service you want quick visibility into. It's not a replacement for real monitoring on anything revenue-generating.
What should /health actually check?
Both scripts above hit a /health endpoint, and it's worth pausing on what that endpoint should actually do, because "just return 200" is a trap teams fall into constantly.
A shallow health check just confirms the process is alive and the web server is accepting connections. It's fast and it's honest about one thing: your app hasn't crashed. It tells you nothing about whether the app can actually do its job.
A deep health check goes further and verifies the things the app actually depends on โ can it reach the database, the cache, the queue, the third-party API it can't function without.
This is a far more useful signal, but it comes with a real trap: if the deep check itself has no timeout, one slow dependency makes your health check slow, which can make a load balancer think the whole instance is unhealthy and yank it out of rotation โ turning a minor blip into a self-inflicted outage.
The practical fix most teams land on is running two separate endpoints: a fast, shallow one for load balancers and Kubernetes probes that need a quick yes/no answer many times a minute, and a separate, slightly heavier one for external monitors that actually checks dependencies โ with tight timeouts on each dependency check so a slow database doesn't cascade into a false "down."
And since health endpoints are often reachable without authentication, keep the response boring: a status and maybe a version number, never stack traces, internal hostnames, or anything else useful to someone probing your system from the outside.
The metrics that actually matter
"99.9% uptime" gets thrown around constantly, but very few people stop to translate that percentage into something concrete. It's worth doing once, because the difference between the nines is enormous:
Uptime target | Downtime allowed per year | Downtime allowed per month |
|---|---|---|
99% | ~3.65 days | ~7.3 hours |
99.9% | ~8.76 hours | ~43.8 minutes |
99.95% | ~4.38 hours | ~21.9 minutes |
99.99% | ~52.6 minutes | ~4.4 minutes |
99.999% | ~5.3 minutes | ~26 seconds |
Notice how brutal the jump from 99.9% to 99.99% actually is โ you go from being allowed almost nine hours of downtime a year to barely fifty minutes.
Chasing extra nines gets exponentially more expensive in engineering effort, which is exactly why serious teams don't pick a target arbitrarily; they decide what level of reliability their users and revenue genuinely require, then build (and budget) toward that number.
Beyond the uptime percentage itself, a few other numbers tell you far more about how well your operation actually handles failure:
MTTD (mean time to detect) โ how long between something breaking and you finding out. This is the number monitoring exists to shrink.
MTTR (mean time to resolve) โ how long between detection and the fix actually landing.
Response time, at percentiles, not averages โ an average can hide the fact that 5% of your users are waiting eight seconds for a page load. Look at p95 and p99, not just the mean.
Alerts that people actually act on
A monitor that fires alerts nobody trusts is worse than no monitor at all, because it trains your team to ignore the pager.
This is the single most common way uptime monitoring quietly fails: false positives from checking a single location, no escalation path, and alerts that all land in the same channel with the same urgency regardless of whether it's a total outage or a one-off timeout.
A few things fix most of this:
Check from multiple regions before alerting. A blip that only one monitoring location sees is often a regional network hiccup, not a real outage. Confirming from two or three locations before firing an alert cuts false alarms dramatically.
Set an escalation policy, not a single contact. If the first person doesn't acknowledge within a few minutes, it should automatically escalate to the next person, then the next. Nobody should be able to sleep through an outage because their phone was on silent.
Match the alert channel to the severity. A Slack message is fine for "response time crept up." A full outage should go to phone calls, SMS, or a dedicated on-call tool โ something that actually wakes a person up.
Route by ownership. The person who gets paged should be able to do something about the specific thing that broke. Paging your whole engineering team for every blip guarantees people start tuning it out.
One more habit worth building early: schedule maintenance windows before planned deploys or infrastructure work, so your monitor doesn't page the whole team over downtime you caused on purpose. Most tools let you mute alerts for a specific window without pausing the checks themselves, which matters โ you still want to see whether the deploy actually restored service on schedule, you just don't want it treated as an incident.
The two failure modes to avoid are symmetrical: forget to schedule the window and you train your team to distrust real alerts because half of them turn out to be routine deploys; forget to end it on time and a genuine outage that overlaps with "maintenance" goes completely unnoticed.
Status pages: the part people forget
When something does go down, your users will find out one way or another โ the only real choice you have is whether they find out from you or from each other on social media.
A public status page, updated the moment you're aware of an issue, does more for customer trust during an incident than almost anything else you can do. It doesn't need to be fancy. It needs to be honest, current, and easy to find, and most monitoring tools can generate and update one automatically as part of the same setup.
Where uptime monitoring stops
It's worth being clear about what uptime monitoring is not, because the terms get blurred together in most tool marketing pages.
Uptime/synthetic monitoring โ everything covered so far โ answers "is it up, and how fast did it respond." It's cheap, it's simple, and it's usually the very first alarm to go off.
APM (Application Performance Monitoring) answers "why is it slow or erroring," by tracing individual requests through your code, database queries, and downstream services so you can find the exact line or query causing the problem. You reach for APM once uptime monitoring has already told you something's wrong and you need to know where.
RUM (Real User Monitoring) answers "what did actual visitors experience," by collecting performance data from real browsers and devices in production. It catches things a synthetic check run from a data center never will โ a specific mobile carrier, a specific device, a specific country having a noticeably worse experience than everyone else.
None of the three replaces the others.
Most teams start with uptime monitoring because it's the fastest and cheapest to set up, add APM once the system is complex enough that "it's down" stops being specific enough to act on, and add RUM once the actual experience of real users, not just a synthetic check's, starts to matter to the business.
Picking a tool without regretting it in six months
The market here is crowded, and it splits roughly into three tiers:
Dedicated uptime/synthetic monitors โ UptimeRobot, Pingdom, StatusCake, Better Stack, HetrixTools, Checkly, Cronitor, and similar tools. These are built specifically for this job: fast setup, multi-location checks, status pages, and alerting, usually with a workable free tier and affordable paid plans.
Full observability platforms โ Datadog, New Relic, Site24x7. These fold uptime checks into a much larger product that also covers logs, traces, infrastructure metrics, and application performance. Worth it if you're already buying (or need) the bigger platform; overkill if uptime checks are all you're after.
Self-hosted โ Uptime Kuma is the standout here: free, open-source, and popular with teams that want full control and don't mind running the infrastructure themselves.
A practical checklist when comparing options:
Check frequency (30 seconds vs. 5 minutes is a real difference if downtime costs you money)
Number of check locations, and whether it confirms from multiple before alerting
The check types it actually supports โ HTTP is table stakes; look for TCP, DNS, SSL/domain expiry, and heartbeat/cron support too
Alert channels โ does it support the ones your team will actually respond to (phone calls and SMS, not just email)?
A built-in, hosted status page
Whether the pricing scales sanely as you add more monitors
One more thing worth factoring in that people rarely think about: whether the vendor itself is going to stick around. In March 2026, one of the more popular free uptime tools, Freshping, shut down entirely โ a reminder that the tool you build your alerting around also needs to still exist next year.
Favor tools with a track record, an active team, and (ideally) an easy way to export your monitor configuration if you ever need to leave.
Practices worth actually adopting
A few habits separate teams that catch problems early from teams that find out from their customers:
Monitor the journey, not just the homepage. Check the login flow, the checkout flow, the API endpoints your app actually depends on โ not just whether the root URL returns a 200.
Watch your dependencies, not just yourself. Your uptime is only as good as your weakest third-party dependency โ your payment processor, your CDN, your DNS provider, your cloud region. If you don't know which external services your app can't function without, that's the first thing to map out.
Don't let expiry dates sneak up on you. SSL certificates and domain registrations expiring unnoticed are still a shockingly common cause of "outages" that were entirely preventable with a 30-day warning.
Run a real (blameless) post-mortem after every significant incident. The goal isn't to assign blame, it's to find out why detection or recovery took as long as it did, and fix that specific gap before the next incident.
Test your alerts, not just your monitors. An escalation policy nobody has ever triggered on purpose is an escalation policy you're hoping works. Run a drill occasionally.
Why this isn't optional anymore
The financial case for uptime monitoring isn't subtle.
Industry estimates on the cost of downtime vary a lot depending on company size and how digital-dependent the business is โ from a couple hundred dollars a minute for a small operation up to well into five figures a minute for a large enterprise โ but the pattern is consistent across every estimate: it's never cheap, and it's almost always more expensive than the monitoring that would have caught it early.
You don't need a hypothetical to see why this matters. In 2024, a single faulty software update from CrowdStrike triggered outages across airlines, hospitals, and banks worldwide, with estimates putting the combined cost to Fortune 500 companies in the billions over just a few days.
A few years earlier, a single misconfiguration at the CDN provider Fastly took down a huge swath of the internet at once โ the Guardian, the New York Times, Reddit, Amazon, and government sites all went dark within minutes of each other, because they all quietly depended on the same piece of infrastructure.
More recently, outages traced back to major cloud and CDN providers have repeatedly shown the same lesson: even companies with excellent engineering teams get taken down by a dependency they don't control and, often, don't even realize they have.
None of those companies lacked resources. What separates a five-minute blip from a headline-making disaster is almost always the same thing: how fast the team found out, and how fast they could act on it.
The bottom line
Uptime monitoring will never be the most exciting line in your budget or the feature you show off in a demo. Nobody gets promoted for the outage that got caught and fixed in ninety seconds instead of ninety minutes.
But that's exactly what makes it worth setting up properly โ it's cheap, unglamorous insurance against the one category of problem that reliably costs real money, real trust, and a genuinely terrible day.
If you don't have a monitor watching your most important user flows right now, that's worth fixing before you do anything else on this list. Everything else here is about doing it well. That first step is about not finding out from Twitter.
Comments (0)
Login to post a comment.