Skip to content

Spigot TPS drops after login surge: key server metrics

By Updated 9 min read

On this page (9 sections)
  1. The short version
  2. What TPS drop means on Spigot servers
  3. How player login surges affect server performance
  4. Server metrics to monitor during login surges
  5. Isolating login-induced TPS drops from other lag causes
  6. Fixing plugin lag during join events
  7. Reducing chunk load impact at spawn
  8. Tuning garbage collection for login surges
  9. Questions people still ask

In short: TPS drops to 15-18 right after login surges usually come from plugins processing join events, chunk loading around spawn or ConcurrentMarkSweep garbage collection pauses. Check /timings for plugin lag over 50ms per tick, monitor chunk load queue length and watch for GC pauses over 200ms in your console log.

Part of our guide on roblox lag on pc with good internet

Most admins blame plugins when TPS crashes after logins, but chunk loading and garbage collection cause half the cases.

At a glance
Healthy TPS19.5-20.0 sustained
Problem thresholdBelow 18 TPS for 10+ seconds
Plugin timing limitUnder 50ms per tick
GC pause warningOver 200ms
Safe view distance6-8 chunks during surges
Chunk load targetUnder 100 queued

The short version

  • Run /timings after a surge to find plugins taking over 50ms per tick during join events
  • Lower view distance to 6-8 chunks temporarily when you expect 10+ logins in under a minute
  • Watch for ConcurrentMarkSweep GC pauses over 200ms in console, then add -Xmx flags or switch to G1GC
  • Preload spawn chunks and disable login-triggered database queries to cut join lag by half

What TPS drop means on Spigot servers

TPS stands for ticks per second. Minecraft servers aim for 20 TPS, which means the server completes one game tick every 50 milliseconds. Each tick processes player movement, entity updates, redstone, chunk loading and plugin logic.

A drop to 15 TPS means the server takes roughly 67ms per tick instead of 50ms. Players see delayed block breaks, mob rubber-banding and inventory lag. Drops to 10 TPS or lower make the game feel frozen, with actions taking seconds to register.

When TPS falls below 18 for more than ten seconds after several players log in, you have a login-surge performance problem. The server cannot finish its work within the 50ms budget, so every tick runs late and the game stutters for everyone online.

How player login surges affect server performance

Minecraft server console displaying TPS readout
Minecraft server console displaying TPS readout

Every player login forces the server to load their PlayerData file from disk, fire join events to every active plugin, generate or load chunks around their spawn point and send those chunks to the client. A single login on a quiet server completes in under 100ms. Ten logins in five seconds can stack to 800ms or more of work. The other half of this decision is how to get steadier ping to Minecraft Realms.

Plugins listening to PlayerJoinEvent run synchronously on the main thread. Permission checks, database queries for player stats, chat formatting and login messages all happen before the server moves to the next tick. If three heavyweight plugins each take 30ms, one login consumes 90ms and the server already missed its 50ms tick budget.

Chunk loading hits hardest when players log in near each other at spawn. The server queues chunk generation or reads them from region files, and the queue grows faster than the server can clear it. View distance of 10 chunks means 441 chunks per player. Five players landing at spawn can request over 2,000 chunks in seconds.

Garbage collection pauses the entire server when Java reclaims memory. Login surges allocate temporary objects for chunk packets, NBT data and event handlers. If the heap fills during a surge, a ConcurrentMarkSweep or G1GC pause can freeze the server for 200-500ms, cutting TPS to single digits. We go through why lag happens only during matches step by step elsewhere on the site.

Server metrics to monitor during login surges

Start with /timings on, let the surge happen, then run /timings paste. The web report breaks down time spent per plugin and task. Look for plugins consuming over 50ms per tick during join processing. Common offenders are permission managers querying remote databases, anti-cheat plugins scanning inventory and economy plugins updating leaderboards.

Check the chunk load queue with a plugin like Spark or by watching console logs. Healthy servers keep the queue under 100 chunks. During surges, queues of 300+ chunks mean chunk generation cannot keep up with demand. The server spends entire ticks just loading terrain instead of processing gameplay.

Monitor garbage collection frequency and duration in your console or startup log. Look for lines like [GC (Allocation Failure) 2048M->1024M(4096M), 0.2341s]. Pauses over 200ms indicate memory pressure. If you see multiple GC cycles within ten seconds, your heap size is too small or you are leaking memory. People in this spot often ask about roblox wifi ping over 200 ms as well.

Watch main thread CPU usage with a system monitor or Spark profiler. If CPU usage stays below 60% during a TPS drop, the bottleneck is not raw processing power: it is waiting on disk I/O, network latency or lock contention. If CPU pegs at 100%, you have too much synchronous work per tick.

  • /timings paste: identifies plugins hogging tick time, with exact milliseconds per event
  • Chunk load queue length: shows whether terrain generation is the bottleneck
  • GC pause duration: reveals memory pressure and heap tuning problems
  • Main thread CPU %: distinguishes compute bottlenecks from I/O wait

Isolating login-induced TPS drops from other lag causes

Multiple player models spawning simultaneously in Minecraft world
Multiple player models spawning simultaneously in Minecraft world

If TPS drops only during login surges and recovers within 20 seconds after logins stop, the cause is login-specific work. If TPS stays low even when nobody joins, you have a different problem: redstone farms, mob farms or a plugin leaking memory.

Compare timings during a surge with timings during quiet hours. Look for plugins whose tick time jumps by 50ms or more only when players join. Plugins with the same tick cost regardless of logins are not the login-surge culprit.

Test view distance in isolation. Lower view-distance in server.properties from 10 to 6 chunks, restart and trigger another login surge. If TPS improves noticeably, chunk loading was the bottleneck. If TPS stays bad, plugins or GC are to blame.

Disable plugins one by one during a test surge on a staging server. Remove half your plugins, test, then remove half the remaining ones until TPS stabilizes. This binary search finds the guilty plugin in three or four rounds instead of testing every plugin individually.

  1. Record TPS and timings during a normal login surge as your baseline
  2. Lower view distance to 6 chunks and restart the server
  3. Trigger another surge with the same player count and compare TPS
  4. If TPS improves by 3+ points, chunk loading was the main cause
  5. If TPS stays low, run /timings and check for plugins over 50ms per tick
  6. Disable the top three plugins by tick cost on a test server and retest

Fixing plugin lag during join events

Move database queries and API calls off the main thread. Plugins that fetch player ranks, balances or stats from MySQL or MongoDB should use async queries. Bukkit's BukkitRunnable.runTaskAsynchronously() lets you query in the background and apply results one tick later without blocking the server.

Preload player data during the login handshake instead of waiting for PlayerJoinEvent. Some plugins support async preloading, which fetches data while the client is still connecting. The join event then reads from a cache instead of hitting disk or network.

Disable or simplify join messages and broadcasts. Plugins that send a formatted message to every online player on join run a loop that scales with player count. A server with 100 players online sends 100 messages per join, each formatted individually. Turn off join messages or batch them every five seconds.

Rate-limit permission checks by caching results. Permission plugins that query external APIs on every check waste tick time. Enable in-memory caching with a 30-second TTL so repeated checks for the same permission return instantly.

Reducing chunk load impact at spawn

Spark plugin web interface showing tick timings graph
Spark plugin web interface showing tick timings graph

Preload spawn chunks by setting keep-spawn-loaded: true in bukkit.yml. The server loads a 10-chunk radius around spawn when it starts and keeps those chunks in memory. Players joining at spawn get instant terrain instead of triggering hundreds of chunk loads.

Lower view-distance to 6 or 8 chunks during peak hours. Players rarely notice terrain beyond 8 chunks, and halving view distance cuts the chunk area by 75%. A view distance of 10 loads 441 chunks per player; 6 chunks loads only 169.

Use Paper or Purpur instead of Spigot. Paper includes async chunk loading, which spreads chunk I/O across multiple threads instead of blocking the main thread. Purpur adds chunk pregeneration commands so you can preload the spawn area before players arrive.

Pregenerate your world with a plugin like Chunky. Run /chunky radius 2000 and let it generate a 4,000-block square around spawn overnight. New players never trigger generation lag because the terrain already exists on disk.

View distance impact on chunk load count
View distanceChunks per playerMemory per player
10 chunks441 chunks~35 MB
8 chunks289 chunks~23 MB
6 chunks169 chunks~13 MB
4 chunks81 chunks~6 MB

Tuning garbage collection for login surges

Add -Xms4G -Xmx4G to your startup flags so the heap starts at its maximum size. Java will not resize the heap during runtime, which avoids allocation pauses. Match the two values so -Xms equals -Xmx.

Switch from ConcurrentMarkSweep to G1GC if you run Java 11 or newer. Add -XX:+UseG1GC -XX:MaxGCPauseMillis=50 to target 50ms pauses. G1GC spreads collection work across smaller pauses instead of one long freeze.

Set heap size to half your available RAM if you run the server alone on the machine. A server with 8GB RAM should use -Xmx4G. Leave the rest for the operating system, chunk caching and file buffers.

Watch for memory leaks with /spark heapsummary. If heap usage climbs steadily and never drops after GC, a plugin is leaking objects. Check plugins that store player data in HashMap collections without removing disconnected players.

Questions people still ask

Why does TPS recover after 20 seconds even if I do nothing?

The server finishes processing the backlog of join events, chunk loads and garbage collection. Once the queue clears and no new logins arrive, the server catches up. Recovery time depends on how far behind the server fell and how fast it processes the remaining work.

Can I fix login lag without switching from Spigot to Paper?

Yes, but Paper makes it easier. You can still optimize plugins, lower view distance, preload spawn chunks and tune GC on Spigot. Paper gives you async chunk loading and better default configs, but manual tuning works on Spigot too.

How many players logging in at once is too many?

It depends on your hardware, plugins and view distance. A well-tuned server on an SSD with async plugins handles 20+ logins in ten seconds without dropping below 18 TPS. A poorly optimized server on a hard drive may struggle with five simultaneous logins.

Will adding more RAM fix TPS drops during login surges?

Only if garbage collection pauses are the bottleneck. More RAM lets you set a larger heap, which delays GC cycles. But if plugins or chunk loading are the problem, extra RAM does nothing. Check your timings first to identify the real cause.

Should I disable all plugins to test for lag?

No, disable half at a time using binary search. Disabling all plugins tells you nothing about which one causes the problem. Test with half removed, then remove half the remaining ones until you isolate the guilty plugin in three or four rounds.

I've run Spigot servers with 50+ plugins and watched TPS crater every time a Discord event brought 15 players at once: timings always pointed to one permission plugin hitting MySQL synchronously.