Game Server Monitoring with Prometheus and Grafana: Metrics, Queries, and Alerts That Predict Outages

Most game server outages are visible minutes before players notice. Tick time creeps up, memory climbs, disk await spikes — and if nobody is watching those three signals on a dashboard, you find out when Discord lights up. This is the practical setup: what to export, which queries matter, and which alerts are worth waking up for.

Architecture: Keep It Boring

One Prometheus instance, one node_exporter and one game exporter per machine, Grafana pointed at Prometheus. Do not over-engineer this with Thanos or Cortex until you are past ten machines. A single Prometheus on 4 vCPU and 8 GB can scrape 50 targets at 15-second intervals comfortably and retain 30 days of data on a few hundred GB.

scrape_configs:
  - job_name: node
    scrape_interval: 15s
    static_configs:
      - targets: ['game-01:9100', 'game-02:9100']
  - job_name: minecraft
    scrape_interval: 10s
    static_configs:
      - targets: ['game-01:9940']

Metrics That Actually Predict Problems

SignalMetric sourceWhy it matters
P99 tick durationGame plugin / RCON exporterDirect player-facing lag; leading indicator of CPU saturation
Heap used vs maxJVM JMX exporterPredicts OOM kill 10–60 min ahead
Disk await (world device)node_exporterChunk gen / autosave contention
TCP retransmitsnode_exporter netstatPacket loss players blame on “server lag”
Player countGame exporterCorrelates load spikes with real demand

Skip CPU utilisation as a primary alert. Processes can sit at 100% on one pinned thread and be perfectly healthy, or sit at 40% total while tick time collapses. Tick duration is the honest signal.

PromQL Queries Worth Bookmarking

P95 tick time over five minutes, per server:

histogram_quantile(0.95, sum by (le, server) (rate(game_tick_duration_seconds_bucket[5m])))

Heap pressure as a fraction of maximum, alerting before GC thrash:

jvm_memory_used_bytes{area="heap"} / jvm_memory_max_bytes{area="heap"} > 0.85

Disk latency on the device holding your worlds:

rate(node_disk_io_time_weighted_seconds_total{device="nvme0n1"}[5m]) > 0.5

A value above 0.5 means the device has more than one I/O in flight on average — saturation. On a healthy NVMe hosting a single survival world you should see well under 0.2 except during generation.

Alerting Rules That Don’t Cry Wolf

  • Critical: tick P99 above 2x target frame time for 5 minutes. Page immediately.
  • Critical: heap above 90% of max for 10 minutes. The OOM killer is coming.
  • Warning: disk await above 0.4 for 15 minutes. Investigate autosave staggering.
  • Warning: TCP retransmit rate above 0.5% for 10 minutes. Check upstream path, not the game.
  • Info: player count above 80% of validated capacity. Time to plan scaling.

Every alert should map to an action. If an alert fires and the correct response is “ignore it,” delete the alert — it trains you to ignore the ones that matter.

Grafana Dashboard Layout

Put one row per server, four panels across: tick time, heap, disk await, network. Above them a single stat panel of total players and a table of any instance currently in alert. Use template variables so you can filter by region or game without duplicating dashboards. Retention of 30 days at 15s resolution is enough to answer “was last Friday’s lag a one-off or a pattern?”

Tying Metrics to Capacity Decisions

After a month you will have the data to answer the only capacity question that matters: is this machine running out of threads, memory, or I/O first? That determines whether the next upgrade is a higher-clock CPU, more RAM, or simply an additional machine. Operators who make that call from dashboards rather than guesswork get far more players per dollar out of their hardware — the same principle behind the provisioning approach outlined at bestdedicatedwebhostingserver.com.

Start with tick time, heap, and disk await. Those three catch the overwhelming majority of real incidents, and you can add exporters for player count and packet loss later without rearchitecting anything.

Leave a Reply