How to Fix TPS Lag Spikes on a Minecraft Server
Diagnose Minecraft server lag with /tps, MSPT, and Spark, tell steady low TPS apart from periodic spikes, then trace and fix the usual culprits.
Don't start changing settings to fix lag. Read what the server is actually doing first, find the one thing the numbers point at, and fix that — most "my server lags" posts are someone tuning a dozen config values at random when ten minutes with the right command would have named the cause outright.
The first thing to sort out is which kind of lag you have, because there are two and they don't share a fix. Steady low TPS means the server is overloaded all the time and can't keep up even when nothing much is happening. Periodic spikes are different: the server runs fine, then freezes for a second or two, then runs fine again. They come from different causes and you fix them in different places, so the whole job starts with telling them apart.
None of what follows costs hardware or money — it's reading tools you already have and changing config you already control. And it's worth doing, because a server that hitches and freezes quietly loses the players who'd otherwise stick around.
First, read the two numbers that matter: TPS and MSPT
TPS is ticks per second, and it targets 20.0 — that's the cap and the ideal, since the server runs 20 ticks a second and can't go faster. The /tps command reports averages over the last 1, 5, and 15 minutes. Above about 18 feels smooth to most players, below 15 is noticeably laggy, and below 10 the game gets hard to actually play.
MSPT is the more useful number to watch. It's milliseconds per tick — how long each tick actually took — and it beats TPS for diagnosis because TPS caps at 20 even when the server has tons of room to spare, so a healthy server and a barely-coping one can both read 20.0. Each tick gets a 50ms budget (1000ms divided by 20 ticks), and on Paper /mspt shows how much of that budget recent ticks used.
This is where TPS hides things. A server at 20 TPS but 45ms MSPT is nearly maxed out — clearing its work in time, but only just, and one busy moment pushes it over 50ms and starts dropping ticks. So read MSPT: comfortably under 50 means you've got slack, and the closer it creeps to 50, the less room you have before the next hiccup turns into real lag.
Steady low TPS vs. periodic spikes — they're different bugs
The pattern in the numbers tells you which one you're dealing with. If the 5- and 15-minute TPS are both low, or your median MSPT is high across the board, that's constant overload — the server can't keep up even at rest. If the averages look fine but you get short freezes, a one-to-three-second hitch every so often, that's a spike, and the give-away is a low median tick time with the occasional very high tick mixed in.
This changes where you look. Steady low TPS almost always traces to too much always-on load: view and simulation distance set too high, entity counts that never get trimmed, a heavy plugin doing work every single tick. Spikes trace to something episodic instead — a garbage-collection pause, a single bad chunk getting touched, an infrequent scheduled task firing. Getting the category right saves you hours, because tuning entity limits won't stop a GC pause and adding JVM flags won't fix a server that's overloaded around the clock.
Use Spark to find the actual culprit
Spark is a sampling profiler that breaks down where the server is spending its CPU time, and it's the most reliable way to name the cause instead of guessing. /spark tps gives you current TPS and MSPT at a glance, and /spark profiler runs a full profiling session you can read afterward.
For steady lag, run /spark profiler, let it collect for two to five minutes under normal player load, then stop it and read the report. It'll tell you which plugin, entity type, or game mechanic is eating the most time — that's your target.
For spikes, profile only the slow ticks, otherwise the report drowns in normal gameplay and the spike barely registers. /spark profiler start --only-ticks-over 70 --timeout 60 records only ticks that ran over 70ms, which is exactly the ones causing your freeze. Let it sit until it catches a few and the cause stands out.
If you suspect garbage collection, /spark gcmonitor prints a message every time a GC event finishes. Leave it running with players online, and if the freezes line up with GC events firing every few minutes, you've got a Java heap problem rather than a plugin problem — a completely different fix.
Whatever you run, let the profiler name the cause before you touch any config. The report points at a specific thing — a named plugin, entity processing, chunk work — and that's the thing you fix, not whatever you guessed first.
Fixing steady low TPS
Lower simulation distance first, and keep view distance higher. Simulation distance sets how far out AI, redstone, and entities stay active, and that's the real CPU cost; view distance is mostly how far players can see. A common tune is simulation-distance 4 with view-distance at 8 to 10, so players still see a long way while the server only simulates what's close. That one change does more for a struggling server than most of the fiddly stuff people try first.
Then tame entity buildup, the usual culprit on any server that's been running a while. Mobs, dropped items, XP orbs, arrows, armor stands, minecarts — they pile up in chunks and quietly drag every tick down. Paper's entity-per-chunk-save-limit caps how many of each type get saved per chunk (experience_orb at 16, arrow at 16) so a buildup can't grow without bound, and lowering max-entity-collisions from its default of 8 down to 2 cuts the cost of crammed-together entities in farms and grinders.
Hoppers deserve their own note, because they cause more lag complaints than almost anything else on a survival server. Every hopper checks for items to move every tick, so a wall of them in a sorting system is constant work. Paper lets you slow them with ticks-per.hopper-transfer and ticks-per.hopper-check, and setting hopper.disable-move-event to true is a big win because it stops InventoryMoveItemEvent from firing for every container slot. cooldown-when-full helps too, by keeping full hoppers from hammering away with nowhere to put anything.
This automation load is worst on the gamemodes built around it. Skyblock servers live on island farms and hopper sorters, and modded servers pile machines and mod tick load on top of the vanilla cost, so if you run either, the entity and hopper tuning above matters more than it would on a plain survival world.
Last, trim what nobody uses. Every plugin or mod running every tick is overhead whether anyone interacts with it or not. If it's installed and sitting idle, take it out — that's free TPS for zero downside.
Fixing periodic spikes
Garbage-collection pauses are the first thing to rule out. Java's collector periodically pauses the whole JVM to reclaim memory, and with default settings that pause can run 200 to 500ms — an instant, obvious tick spike. If gcmonitor showed pauses landing on top of your freezes, switch to Aikar's JVM flags, which tune G1GC for the way Minecraft allocates memory and cut both how often and how long those pauses run. On Java 21 and up, ZGC is another option that keeps pauses tiny even on large heaps.
A single hot chunk can do it too. One chunk holding a broken farm, a redstone clock, or a giant entity cram will spike ticks every time the server processes it, and Spark's profiler points you toward it — from there you teleport in and either fix or rip out whatever's running. It's surprisingly common for a whole server's reputation for lag to come down to one contraption someone built and forgot.
Chunk generation on the fly is another spike source, and it's the one tied to player movement. Generating new terrain is one of the heaviest single jobs a server does, so when players push out into ungenerated areas at the map edge, you get a spike as the world gets built under them. Pregenerate the world with a tool like Chunky so players move through ready-made terrain instead of forcing it to generate live.
Infrequent scheduled tasks round it out. A plugin doing heavy work on a timer — an auto-save, a world backup, a periodic sweep — shows up as a spike on a regular beat. The --only-ticks-over profile will catch and name it, and then you reschedule it for an off-hour, stagger it, or swap the plugin for one that behaves. The throughline for every spike is the same: it's episodic, so you're hunting the one event that keeps repeating, and you fix that event rather than the steady-state config.
Why stable TPS is what keeps your community
Smooth, hitch-free TPS holds onto the players who keep showing up, and those active communities are the ones that climb the monthly vote rankings. A server that freezes drives people off, and people who leave don't vote — so the lag quietly costs you the standing that decides where you land on the list.
So keep the habit that got you here: measure, find the one cause, fix it, then re-check /tps and MSPT to confirm the numbers actually moved. Don't assume a change worked because it should have. And if you want a sense of what a healthy server of your type looks like from the outside, the live rankings are a decent benchmark.
FAQ
Is low TPS the same thing as low FPS?
No, and confusing them sends you fixing the wrong machine. TPS is the server's tick rate (out of 20) and it's shared by everyone connected; FPS is how fast each player's own computer draws the game. If one player has low FPS but everyone else is fine, that's their client or GPU, not your server. If everyone stutters at once and your /tps is low, that's the server. Check /tps before you assume a single player's report is your problem to solve.
Does adding more RAM fix TPS lag spikes?
Usually not. TPS lag is almost always CPU-bound — chunk processing, entity ticking, and redstone all run on a single main thread, so a faster core helps far more than more gigabytes. Once your world fits comfortably in memory, extra RAM does nothing for TPS, and it can actually make garbage-collection pauses longer. The one case where more memory helps is when GC pauses come from a heap that's genuinely too small for the world, which gcmonitor will show you.
How long should I run Spark before reading the result?
For steady lag, two to five minutes of profiling under normal player load is enough to see which plugin or mechanic dominates the CPU time. For spikes, profile longer but filter to slow ticks with --only-ticks-over, since you need to actually catch a spike happening — a clean two-minute window with no freeze won't show the cause. Always profile while players are online and the server is doing its normal work; an empty server profiles nothing useful.
How far out should I pregenerate the world?
Match it to a world border you actually set, so the pregen has an edge to stop at instead of running forever. A radius somewhere around 5,000 blocks from spawn is a sane starting point for most survival servers — far enough that players rarely reach raw terrain in normal play, without the pregen taking days or bloating the save folder. Set the border first with /worldborder set, then point Chunky at that radius. The job only runs once: a chunk that's been generated stays generated, so pregen doesn't re-trigger or cost you anything on later restarts. You only revisit it when you widen the border and open up new ground.


