A status page is the cheapest trust-building tool a Minecraft server can own, and the easiest one to build wrong. Most owners wire a port check to a green dot, then discover during the first real outage that the dot stayed green while players timed out at login. This guide is for owners, admins, and staff who want a page that reports the truth.
It is built on measurement. We ran a 90-second probe loop against a Paper 26.2 server on Ubuntu in our lab, a timed TCP connect to the game port and an RCON list call, logging every sample. We also compared the four realistic ways to publish a status page and read each project’s documentation.
Port Open Is Not Healthy: Three Layers to Check
A Minecraft server can accept a TCP connection on port 25565 while being completely unplayable. The kernel backlog and Netty’s accept threads keep working when the main game thread hangs on a world save, a plugin deadlock, or a garbage-collection spiral. Logins sit at “Joining world” and never proceed, yet a socket connect still finishes in under a millisecond. That gap is why port checks produce false green.
- Layer 1, port reachable. A socket connect to 25565 finishes. This catches a dead JVM, a crashed container, a firewall edit, or a suspended VPS. Necessary, cheap, and proof of almost nothing.
- Layer 2, tick loop responsive. The server answers a request in plausible time. This catches a hung main thread or a plugin blocking on I/O. A reply in four seconds is worse news than no reply: the server is alive and drowning.
- Layer 3, world reacting. An RCON command returns real state, such as
listwith names and counts. This catches a frozen world and gives you a number to chart.
Most public status pages stop at layer one, the layer players never experience. If you can afford one check beyond the port, make it an RCON round trip; the protocol is documented at wiki.vg/RCON and runs on a port you already control.
What 90 Probes Measured on a Paper Server
The lab server was a Paper 26.2 build on Ubuntu with no plugins, sampled once per second for 90 seconds from localhost. Each sample did a timed TCP connect to 25565 and a timed RCON list, recording success and latency.
| Check | Samples | Success rate | Median | p95 |
|---|---|---|---|---|
| TCP connect to 25565 | 90 | 100% | 0.41 ms | 0.65 ms |
RCON list round trip | 90 | 100% | 3.08 ms | 4.81 ms |
The absolute numbers matter less than their scale. A full port-plus-command probe costs about 3.5 milliseconds and no measurable CPU, so at 60-second intervals it is 1,440 probes and roughly five seconds of work per day. “We cannot afford to monitor often” is the wrong conversation.
The two decisions that shape the design are where the probe runs and what a failure means. A probe on the same box cannot see the failures players complain about most: a dead ISP uplink, a route flap, or a saturated NIC. Those look healthy from inside the machine, while an off-site probe travels the same path your players do. Run a fast local probe for ticks and a slower outside one for the network.
A Drop-In Probe You Can Run Anywhere
This script uses only the standard library and writes one JSON object per line, so the log stays append-only and readable by a static page. Set RCON_PASS and point HOST at your public address when running it elsewhere.
import json, os, socket, struct, time, datetime
HOST, PORT, RCON_PORT = "127.0.0.1", 25565, 25575
PASS = os.environ["RCON_PASS"]
def pkt(i, kind, body):
payload = struct.pack("<ii", i, kind) + body.encode() + b"x00x00"
return struct.pack("<i", len(payload)) + payload
def tcp_probe():
t = time.perf_counter()
try:
socket.create_connection((HOST, PORT), timeout=2).close()
return True, round((time.perf_counter() - t) * 1000, 2)
except OSError:
return False, None
def rcon(cmd="list"):
t = time.perf_counter()
try:
with socket.create_connection((HOST, RCON_PORT), timeout=2) as s:
s.sendall(pkt(1, 3, PASS)); s.recv(4096)
s.sendall(pkt(2, 2, cmd))
reply = s.recv(4096)[12:-2].decode(errors="replace")
return True, round((time.perf_counter() - t) * 1000, 2), reply
except OSError:
return False, None, ""
tcp_ok, tcp_ms = tcp_probe()
rcon_ok, rcon_ms, body = rcon()
row = {"ts": datetime.datetime.now(datetime.timezone.utc).isoformat(timespec="seconds"),
"tcp_ok": tcp_ok, "tcp_ms": tcp_ms, "rcon_ok": rcon_ok,
"rcon_ms": rcon_ms, "list": body, "version": "26.2"}
open("/var/log/mc-probe.jsonl", "a").write(json.dumps(row) + "n")
Schedule it with cron as * * * * * /usr/bin/python3 /opt/mc-probe/probe.py, with a systemd timer every two minutes, or in a container via docker run --network host. A $4 VPS is enough for the off-site copy, and it can host the public page too. Because the output is one JSON line per sample, a single HTML template can read the last 1,440 lines and draw a 24-hour uptime bar without a database. Keep that log somewhere durable, the habit behind How to Set Up Automated Minecraft Server Backups on Linux.
Hand-Rolled, Uptime Kuma, or Commercial: The Tradeoffs
| Option | Cost | Setup effort | Minecraft-aware | Alerting |
|---|---|---|---|---|
| Hand-rolled probe plus static page | VPS only, ~$4/mo | 2 to 4 hours | Full, you write the checks | Whatever you script |
| Uptime Kuma, self-hosted | VPS or spare box | 20 to 30 minutes | TCP, HTTP keyword, push monitors | 90+ channels, including Discord |
| Pterodactyl panel status | Included with panel | None | Container and process state only | Panel and email |
| Commercial monitors | Free tier, paid from ~$7/mo | 5 minutes | Port and keyword checks | Email, SMS, webhooks |
Uptime Kuma is the pragmatic default for most communities. It is a single Node application, it stores history in SQLite, it ships a clean public status page, and its monitor types cover TCP ports, HTTP responses, and keywords, which is enough for layers one and two if you expose a small health endpoint. Its weakness is shared with every self-hosted monitor: if it runs on the machine it watches, it dies with it.
Commercial monitors are the least effort and the least insight. They will check that port 25565 answers from three continents, which spots regional routing problems, but they cannot tell you the tick loop is stalled. Pterodactyl’s built-in status is a container view: it says the process is running, which you already knew.
Alerts That Respect Sleep, and What List Pings Really Show
An alert that fires on the first missed packet trains staff to ignore alerts. Stagger the thresholds. Three consecutive failures at a 60-second interval is three minutes of real downtime, worth a Discord ping. Ten minutes deserves a louder message. Twenty minutes with no recovery should reach a phone, and a recovery notice closes the loop.
- Down: layer one failing, three consecutive samples, webhook plus a status-page banner.
- Cannot tick: port open but RCON failing or slower than two seconds, warning at five minutes.
- Degraded: RCON answering but MSPT above 50 for ten minutes, no page, just a channel note.
- Recovered: always send it, even at night.
Suppress alerts during planned work. A maintenance window that mutes the monitor for the restart and the two minutes after it removes most false pages, and How to Set Up Scheduled Restarts Without Angering Players shows how to make restarts predictable. Keep the window short.
A ping is not a status page
Server list ping and a status page answer different questions. The ping is the handshake your client performs when it refreshes the multiplayer list, documented at wiki.vg/Query: it returns the MOTD, version, protocol number, and player count. It says nothing about whether a login will succeed, and it is rate-limited and cached by clients and list sites. Because it is answered before the world is involved, it stays green while the tick loop is frozen. Players see a nice MOTD, then get stuck at “Joining world”.
Pros and Cons of Running Your Own Status Page
- Pro: it tells the truth. You choose the checks, so you report tick health and player counts instead of a decorative port light.
- Pro: it costs almost nothing. About 3.5 ms per sample, one small VPS, one cron entry.
- Pro: it is a support tool. When a player says the server is down, you link the page.
- Con: you own the outages. The monitor, the page, and the DNS break eventually, usually during the incident they were meant to catch.
- Con: it needs tuning. Thresholds and routing are ongoing work.
Common Mistakes That Make Monitoring Useless
- Monitoring from the same box you host on. The monitor shares the failure domain, so a dead network takes both down and you learn nothing.
- Probing so often you look like an attack. Dozens of parallel connections can trip host anti-DDoS rules. Stay at 30 to 60 seconds.
- Ignoring the restart pattern. Uptime alone hides a server that restarts every four hours and recovers in 40 seconds.
- Treating 99.9 percent as a promise. It is a measurement of the past. The same figure still allows 43 minutes of downtime a month.
- Forgetting the human path. An alert that only lands in an unread channel is a log entry, not an alert.
Two habits separate a page people trust from one they ignore. Log MSPT alongside uptime so you chart health, not existence, and write the server and probe versions into every row. When a Paper update introduces a stutter, that column dates the change.
Frequently Asked Questions
A ping is the handshake your client sends to refresh the multiplayer list. It returns the MOTD, version, and player count, and it is rate-limited and cached. A status page probes the port and the game loop repeatedly, so it reports downtime a ping cannot see.
Yes. Add a TCP port monitor for 25565 to catch a dead server, and a push monitor fed by your own probe to catch a stalled tick loop. Host it on a different machine than the server it watches.
Every 30 to 60 seconds suits almost everyone. Our probe cost about 3.5 ms, so the limit is not load but your host’s anti-DDoS rules and alert noise.
The socket is accepted by the kernel and by Netty’s accept threads, which keep working while the main game thread is blocked. A port check only proves something is listening. Probe an RCON command and time it to prove the tick loop answers.
Start With One Probe From Outside the Box
You do not need a monitoring stack to begin. Add the RCON check to a script, run it from a cheap VPS, log one JSON line per minute, and point a single page at the log. That takes an afternoon and beats every green dot that only tests a port. Once the page is trustworthy, layer alerts on top, then add MSPT and restart counts so the graph shows health. If you run a How to Set Up a "Fallback" Server: Prevent Downtime, monitor both addresses.

Leave a Reply