How long does your server wait for a CPU?
When the machine under your server is busy, its main thread queues for a core and every tick starts late. Nothing in the console shows that queue. TickWait reads it from the kernel once a second and tells you how long the wait was and what caused it: your plan's CPU limit, your server's own threads, or other work on the same cores.
Three numbers your console doesn't have
Every tick, split
On Paper and its forks, each tick is timed and split into CPU work, waiting for a CPU, and the rest — garbage-collection pauses, disk, locks, and on a virtual machine the moments its host took the core away.
Every second, split
Each second the main thread is working, waiting for a core, or neither — idle or blocked. Waiting between ticks doesn't lengthen a tick — it starts the next one late, which is what TPS feels.
Who made it wait
Waiting while the server was throttled is its plan's CPU limit. The rest is shared between the server's own threads and other work on the same cores — other servers, the host — by how much CPU each used.
What the numbers look like
Test setups on our DEV box — an OVH VPS with 4 vCPUs (Intel Core Processor, Haswell), Ubuntu 24.04, kernel 6.8.0-134-generic, cgroup v2. They show how each cause reads — they are not a ranking of anyone's hosting.
One busy loop in a container pinned to one CPU — the kernel counters TickWait reads
| Setup | On a CPU | Waiting for one | of which throttled |
|---|---|---|---|
| Alone on its core | 5,003 ms | 4 ms | 0 ms |
| Sharing the core with another container | 2,502 ms | 2,503 ms | 0 ms |
| Alone, limited to a quarter of a core | 1,249 ms | 3,749 ms | 3,748 ms |
Each row is one 5,000 ms window.
Paper 1.21.11 with 256 villagers on one CPU, running TickWait — its last 60 seconds in each setup
| Setup | Waited | of wanted time | Plan's limit | Own threads | Other work | Tick | TPS |
|---|---|---|---|---|---|---|---|
| Alone on its core | 14.2 ms / s | 4% | 0% | 88% | 12% | 18.5 ms (work 17.4 ms, waiting 0.7 ms) | 20.0 |
| Same, with another container busy on that core | 452 ms / s | 57% | 0% | 12% | 88% | 42.0 ms (work 17.6 ms, waiting 23.9 ms) | 18.6 |
| Same, no neighbour, limited to half a core | 60.8 ms / s | 15% | 67% | 27% | 5% | 19.4 ms (work 15.9 ms, waiting 2.9 ms) | 20.0 |
Leave it running through your busy hours
- Put the TickWait jar in your server's plugins folder and restart.
- After a minute, /tickwait status shows the last 60 seconds.
- Keep it running through the hours your players are on. It keeps up to 7 days of per-minute numbers in plugins/TickWait, across restarts.
- /tickwait report sums everything. /tickwait share turns it into a link you can post.
Needs the server to run on Linux, under Paper, Spigot, Purpur or another Paper fork. The per-tick split needs a Paper build with tick events (current Paper and its forks); elsewhere — Spigot, old Paper — you get the per-second split. Folia isn't supported yet — it has no single main thread to watch.
Run on, 2026-10-11: Paper 1.21.11 on Java 25 — per-second and per-tick split · Purpur 1.21.11 on Java 25 — per-second and per-tick split · Spigot 1.21.11 on Java 25 — per-second split · Paper 1.8.8 (PaperSpigot build 445) on Java 8 — per-second split.
| /tickwait status | The last 60 seconds and the last hour. |
| /tickwait report | Everything measured so far — up to 7 days — with the busiest hour. |
| /tickwait export | Saves exactly what share would upload to plugins/TickWait, sends nothing. |
| /tickwait share | Uploads the report and prints a public link. Asks first. |
| /tickwait shared · unshare <id> | Lists your links; deletes one. |
| /tickwait host <text> · plan <text> | Optional labels shown on a shared report. |
| /tickwait reset confirm | Deletes the measured data and starts over. |
All commands need the tickwait.admin permission (operators by default).
What it reads, and how it splits the waiting
| /proc/self/task/<main thread>/schedstat | How long the main thread ran on a CPU, and how long it was ready to run but waiting for one. Read once a second, and at the start and end of every tick on Paper. |
| cgroup cpu.stat | How long the server's cgroup was throttled because it had used its CPU allowance for the period. |
| cgroup cpu.max (v2) · cpu.cfs_quota_us (v1) | The CPU limit itself, when the host sets one. |
| /proc/self/stat | CPU time of the whole server process — so, of its threads other than the main one. |
| /proc/stat | Busy time of the CPUs the server may run on (its affinity list), and the machine's steal time. |
| Bukkit API | Ticks and the number of players online, for TPS and the with-players numbers. |
Waiting in a second when the cgroup was throttled counts toward the plan's CPU limit, up to how long it was throttled — an upper bound, since the kernel doesn't say which thread each throttled moment held back. What's left is shared between the server's other threads and other work on the cores it may use, in proportion to the CPU time each used in that second. That's a proportion, not a verdict: when both were busy, both get a share.
Time that is neither running nor waiting lands in the per-second "idle or blocked" part and the per-tick "other" part: garbage-collection pauses, disk, locks, interrupts — and, on a virtual machine, moments the hypervisor took the core away (steal), which the guest kernel counts as neither.
What it costs
On Paper, 2 reads of a small kernel file per tick, about 3.1 µs each on our DEV box, plus a sampler thread that used 0.9 ms of CPU a second there.
What it can't tell you
- Inside a virtual machine, steal is reported for the whole machine, not for the cores your server runs on.
- Some hosts show a server its own version of /proc/stat (LXCFS). Other work on its cores can't be measured there, so waiting the CPU limit doesn't explain is left unattributed.
- It reads the CPU limit of the server's own cgroup. A limit set higher up — on the container or VM around it, as some LXC and Proxmox setups do — isn't seen, so that waiting is shared out between the other causes.
- When a counter is missing, TickWait shows "—" rather than 0.
- Waiting isn't the only cause of lag. A long tick with little waiting is the server's own work — a profiler such as spark shows where it goes — or, on a virtual machine, steal: check the VM steal figure.
Measured on TrueTick
TrueTick runs Minecraft servers, so TickWait measures us too. Listed here: reports we measured on our own nodes. Customers' servers run on those nodes as well, so coming from one isn't enough to be listed — we add ours by hand.
None listed yet. Our reports will appear here once we've measured our nodes.