Minecraft's server loop wants to complete a full tick every 50 milliseconds — 20 ticks per second. Anything that pushes a tick past that budget shows up in-game as rubber-banding, delayed block updates and mobs skating around. Keeping a Java server at a stable 20 TPS is mostly an exercise in JVM tuning and configuration discipline, and it is far more deterministic than folklore suggests.
Start with the right server software
Vanilla is single-threaded for almost everything that matters. Paper (and its fork Purpur) rewrite the hot paths: async chunk loading, optimised entity ticking and per-world configuration. For CurseForge modpacks you will be on Forge/NeoForge, where the same principles apply but headroom matters more — mods routinely add tick-time in places you cannot configure away.
Heap sizing: more RAM is not faster
Set -Xms equal to -Xmx so the heap never resizes, and resist the urge to hand the JVM everything the machine has. A 10 GB heap on a 20-player Paper server mostly buys you longer garbage-collection pauses, not performance. 6–8 GB is the sweet spot for typical modded servers; vanilla-ish servers run happily on 4 GB.
Use Aikar's flags
The community-standard G1GC arguments — -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -XX:G1NewSizePercent=30 -XX:G1ReservePercent=20 and friends — exist because Minecraft allocates young-generation garbage at an enormous rate. Tuning G1 to collect the young gen aggressively keeps pause times inside the 50 ms tick budget. On Java 17+ they remain the safe default; ZGC is only worth testing on very large heaps.
The quadratic cost of view distance
Loaded chunks scale with the square of view distance. Dropping view-distance from 10 to 7 roughly halves chunk load, and Paper's simulation-distance lets you keep distant terrain visible while only ticking entities nearby — the single highest-value config change on busy servers.
Latency is a hosting problem, not a config problem
Once the tick loop is healthy, the remaining lag your players feel is network distance. A perfectly tuned server in the wrong region still hands Australian players 250 ms round trips from US-hosted boxes. Local infrastructure matters: Australian providers such as Radiant Play run Sydney-based Java and Bedrock servers with one-click CurseForge modpack installs, which puts trans-continental latency back under 30 ms for AU players and removes the JVM setup work entirely for admins who would rather not manage flags at all.
Measure before you tweak
Paper ships with /spark-style profiling via timings v2; Forge servers can use the spark mod directly. Profile first, change one variable at a time, and re-measure. Most "lag fixes" copied from forums address problems your server does not have — the profiler tells you which ones it does.