Someone signs up for a monitoring service, types in the address of the machine they care about — 192.168.18.1, or localhost:8080, or nas.local — and waits. The check fails immediately and keeps failing. Nothing about the error explains why, so the reasonable conclusion is that the monitoring service is broken. It isn’t. It is being asked to do something no service on the public internet can do, and the fix is to turn the problem around.
An external uptime check works by connecting to your machine from somewhere on the internet. That requires an address the internet can route to. These are not:
| Address | What it actually means |
|---|---|
192.168.x.x | A private address on your network. Millions of networks use the same numbers. |
10.x.x.x · 172.16–31.x.x | Also private. Same story, bigger ranges. |
127.0.0.1 · localhost | “This machine.” From our servers, that means our machine, not yours. |
nas.local · printer.lan | Names that only resolve inside your own network. |
169.254.x.x | Link-local. Never routable, and often a cloud metadata endpoint. |
The point that catches people out: this has nothing to do with which monitoring service you pick. There is no setting, no paid tier and no clever configuration that lets a server in a datacentre open a connection to a device sitting behind your home router. The address is not reachable from outside the building. That is what “private” means.
What you see instead is one of two failures, and they look different enough to be confusing:
DOWN Get "https://192.168..18.1": dial tcp: lookup 192.168..18.1: no such host # a typo — an extra dot makes it a hostname, and no such name exists DOWN Get "https://192.168.18.1": dial tcp 192.168.18.1:443: i/o timeout # the typo fixed — and now it fails forever instead, which is worse, # because it looks like the kind of failure that might come back
Fixing the typo swaps a loud failure for a quiet permanent one. That is the trap.
The machine you want watched can reach the internet — it downloads updates, it browses, it syncs. What it can’t do is accept connections from it. So stop trying to connect inward and let the machine reach out instead. Two shapes of that, depending on what you’re protecting.
A small program on the machine pushes its vitals outward over ordinary HTTPS every few seconds. Nothing listens, no port is opened, no firewall rule is added, and no router configuration changes. It works behind NAT, behind CGNAT, on hotel wifi, and on networks you have no administrative control over.
# Linux / macOS curl -fsSL https://pylonmon.com/beacon.sh | sh # Windows (PowerShell as Administrator) irm https://pylonmon.com/beacon.ps1 | iex
About twenty seconds later the machine shows up as a monitor, reporting what it can see from the inside — which is strictly more than any external check could tell you anyway:
Both machines are on a home LAN. Neither is reachable from the internet. Both are monitored.
There is a second, subtler benefit. An external check can only ever tell you “it answered” or “it didn’t.” An agent inside the machine can tell you the disk is at 94% a week before that becomes an outage. The thing you couldn’t monitor from outside turns out to be better monitored from inside.
And if the machine goes quiet, that silence is the alert. This is the part people miss: a push agent still detects a dead machine. If a box that has checked in every twenty seconds for a month stops, something is wrong — power, network, kernel panic, theft. You get paged for exactly the failure an external check was supposed to catch, without ever needing to reach the machine.
If what you care about is a task rather than a machine — a nightly backup, a sync script, a cron job, a service that processes a queue — you don’t need an agent. Have the thing tell you it ran:
#!/bin/sh restic backup /srv /home # ...the actual work curl -fsS https://pylonmon.com/hb/YOUR-ID # tell us it finished
You tell the monitor how often to expect that ping. If it doesn’t arrive on schedule, you get alerted. That single line covers three failures an external check cannot see at all: the job errored, the job never started, and the machine that runs the job is gone.
Put the curl at the end of the script, after the work. A heartbeat sent at the start only proves the script began.
| What you want to know | Use |
|---|---|
| Is this machine alive and healthy? | Agent |
| Is the disk about to fill up? | Agent |
| Did the backup actually run last night? | Heartbeat |
| Is my public website up? | Ordinary URL check — that one is reachable |
| Is an internal service responding correctly? | Heartbeat from a script that tests it locally |
That last row is the one worth stealing. If you have a service on a private address and you want more than “the host is up”, write four lines that test it from inside the network and heartbeat only on success:
#!/bin/sh # test the private service locally, where it IS reachable if curl -fsS --max-time 10 http://192.168.18.1/health | grep -q ok; then curl -fsS https://pylonmon.com/hb/YOUR-ID # healthy: check in fi # unhealthy: say nothing, and the missing heartbeat becomes the page
The logic reads backwards at first and it is worth sitting with: saying nothing is how you raise the alarm. That property is what makes it robust. A script that tries to send you an alert when things break can’t send anything when the network is down, the power is out, or the script itself crashed. A script that stays silent when things break is correct in all of those cases, because silence is exactly what the outside world observes.
You can make a private machine externally reachable. It is usually a bad trade, and it’s worth being specific about why rather than just saying “insecure”.
The push agent avoids all of it. Nothing is exposed, nothing is forwarded, and the failure mode of the monitoring path is the same as the failure mode you’re watching for.
If the internet can reach it, check it from outside. If it can’t, have it reach out — an agent for a machine, a heartbeat for a job. There is no third option, and any service that implies otherwise is selling you a port forward with extra steps.
PylonMon does both, and the free plan covers three machines with no card. If you try to point a URL check at a private address, it now stops you at the point of saving and says which of the two you want — because watching someone discover this over three days of silent failures was what prompted writing it down.
Start free — 3 machines, no card →
Related: how to get alerted when a cron job fails · Monitor 3 servers on the free plan