RE:NODE

Sizing12 min read

CPU or RAM: which one is actually holding your server back

Memory is what gets sold and clock speed is usually what limits you. How to tell them apart in thirty seconds, with the exact commands, graphs and fixes.

Updated

0 readers

Hosting is sold by memory because memory is easy to count. Most game servers are limited by how fast one CPU core can finish a simulation step, which is hard to count and harder to advertise. That mismatch is why adding gigabytes to a stuttering server so often changes nothing at all, and why the upgrade that does fix it is sometimes cheaper than the one that does not. The two problems look identical to players and nothing alike in the numbers, so the whole skill is knowing which numbers to look at. It takes about thirty seconds once you know where they are.

What each one actually does#

Memory holds state. The loaded world, the entities in it, the chunk cache, the plugin data, the JVM heap. Memory decides how much can exist at once. It has a hard edge: below the line everything is fine, above it the process is killed. There is no such thing as "a bit short of memory" on a container with a limit - you are under it or you are dead.

CPU does work. Every tick, the server advances the simulation, and almost every game engine does that on one thread. CPU decides how fast that step finishes. It has a soft edge: as you approach the limit, ticks take longer, the world slows, and nothing crashes. It degrades instead of failing, which is exactly why it goes undiagnosed for months.

There is a third resource that is usually miscategorised as one of these two. Disk shows up as a brief freeze on a schedule - a world save, a backup, a log rotation - and people report it as lag and buy CPU. And a fourth, network, which shows up as one player having a bad time while everyone else is fine.

One more distinction that matters on any panel-based host. A plan's CPU figure is a percentage of one core: 100 is one core, 250 is two and a half. It is a throttle, not a reservation and not a guarantee of which physical cores you get. On RE:NODE it is a hard limit - your container is never allowed past it - and a server sitting at 100% is slow, not broken, and is never suspended for it. That matters for diagnosis: a server pinned at its CPU limit is not misbehaving, it is telling you the limit is the limit.

Telling them apart in thirty seconds#

SymptomAlmost certainlyNot
Crashes with "out of memory" in the logMemoryCPU
Restarts on its own with nothing in the logMemory, killed from outsideCPU
Memory graph climbs to the limit and flatlines thereMemoryCPU
Memory calm, tick time above budgetCPUMemory
Gets worse with more entities, not more playersCPUMemory
Short freeze every few minutes, otherwise fineDiskEither
Long pauses at random, memory graph sawtoothing hardGarbage collectionRaw CPU
One player affected, everyone else fineNetworkEither
Fine at 10 players, unplayable at 40CPU, usuallyMemory, sometimes

The two rows people mix up are the second and the fourth. A server that restarts with an empty log is nearly always being killed for memory - the process does not get to write a goodbye note. And a server whose memory graph is flat and comfortable while players complain is a CPU problem no matter how tempting the bigger plan looks.

Reading memory properly#

The single most common mistake is confusing the memory the game thinks it has with the memory the container gives it.

On a Java server, the JVM heap is set with -Xmx, and the heap is not the whole process. Metaspace, thread stacks, direct byte buffers, the JIT code cache and the garbage collector's own structures all live outside it. Set -Xmx4G inside a 4 GB container and the process will exceed 4 GB and be killed, with nothing in the game log because the game did not run out of memory - the container did.

bash
# Leave the JVM headroom. On a 6 GB plan:$ java -Xms5G -Xmx5G -XX:+UseG1GC -XX:+ParallelRefProcEnabled \    -XX:MaxGCPauseMillis=200 -XX:+AlwaysPreTouch -XX:+DisableExplicitGC \    -jar paper.jar nogui

A reasonable rule is -Xmx at 80-85% of the container limit, with at least 512 MB left over and a full gigabyte on anything with mods. Setting -Xms equal to -Xmx with -XX:+AlwaysPreTouch means the heap is claimed up front, so the memory graph looks alarming and flat from the first minute - that is correct and intended, not a leak. Minecraft JVM flags and Java versions goes through the rest of the flag set, and node memory limits explained covers the equivalent problem for Node applications, which have their own separate heap ceiling.

On a machine you control, the evidence is in three places:

bash
$ free -h                                  # what the system has left$ dmesg -T | grep -i "killed process"      # the kernel's own record of a kill$ journalctl -k | grep -i oom              # same, on a systemd box# With cgroup v2, which is what a container limit actually is:$ cat /sys/fs/cgroup/memory.max            # the limit, in bytes$ cat /sys/fs/cgroup/memory.current        # what is in use right now$ cat /sys/fs/cgroup/memory.events         # oom_kill counts, cumulative

memory.events is the one to know. If oom_kill is greater than zero, something in this container has been killed for memory and you now have a fact instead of a theory. Linux swap and the OOM killer is the long version.

The shape of the memory graph tells you which kind of memory problem you have:

  • Rises, then flat, well below the limit. Healthy. This is what a correctly sized server looks like.
  • Rises to the limit and stays pinned. Either a heap that was told to claim everything, or a genuine shortage. Check whether the tick time is also bad; a pinned graph with good tick times is usually just -Xms.
  • Sawtooth, deep and frequent. Garbage collection running hard because the heap is too small for the working set. This costs CPU and shows as spiky tick times.
  • Climbs slowly for days and never comes down. A leak, or a world that is genuinely still growing. A weekly restart masks it; a profiler finds it.

Reading CPU properly#

The number to look at is not "CPU usage". It is CPU usage of the thread that matters, against the limit you bought.

bash
$ top -H -p $(pgrep -f paper.jar)   # per-thread, not per-process$ uptime                            # load average over 1, 5, 15 minutes$ vmstat 1 5                        # r = runnable, st = stolen by the hypervisor# cgroup v2 again - this is the one that proves throttling:$ cat /sys/fs/cgroup/cpu.max        # quota and period, e.g. "250000 100000"$ cat /sys/fs/cgroup/cpu.stat       # nr_throttled, throttled_usec

nr_throttled climbing while the server feels bad is definitive: your container is being stopped at its quota, repeatedly, mid-tick. That is what a CPU limit feels like from the inside, and no configuration change inside the game removes it.

Three things confuse people here:

A four-core allocation showing 25% usage can still be CPU-bound. If one thread is pinned at 100% of one core and three cores are idle, a process-level view reports 25%. top -H shows the truth. This is the normal state of a busy game server, because the simulation is one thread.

Steal time is not your CPU. The st column in vmstat is time your virtual CPU wanted to run and the hypervisor gave to someone else. Consistently non-zero steal means the host is oversubscribed, and no amount of tuning on your side helps. Shared CPU and noisy neighbours covers what you can do about it.

Load average is not a percentage. A load of 4.0 on a four-core box is fully busy; the same 4.0 on a one-core container means three things are permanently waiting. Read it against your allocation.

Which games can actually use more than one core is worth knowing before you pay for cores:

WorkloadUses extra cores forStill single-threaded for
Minecraft (Paper)Chunk generation, networking, I/OThe world tick
Source engine gamesVery littleThe whole game loop
Unreal servers (Squad, Satisfactory)Rendering-adjacent and I/O workPhysics and gameplay
FactorioSome entity updatesThe deterministic update step
A Node or Python appNothing, unless you fork workersThe event loop
A databaseGenuinely parallel across connectionsA single long query

The pattern: the second core is worth buying, the eighth usually is not, and a higher clock is worth more than either. Paper's Folia fork is the notable exception in Minecraft - it splits the world into independently ticking regions and does scale across cores - but it needs plugins written for it, so it is a choice made at the start of a project rather than a fix applied to a struggling one.

What to do when it is memory#

  1. Check `-Xmx` against the container limit first. More plans are killed by a misconfigured heap than by a genuine shortage. Fixing this is free.
  2. Reduce what is loaded. Lower view-distance and simulation-distance, cut the world border, pre-generate instead of generating live, clear the entities that have accumulated. Every chunk you do not hold is memory you do not need.
  3. Find the leak before you feed it. Memory that climbs forever is a plugin or a mod. A profiler names it in minutes; a bigger plan just moves the deadline.
  4. Then, and only then, buy memory. It is the one problem a bigger plan genuinely solves, cleanly and permanently.

Memory problems are the good kind, in a sense: they have a definite cause, a definite fix, and the fix scales with money. How much RAM a Minecraft server needs and how many players fit on a server both work through the sizing.

What to do when it is CPU#

The order here is different, because the cheapest fix is almost never hardware.

  1. Find the expensive thing. Most CPU-bound servers have one cause, not a general shortage: a mob farm, a plugin ticking every game tick, a hopper chain, a script running a database query inline. Profile before you do anything else - the spark profiler for Minecraft, the equivalent in your game otherwise.
  2. Do less work per tick. Lower simulation distance, cap entity counts, raise the interval on anything scheduled, remove plugins you are not using. This is where the big wins live.
  3. Move blocking work off the main thread. A synchronous disk write or an inline database call spends tick budget doing nothing. Asynchronous saves and pooled connections are configuration, not hardware.
  4. Buy clock speed, not cores. If steps one to three are exhausted, the remaining lever is a faster core. That usually means a different machine rather than a bigger allocation on the same one - the dedicated machines are where the CPU model is actually stated, which is the only way to compare clock speed before you buy.
  5. Accept a lower player count. Unpopular, honest, and sometimes correct.

The general principle: memory problems are solved by buying more, CPU problems are solved by doing less. A server that is CPU-bound and gets a bigger plan with the same generation of processor gets a marginally higher ceiling and the same problem a month later.

A worked example#

A Paper server on a 4 GB, 2 vCPU plan. Twenty regulars, a year-old world, complaints about "lag" every evening.

code
/mspt   -> 48.6/312.4, 44.1/312.4, 29.8/486.0memory  -> pinned at 3.9 GB of 4 GB all dayCPU     -> 185% of an allowed 200%, all evening

Read those three lines together. The memory graph is pinned, which looks like the answer - but the heap was set with -Xms3G -Xmx3G, so of course it is pinned, and there are no OOM events. The CPU is at 92% of its allocation with one thread doing all of it. The average tick time is at the edge of the 50 ms budget and the maximum is nearly ten times it.

The diagnosis is CPU, with a spike problem on top. The 486 ms maximum is a separate issue from the 29.8 ms average: something is occasionally taking half a second, and that is usually chunk generation at the world border or an autosave. A profiler found the real culprits in about ten minutes: view-distance=12 on a server where nobody looks past six, and 3,000 accumulated item entities in one base.

The fix cost nothing. view-distance=8, simulation-distance=6, an item despawn timer, and the average tick time went to 18 ms. The 4 GB plan was never the problem. Had the owner bought 8 GB, the graph would have looked healthier and the server would have felt exactly the same.

Buying the right thing#

You observeBuyDo not buy
OOM kills, heap correctly sizedMore memoryMore CPU
Tick time high, memory calmClock speed, or do less workMore memory
CPU pinned only at peak hoursMore CPU share, or check for stealMore memory
Periodic freezes at save timeFaster storage, or a shorter saveEither of the above
One player complainingNothingAnything
Everything fine, world still growingNothing yet, but watch memoryAnything

Before any purchase, get two data points a week apart with the same measurement. Reading a server load graph covers what you are looking at, when to upgrade your plan covers the thresholds that justify it, and what NVMe actually changes covers the third resource that gets blamed on the first two. If the answer turns out to be "a whole machine", choosing between a VDS and a game panel is the trade-off.

FAQ#

Will more RAM make my server faster?

Only if it was short of memory. On a server whose memory graph is flat and whose tick times are bad, more memory changes nothing measurable. Memory raises the ceiling on how much can exist; it does not make what exists update faster.

What does 2 vCPU actually mean?

Two hundred percent of one core, as a throttle. It does not mean two cores reserved for you, and because most game servers simulate on one thread, a single thread can still only ever use 100% of that allocation. The second core covers networking, saves, garbage collection and everything else around the simulation.

Why does my server use 100% CPU all the time?

Because something is always asking for work and the limit is doing its job. On RE:NODE a server sitting at 100% is slow, not in trouble, and is never suspended for it. The question to ask is whether the tick time is within budget; if it is, high CPU is just an efficient server.

My host says 8 GB but Minecraft only sees 6. Why?

Because -Xmx was set below the container limit, deliberately. The JVM needs memory outside the heap for threads, metaspace and buffers, and a heap set to the full container size gets the process killed. 6 GB of heap in an 8 GB container is correct.

Is a crash with no error message always memory?

Not always, but that is the way to bet. A process killed by the kernel for exceeding its memory limit has no opportunity to log anything. Check the OOM counter or the kernel log before assuming a bug. Why your game server keeps restarting works through the other causes.

Should I buy more cores or a faster core?

A faster core, for nearly every game. The simulation runs on one thread, so clock speed sets the ceiling and extra cores only help the work around it. Two fast cores beat eight slow ones for a game server, and the reverse is true for a database or a build server.


Comments

Completely anonymous: no account, no email, no cookie. We store the name you type, the text and the time - nothing else. Links are limited and markup is not rendered.

0/2000