The short answer: heap sizes that actually work
These are heap sizes, meaning the value you pass to the JVM as -Xmx, not the plan size you buy. They assume view-distance 8 and simulation-distance 6, which is a sane server default rather than a single-player one. Treat them as a starting point you then measure, because world size and how much map players keep loaded move the number more than anything else.
The pattern worth noticing: going from 5 players to 25 on vanilla Paper adds roughly 1 GB, while swapping a 60-mod pack for a 300-mod pack adds 5 GB at the same player count. Mod count and chunk footprint dominate. Player count only matters because each player drags a bubble of loaded chunks around with them.
Anything above about 12 GB for a Minecraft server is nearly always a leak, a runaway chunk loader, or a live map renderer, not a genuine requirement. If your sizing calculation lands there, stop and profile instead of buying.
- Vanilla or Paper, 1 to 10 players, under 10 plugins: 1.5 to 2 GB heap
- Paper, 10 to 25 players, 15 to 25 plugins: 2.5 to 3 GB
- Paper, 25 to 60 players, full plugin suite, pre-generated map: 4 to 6 GB
- Fabric or NeoForge light pack (40 to 80 QoL and performance mods), up to 10 players: 3 to 4 GB
- Mid-weight tech or magic pack (150 to 250 mods), 5 to 10 players: 5 to 6 GB
- Kitchen-sink pack (300+ mods, All the Mods style), 5 to 15 players: 8 to 10 GB
- Legacy 1.7.10 or 1.12.2 packs on Java 8: budget 20 to 30% more than the same mod count on a modern pack, and watch metaspace separately
Your plan size is not your -Xmx
The JVM needs memory outside the Java heap: metaspace for loaded classes, thread stacks for the dozens of threads a Minecraft server runs, the JIT code cache, garbage collector bookkeeping, and direct byte buffers for networking and world I/O. On a modern modded server that overhead lands between 400 MB and 1 GB, and none of it is covered by -Xmx.
If your container limit is 4 GB and you set -Xmx4G, the heap alone is permitted to grow to the limit before off-heap allocation pushes the container over. The kernel then kills the process. You get exit code 137, no Java stack trace, no OutOfMemoryError in the log, and a server that appears to have simply closed. This is the most common false RAM emergency there is.
The rule: set -Xmx to the container limit minus about 20%, with a minimum of 512 MB headroom. Puranode's Minecraft plan is a 4 GB container, so the working heap there is roughly 3 to 3.2 GB. Set -Xms to the same value, because a fixed heap avoids resize pauses and lets G1 size its regions once. Panels built on Pterodactyl often ship a startup line that passes the full container size straight to -Xmx, which is exactly the trap above. Check yours before assuming.
What actually eats the heap: chunks, entities and force-loading
A connected player costs almost nothing on its own. What costs memory is the chunk bubble that follows them. At view-distance 10 a player keeps (2 x 10 + 1) squared, or 441 chunks, loaded. At 8 it is 289. At 6 it is 169. Dropping view distance from 10 to 7 removes roughly half the chunk-driven heap per player, and on a survival server most players never notice.
Since 1.18 these are two separate knobs. view-distance controls how far chunks are sent and kept loaded, which is mostly a memory and bandwidth cost. simulation-distance controls how far chunks actually tick, which is mostly a CPU cost. view-distance 10 with simulation-distance 5 is a good trade.
The real heap hogs in practice are block entities in dense builds, mob farms, force-loaded chunks from mod chunk loaders, quarries, large AE2 networks and Create contraptions, and live map renderers such as Dynmap or BlueMap during a full render. Force-loaded chunks never unload, which is why a modded server with 8 players can hold more resident chunks than a vanilla one with 60.
Read MSPT before you buy anything
Minecraft runs 20 ticks per second, so each tick has a 50 ms budget. TPS is a lagging indicator: it only falls once MSPT exceeds 50, so a server sitting at 47 ms per tick reports a healthy 20 TPS while being one mob farm away from visible lag. Watch MSPT, not TPS.
On Paper, /mspt gives 5-second, 10-second and 60-second averages plus a high percentile. The percentile is what players feel. A 30 ms average with 120 ms spikes stutters noticeably; a flat 40 ms does not. Under 35 ms average is comfortable, 35 to 45 is workable, above 45 means you are out of budget.
spark is the tool that answers why. /spark health prints TPS, MSPT, CPU load and GC statistics on one screen. /spark profiler start, left running through several minutes of real player load and then stopped, produces a sampled call tree naming the plugin, mod or subsystem consuming tick time. On current Paper builds spark has replaced Timings entirely.
Nothing on that screen improves by adding RAM. Tick time is single-threaded work on one CPU core.
Symptom to cause: what your lag is actually telling you
Almost every request for more RAM describes a symptom that RAM cannot fix. Work out which one you have before spending anything.
Only two entries in the list below are genuinely solved by more memory: the OutOfMemoryError, and a live set that stays high after collection. Everything else is CPU time, disk throughput, network path or configuration, and buying a larger plan for those changes nothing except your invoice. The most useful habit is to reproduce the symptom while spark is profiling, then read the call tree instead of guessing. A five-minute profile taken during real player load answers the question definitively. A profile taken on an empty server answers nothing at all.
- Constant rubber-banding, mobs sliding, hoppers and redstone slow: tick time. Profile with spark and check MSPT.
- Smooth play with a 1 to 3 second freeze every few minutes: garbage collection pauses, from either heap pressure or bad GC flags.
- Crash log contains java.lang.OutOfMemoryError Java heap space: genuine heap exhaustion or a leak. This one is RAM.
- Process disappears with exit code 137 and no Java error: the container was OOM-killed. Your -Xmx is too close to the limit.
- Slow chunk loading when exploring or flying an elytra: chunk generation and disk I/O. Pre-generate the map.
- Lag only when one specific player is online: profile their base and look for entity or block-entity clusters.
- Lag that started after an update with no config change: read the mod or plugin changelog before touching hardware.
- Ping spikes with 20 TPS and low MSPT: network path, not the server.
The one test that tells you if you are genuinely short
Heap graphs mislead people constantly. A Java heap is meant to look like a sawtooth: it fills, the collector runs, it drops. Seeing 90% used means nothing on its own, because the JVM has no reason to collect early.
The number that matters is the live set, which is how much heap remains in use immediately after a full garbage collection. Trigger one and read the floor of the sawtooth rather than the peak.
Interpretation, as a fraction of -Xmx: below 50% means you have spare capacity and more RAM will change nothing. 50 to 70% is healthy. 70 to 85% means the collector is working harder than it should and a large new build could tip you over. Consistently above 85% after a full collection means you are genuinely short, and that is the point where more RAM is the correct purchase.
One further check before buying. If the live set climbs steadily over days and never returns to its earlier floor, that is a leak rather than a capacity problem, and more RAM only buys a longer gap between crashes. The spark heap summary shows which classes dominate the heap, and the package names usually identify the offending plugin immediately.
Tuning that buys you more than an upgrade
Work through these before paying for anything. On a typical 4 GB Paper server they routinely cut the live set by a third, and MSPT by more than that.
Aikar's flags deserve a specific note. They are a widely used G1 tuning set that makes the collector run frequent small collections instead of rare large ones, which is what a latency-sensitive game loop wants. Use the standard values up to about 12 GB of heap and the documented large-heap variant above that. Do not paste random flag strings from forum posts, as several popular ones disable features the server relies on.
There is also a hard ceiling. Never set -Xmx above roughly 31 GB. Past about 32 GB the JVM loses compressed object pointers, every reference grows from 4 bytes to 8, and a 33 GB heap gives you less usable space than a 31 GB one.
- Pre-generate the world with Chunky out to a fixed border. Live chunk generation is the single largest source of tick spikes.
- Set and enforce a world border. An unbounded map grows region files and exploration cost forever.
- Lower simulation-distance before view-distance. It costs players almost nothing visually.
- Cut mob spawn caps in bukkit.yml (monsters defaults to 70 per world) and tighten entity-activation-range in Paper's config.
- Schedule live map renders instead of leaving them running. A full Dynmap or BlueMap render is a temporary spike, not a steady state.
- Audit force-loaded chunks on modded servers with /forceload query, and check every mod chunk loader in use.
- Set -Xms equal to -Xmx so the heap never resizes at runtime.
- Restart on a schedule only as a workaround for a leak you cannot fix, never as the fix itself.
When to actually upgrade, and when not to
Upgrade when all three of these are true at once, not when one of them is.
First, the live set after a full collection sits above 85% of -Xmx during normal play. Second, GC activity is visible in the spark health report as frequent old-generation collections rather than cheap young ones. Third, you have already pre-generated the map, reduced view and simulation distance, and confirmed with a profile that no single plugin or mod is responsible.
If all three hold, move up one sensible band rather than doubling. A plugin server that has outgrown 3 GB usually settles at 4. A modpack that has outgrown 6 GB usually wants 8, and very rarely more than 10.
Be honest about the direction of the fix too. Moving from 6 GB to 12 GB on a server whose MSPT is 60 changes nothing, because you bought capacity for a problem that is speed.
On mechanics: many hosts sell tiered plans with a self-serve resize. Puranode currently runs a single 4 GB Minecraft SKU, so a larger allocation is a manual resize handled the same day on request by email. Either way, ask what the container limit is and keep -Xmx below it.
When you should not be paying for hosting at all
For two to four people playing a vanilla or lightly modded world a few evenings a week, a spare desktop or an always-on laptop is genuinely enough. A 2 GB heap runs fine on almost any machine made in the last decade, and the honest answer is that you do not need a host.
The real costs of self-hosting are not CPU or RAM. They are your upload bandwidth, which is what gives friends 300 ms and rubber-banding the moment someone else in the house starts streaming; port forwarding, which exposes your home IP address and needs router access you may not have; and the machine having to be awake whenever anyone wants to play. Tunnelling services or a private mesh network solve the port-forwarding half of that, not the bandwidth half.
Paid hosting starts to make sense when players are spread across countries, when the world needs to be up while you are asleep, when you want backups that run without anyone remembering to make them, or when a modpack wants 8 GB you would rather not dedicate at home. Below that threshold, buying RAM you will not use is just buying RAM you will not use.