Skip to content
Blog

External uptime monitoring for a Hetzner server: why SysMon is not enough on its own

Hetzner SysMon checks ping, ports and HTTP from Hetzner's own network and proves only a status code. What it misses and how to add an outside check.

If you run a dedicated or Robot server on Hetzner, you have almost certainly seen SysMon. It is free, it is one form in the Robot panel, and it is the first thing most people set up because it is right there. SysMon is not the whole answer, and the gap between "SysMon is green" and "my server is actually fine" is exactly where outages hide.

What does Hetzner SysMon actually check?

Hetzner's System Monitor (SysMon) checks ping, ports, HTTP and a few mail and DNS protocols from Hetzner's own network. The supported checks are ping, TCP/UDP port, HTTP (accepting only 200, 301, or 302 as healthy), FTP, POP, IMAP, SMTP, and DNS. You set the interval and a reminder cadence, and it requires two consecutive failures before it alerts, which cuts false alarms at the cost of a delay on the first real one.

SysMon is a genuinely useful floor. A port check on 443 catches "nginx crashed and nothing is listening." An http check catches a web server that answers but returns a 5xx. For a single box with no redundancy, having any external eyes on it beats having none, and SysMon costs nothing to turn on.

What can SysMon not tell you about your server?

SysMon cannot tell you whether the app works, and it cannot tell you what customers outside Hetzner see.

SysMon only proves a status code. Its HTTP check accepts 200, 301, or 302 and nothing else, per the HTTP status code definitions. Three cases all read as healthy to SysMon:

  • A page that returns 200 with a stack trace instead of your homepage.
  • A health endpoint that always returns 200 regardless of what is broken behind it.
  • An API that answers 200 with an empty or malformed body.

The check confirms that something answered with an acceptable code. That is a real signal and a narrow one.

SysMon checks from one place: Hetzner's own network. Its monitoring pool queries your server from Hetzner's infrastructure, and the docs describe granting pool.sysmon.hetzner.com access through your firewall. The docs say to look the pool's addresses up in DNS, so resolve the hostname rather than hardcoding addresses in a firewall rule. The probe comes from the same provider, often the same data center region, as the server being checked. Three problems never show up, because SysMon never leaves the neighborhood:

  • A routing problem between your box and the rest of the internet.
  • A firewall rule that blocks traffic from outside Hetzner's own ranges but not from inside them.
  • A regional peering issue that makes your site slow or unreachable for customers in Singapore while it answers Hetzner's own probe in Nuremberg instantly.
Failure SysMon sees it Outside check sees it
Process dead, port closed Yes Yes
200 response with the wrong body No Yes, with a body assertion
Routing or peering problem for one region No Yes, from several regions
Firewall blocks traffic from outside Hetzner No Yes

Both gaps share one cause: SysMon confirms the server is up from the vendor's point of view. It cannot confirm your customers, who are not on Hetzner's network and are not satisfied by a bare 200, are actually getting a working site.

How do you add an outside check to a Hetzner server?

Keep SysMon and add a check that runs from outside Hetzner. SysMon is free and catches the box-is-dead case fast. Add a check that runs from outside Hetzner entirely, against what a real visitor sees, on top of it.

An HTTP check against your real endpoint, from several regions. On RealUptime Monitor, a check hits the actual URL a visitor loads, not just any 200-yielding path. You can assert on more than the status code: response time thresholds, and a specific string or pattern in the body to catch "the page loaded but the content is wrong."

Running from multiple regions turns a single green or red into a map. A check that fails from Singapore but passes from Virginia and Frankfurt points to a routing or peering problem on one path to your server, not a dead box.

A TCP check for anything that is not HTTP. The server may also run Postgres, Redis, an SMTP relay, or an SSH bastion on a non-standard port. A TCP check confirms the port accepts connections from outside your own network. That is a different, complementary question to whether nginx is up.

A status page, if anyone besides you needs to know. Hetzner's Robot panel has no way to tell your team or your customers what is happening. A public or internal status page turns "I noticed the alert" into "everyone affected already knows," without a Slack message you have to type by hand mid-incident.

Why does SysMon wait for two failures before alerting?

SysMon requires two consecutive failures because one failed check is cheap and usually noise. Typical causes are a momentary network blip on the monitoring pool side, a packet lost between the probe and your box, or a brief CPU spike from a cron job. If every failed check paged you, most pages would be noise, and noise is how real alerts get ignored. Requiring the second consecutive failure before Hetzner notifies you filters that noise out, at the cost of however long your check interval is, twice over, before you hear about a real outage.

An outside HTTP or TCP check follows the same logic for the same reason, and it is worth setting the interval and the failure threshold deliberately rather than leaving the defaults unexamined. A one-minute interval with a two-failure threshold means at most two minutes between a real outage starting and an alert firing. A five-minute interval with the same threshold means up to ten. A server that only you depend on might be fine at five minutes. A server that customers hit directly is worth the marginally higher check volume of the tighter interval.

In what order should you troubleshoot a Hetzner alert?

Compare SysMon and the outside check first. When an alert fires, whether from SysMon or from an outside check, the order you check things in matters more than how fast you check them. A reasonable sequence:

  1. Check whether SysMon and the outside check agree. If both are down, the problem is almost certainly the box itself: the process crashed, the server is out of memory, or the network interface is down. SSH in (or use the Robot panel's remote console if SSH itself is unreachable) and look at systemctl status, docker ps, or whatever supervises your app.
  2. If only the outside check is down and SysMon is green, suspect the network path, not the process. A firewall rule that changed, a DNS record pointed somewhere stale, or a regional routing issue between your outside monitor and the server are the likely causes, since the process itself is answering Hetzner's own probe fine.
  3. If only SysMon is down and the outside check is green, suspect the reverse: something specific to Hetzner's monitoring pool reaching your box, most often a firewall rule that was tightened without adding the SysMon IP ranges back in.

The general form of this reasoning is in what a TCP port check proves, and what it does not. A check from one vantage point cannot distinguish a dead process from a broken path to it. Two checks from two vantage points can, and the disagreement between them is itself the diagnosis.

What does a complete setup look like?

A reasonable setup for a single Hetzner box running a web app and a database has four parts:

  • SysMon port checks on 443 and 5432, as the fast, free layer that catches "the process died" in under a minute.
  • An HTTP check on RealUptime against the real app URL from at least two regions outside Hetzner's network, to catch the routing and content problems SysMon's status-code check cannot see.
  • A TCP check on the database port, if anything outside the box connects to it directly.
  • A heartbeat monitor for scheduled work such as a nightly backup or a cron-driven export, so a silently failing cron job pages you instead of going unnoticed for a month.

The HTTP and TCP checks need nothing installed on the server. They run from RealUptime's side, pointed at whatever is already public.

Host-level checks cover disk, memory, or a service that only listens on localhost. The Monitor agent runs as a small process on the box and reports alongside the outside checks. People running Coolify on Hetzner already use the same pattern, covered in our Coolify monitoring guide.

Hetzner also publishes its own status page, which covers platform incidents such as SysMon maintenance, but not whether your app answers. An external HTTP or TCP check from regions Hetzner does not control closes that gap and takes a few minutes to set up on top of what you already have.