Running a Minecraft server is easy until something slows down and nobody can say why. ServerScope is Etoryx's answer to that gap: a monitoring and observability platform for Paper and Folia servers that turns runtime health into something you can actually look at — live metrics, stored history, alerts, a web dashboard, and a JSON API.
This is the story of how it was designed and the decisions that shaped it.
The server visibility problem
Most performance problems on a Minecraft server are invisible until they hurt. The server feels fine, then a chunk loads, an entity count spikes, or a plugin does too much work during an event, and suddenly players notice the lag.
The default tools answer only the broadest question. A single tick-rate number tells you that something is wrong, not where. Administrators are left guessing between worlds, chunks, and plugins, often restarting the server just to clear a symptom they never diagnosed.
ServerScope starts from a simple premise: an administrator should be able to look at the server and understand its state — right now and over time — without attaching an external profiler or reading raw logs.
What ServerScope sets out to do
The goal is operational visibility on Paper and Folia, delivered through several integrated subsystems rather than a single command:
- collect live metrics continuously and keep a bounded history;
- diagnose problems and raise alerts with clear severity levels;
- expose everything through in-game commands, a web dashboard, and a JSON API;
- stay cheap enough to run continuously in production.
A monitoring tool that meaningfully degrades the thing it monitors has failed. Low overhead is a feature, not a nice-to-have.
Inside the architecture
ServerScope is built as a set of cooperating modules rather than one monolith:
- Collectors gather operational metrics continuously.
- Storage persists data to SQLite — asynchronous, batched, and bounded so writes never block the server and the database cannot grow without limit.
- Diagnostics and alerts analyze the collected data and generate notifications by severity.
- A profiling engine watches a selected set of Bukkit/Paper events.
- A web server serves both the dashboard and the JSON API.
Persistence is what separates a live readout from real observability. Because history lives in SQLite (serverscope-mvp.db) rather than only in memory, trends survive and can be reviewed after the moment has passed — not just glanced at while they happen.
Paper and Folia compatibility
Folia changes the rules. Instead of one main thread, the server is divided into regions that tick independently across a thread pool. Code that assumes a single global main thread is not just slower on Folia — it is incorrect.
ServerScope treats threading as a first-class concern rather than a port target:
| Concern | Paper | Folia |
|---|---|---|
| Scheduling | Global scheduler | Region / global schedulers |
| Shared state | Single main thread | Per-region ownership |
| Safe access | Main thread | Owning region's thread |
The practical rule the plugin follows is to never read or mutate server state from the wrong thread, and to push storage and the web server off the tick path entirely. It targets Paper or Folia 1.21+ on Java 21+.
What the plugin does today
These capabilities are in the project as it stands.
It continuously collects core signals — TPS, MSPT, entity population trends, chunk performance, and plugin event execution profiles. The profiling engine watches events such as player_interact, block_break, block_place, entity_damage, inventory_click, and creature_spawn, flagging the ones that are slow, frequent, or anomalous.
When something crosses a line, the alert system reacts by severity: CRITICAL alerts reach admins in game, while WARN-level findings surface in the console and the web UI — for conditions like low TPS, high MSPT, excessive entities, or unusual chunk activity.
Everything is reachable in three ways: in-game commands, a built-in web dashboard, and a JSON API.
/serverscope
/serverscope status
/serverscope reload
/serverscope report
/serverscope findings
/serverscope alerts
/serverscope web regenerate-token
The web layer binds to 127.0.0.1:8080 by default and is protected by an auto-generated per-server token (regenerated with web regenerate-token). It supports CORS and reverse proxies, applies configurable rate limiting, and caps responses at 2MB of JSON. Access is gated by granular permissions such as serverscope.admin and serverscope.alerts.
Constraints and design decisions
Two constraints shaped most of the work.
The first was overhead. A monitor that taxes the server defeats its own purpose, so the expensive parts are deliberately kept off the hot path: storage writes are asynchronous, batched, and bounded, and the web server runs independently of the tick loop.
The second was correctness under Folia. Supporting regionized threading is not a flag you flip; it is an assumption that runs through the whole codebase. Building with it in mind from the start was far cheaper than retrofitting it later would have been.
Security got the same treatment as a default rather than an afterthought: the dashboard binds to localhost and requires a token out of the box, so exposing it is an explicit choice, not an accident.
Lessons and where to go next
The clearest lesson was that observability is persistence plus restraint. Storing a bounded history turns a momentary readout into something you can actually investigate, and keeping every expensive path off the tick loop is what makes that safe to run on a live server.
If you want to read the code or try it on your own server, both are public:
- Modrinth: ServerScope on Modrinth
- GitHub: Etoryx/ServerScope
If you are planning a Minecraft plugin or mod and want it built with the same care for overhead and platform correctness, get in touch — that is exactly the kind of work Etoryx does.