RE:NODE

Sizing14 min read

Shared CPU and noisy neighbours: what you actually rent

How hosts divide a CPU between tenants, how to measure steal time and throttling from inside your own server, and when a reserved core is worth paying for.

Updated

0 readers

Almost all hosting is shared. One physical machine, several tenants, an allocation each, and a scheduler deciding whose turn it is. That is not a problem in itself - it is how the economics work, and it is the reason a game server costs six dollars a month instead of sixty. It becomes a problem when your allocation is a share of whatever is left over rather than a reservation, because then your performance is a function of somebody else's evening. The whole skill here is telling those two arrangements apart before you buy, measuring which one you are actually on from inside your own server, and recognising the small number of cases where a reserved core is the right answer rather than an expensive superstition.

What "shared" actually means#

There are two ways a machine gets divided, and they behave differently under pressure.

Full virtualisation (KVM, VMware, Hyper-V) gives each tenant a virtual machine with virtual CPUs. A vCPU is a thread the hypervisor schedules onto a real core when it feels like it. If the hypervisor has 32 threads and has sold 96 vCPUs, that is a 3:1 overcommit ratio, and it works because most tenants are idle most of the time. When they are not, your vCPU sits in a queue waiting for a real core, and the guest kernel records that wait as steal time.

Containers (Docker, LXC, and everything built on them, including Pterodactyl and Wings) skip the virtual machine. Every container is a group of processes on the host's own kernel, fenced off with namespaces and limited with cgroups. There is no virtual CPU to be stolen from - your processes are on the host's run queue with everyone else's, and the kernel's scheduler enforces your limit directly.

That difference matters for diagnosis. In a VM you can measure contention with one column of vmstat. In a container, steal time is almost always zero and the contention shows up as scheduling latency instead, which nothing puts in front of you by default. Most game and app hosting, including anything on a Pterodactyl-style panel, is the container case.

The limit itself lives in two files. On cgroup v2, which is what current kernels use:

bash
$ cat /sys/fs/cgroup/cpu.max250000 100000$ cat /sys/fs/cgroup/cpu.weight100

cpu.max is a quota and a period in microseconds: 250,000 microseconds of CPU time in every 100,000 microsecond window, which is 2.5 cores. cpu.weight is the proportional share used when several cgroups want the machine at once, on a scale of 1 to 10,000, default 100. The cgroup v1 spellings are cpu.cfs_quota_us, cpu.cfs_period_us and cpu.shares (default 1024), and you will still meet them on older hosts.

The three ways a host divides a CPU#

Every hosting product is one of these three, whatever the marketing page calls it.

MethodMechanismWhat happens when neighbours get busy
Weight or sharesProportional, no ceilingYou can burst above your nominal share, and you lose the surplus first
Quota or throttleHard cap per periodYou get exactly what you bought, no more and no less
Pinning or dedicatedSpecific cores assignedNobody else runs on those cores at all

Weight-based selling is where "up to 4 vCPU" and "burstable" come from. It is generous when the machine is quiet and it is the arrangement that produces noisy-neighbour complaints, because the thing you lose under contention is exactly the thing you were shown in the sales page.

A hard quota is less flattering and more predictable. You never get a free burst, and you also never lose one. A plan that says 250 means 250% of one core, always, and a server sitting at that ceiling is slow rather than broken. This is how RE:NODE allocates: one container per server, CPU as a hard throttle to the share bought, and a server pinned at 100% is never suspended for it. The trade is explicit - the ceiling is real, but so is the floor.

asks to run100 ms slicesYour containerquota 250%Neighbour Aquota 100%Neighbour Bquota 400%Kernel schedulerquota and weightPhysical coresthe real ceiling
Where your share of a core is decided

What a noisy neighbour looks like from inside#

Your own graphs look fine. Memory is calm, your process is not doing anything unusual, nothing changed in your configuration, and yet the tick rate wobbles at times you cannot explain. That pattern - unexplained variance with clean internal metrics - is the signature. Three rules of thumb cover most cases:

  • Consistent slowness is usually you. A server that is equally bad at 04:00 and 21:00 is doing too much work, and the machine has nothing to do with it.
  • Variable slowness with quiet internal metrics is usually the machine. Good for an hour, bad for twenty minutes, good again, with no correlation to your player count.
  • Slowness at the same time every day is usually a schedule - yours, the host's, or a neighbour's. Backups, log rotation, package updates and world saves all cluster on the hour.

The measurement that separates them is variance, not average. An average hides the thing players actually feel. If your game exposes per-tick timings, use the maximum and the 99th percentile: Minecraft's /mspt prints averages and peaks over the last five seconds, minute and five minutes, and the peak column is where contention appears first. What tick rate actually means covers why a good average with a terrible peak feels worse than a mediocre average that is stable.

Measuring it: steal, throttling and pressure#

Four numbers tell you nearly everything. Get them while the server feels bad, not afterwards.

Steal time, if you are in a virtual machine. The st column of vmstat, the %steal column of mpstat, and the eighth field of the cpu line in /proc/stat:

bash
$ vmstat 1 10          # last column: st$ mpstat -P ALL 1 5    # %steal, per core, needs the sysstat package$ grep '^cpu ' /proc/stat

Anything consistently above about 5% means the hypervisor is handing your time to someone else, and no tuning on your side recovers it. Occasional single-digit spikes are normal.

Throttling, which is your own quota being enforced:

bash
$ cat /sys/fs/cgroup/cpu.statusage_usec 8012345678user_usec 6902110004system_usec 1110235674nr_periods 940213nr_throttled 7311throttled_usec 41233905

Divide nr_throttled by nr_periods. If it is under a per cent, ignore it. If it is 5% or more, your container is being stopped mid-work, repeatedly, and that is you hitting your ceiling rather than a neighbour taking anything. The fix is doing less work or buying a larger share - CPU or RAM works through which.

Pressure stall information, which is the one that catches container contention. On kernels with PSI enabled:

bash
$ cat /proc/pressure/cpusome avg10=12.44 avg60=9.81 avg300=4.02 total=88213445$ cat /sys/fs/cgroup/cpu.pressuresome avg10=11.90 avg60=9.02 avg300=3.88 total=71004112

some avg10 is the percentage of the last ten seconds in which at least one task in that group was runnable but waiting for CPU. High pressure while you are comfortably under your quota and nr_throttled is flat is the closest thing to proof that the machine, not your server, is the constraint.

Scheduling delay for one process, if you want it at that resolution. The second field of /proc/<pid>/schedstat is nanoseconds spent waiting on a run queue, cumulative since the process started. Sample it twice a minute apart and divide by the interval.

If you have shell access and want a number you can compare across days, run a fixed single-threaded benchmark on a schedule and watch the spread rather than the value:

bash
$ sysbench cpu --cpu-max-prime=20000 --time=10 --threads=1 run | grep 'events per second'

Hourly for two days is enough. A quiet machine varies by a few per cent between runs. A contended one varies by tens, and the bad runs line up with the hours your players complain about.

Telling your problem from the machine's#

What you observeAlmost certainlyNot
At your quota, nr_throttled climbingYou, doing too much workA neighbour
Well under quota, tick peaks spikyThe machine, or your own GCA shortage of memory
%steal above 5% sustained, in a VMThe machineAnything you control
cpu.pressure high, throttling flatThe machineYour configuration
Bad at the same hour every dayA schedule somewhereRandom contention
Bad since the day you changed somethingThe thing you changedThe host
Every tenant on the node complains at onceThe machine, and support knowsYour server
One player's ping is bad, the rest are fineThe network pathCPU at all

The last row catches people constantly. A single player having a terrible time is a routing or connection problem, not a CPU problem, and it wants latency, jitter and packet loss rather than a bigger plan.

Neighbours on disk, network and cache#

CPU gets the blame because it is the resource with a number on the sales page, but it is not the only thing shared.

Disk. NVMe has enormous throughput and still finite IOPS, and the queue is shared. A neighbour restoring a large backup can add milliseconds to your writes for the duration. On a ZFS pool - which is what RE:NODE runs, NVMe throughout - the adaptive read cache is also shared memory on the host, so a neighbour reading a very large working set can cold-start everyone else's cache. Symptom: a short freeze at a moment you did not schedule anything, usually while your own save is in flight. What NVMe actually changes covers what the storage does and does not fix.

Network. Unmetered means not billed per gigabyte, not a licence to saturate a shared uplink. A neighbour pushing a sustained gigabit does not usually affect a game server, because games send very little data - a few dozen to a few hundred kilobytes per second per player. What does affect you is a volumetric attack aimed at the machine rather than at you, and that is a different conversation: what we do about attacks and bandwidth and fair use.

Memory bandwidth and last-level cache. The genuinely invisible one. Your memory limit is yours, but the path to memory and the shared L3 cache are not, and a neighbour with a large working set evicts your hot data from cache. There is no counter you can read for this from inside a container. It shows up as the same workload taking 10-20% longer for no reason you can name, and it is one of the reasons benchmark variance on shared hardware never goes fully to zero.

What you can actually do about it#

In order, cheapest first.

  1. Prove it before you argue about it. Two measurements a week apart with the same method, taken while the problem is happening. "It feels laggy" cannot be actioned by anyone, including you.
  2. Remove your own variance first. A garbage collection pause, a synchronous world save, a plugin doing a database query on the main thread and a backup running at peak all produce the same wobble as a neighbour. Fix those and whatever is left is genuinely external. Reading a server load graph is the first pass.
  3. Move your schedules off the hour. Everybody picks 0 0 * * *. Pick 0 4 * * * or 17 3 * * * and you are contending with far fewer people, including your host's own maintenance. Cron expressions explained has the syntax; scheduled tasks worth having has the list.
  4. Open a ticket with the numbers. A host can move a container to a quieter node, and a host that can see steal or pressure data from your side will usually do it without much argument. On RE:NODE a ticket from the panel reaches all staff and takes private attachments, which is the right place for a graph screenshot.
  5. Buy a hard quota rather than a burst share. If you have a choice between "up to 4 vCPU" and "2 vCPU, guaranteed", the second is the better product for anything with a latency budget, even though it benchmarks worse on an empty machine.
  6. Buy the machine. Covered below, and it is genuinely the last resort rather than the first.

What does not help: nice and renice (they reorder your own processes against each other, not against another tenant's), CPU pinning from inside a container you do not own, and buying more memory. Adding memory to a CPU-contended server changes the graph you look at and nothing the players feel.

When a reserved core is worth it#

For most servers, never. A twenty-player survival world, a Discord bot, a small web application and a database with a handful of queries a second are all far below the point where contention is the limiting factor, and the money is better spent on a faster core or on doing less work.

There are three cases where it is the right call:

  • Tick consistency is the product. A competitive shooter, a racing server, anything where a 40 ms hitch is a lost round. The average was never the metric; the worst case is what people remember and what they leave over.
  • You have promised somebody a latency budget. An API with a published p99, a payment flow, anything with a contract attached. You cannot commit to a number you do not control.
  • The workload genuinely uses several cores continuously. Compiles, video encoding, a database doing parallel scans. Most game servers are not this - the simulation is one thread, so a dedicated eighth core buys nothing that a faster first core would not buy more cheaply.

Be honest about what changes. A reserved core removes variance introduced by other tenants. It does not make a single-threaded simulation faster than the clock speed of the core it is on, it does not fix a plugin that blocks the main thread, and it hands you the maintenance you were previously paying someone else to do. If your tick times are bad in a way that is stable and repeatable, a dedicated machine will reproduce them perfectly. Choosing between a VDS and a game panel and VPS, VDS and dedicated server go through the rest of the trade, including the parts that are work rather than money.

One practical test before you spend: rent the smaller machine for a month and run your existing workload on it alongside the shared one, with the same benchmark on a schedule on both. If the shared server's bad hours disappear and the average barely moves, contention was real and you have bought the right thing. If both graphs look the same, you have just paid more for the same problem, and the answer was in your configuration all along.

FAQ#

What is CPU steal time, exactly?

Time your virtual CPU was ready to run and the hypervisor gave the physical core to another guest. It is reported by the guest kernel in vmstat, mpstat and /proc/stat. It only exists under full virtualisation - inside a container there is no hypervisor, so steal is zero even when the machine is heavily contended, and you have to look at pressure stall information instead.

Does 2 vCPU mean two cores are reserved for me?

Almost never. On a container it means 200% of one core as a throttle: the total your processes may use per scheduling period. On a virtual machine it means two schedulable threads, which the hypervisor places on real cores when they are free. Neither is a reservation unless the host specifically says the cores are pinned.

My server sits at 100% CPU. Am I about to be suspended?

Not on RE:NODE. The container is throttled to the share bought, so it runs slowly rather than damaging anything, and 100% is never grounds for suspension. It is a signal that the ceiling is the ceiling: check whether your tick time is inside budget, and if it is, a pinned graph is just an efficient server.

Can a noisy neighbour actually crash my server?

Not directly. Contention makes things slow, not dead. What it can do is push a timeout over its limit - a watchdog that kills a server for not responding within 60 seconds, a health check that fails three times in a row, a database connection that gives up. If you see crashes rather than slowness, look at why your game server keeps restarting first; contention is rarely the whole story.

How do I prove it to my host?

Send timestamps and numbers: cpu.pressure or %steal samples from the bad window, your own cpu.stat showing you were not throttled, and a tick-time or response-time graph over the same period. That combination is hard to argue with and easy to act on. Adjectives are not.

Is a dedicated machine always faster?

No. It is more consistent. A shared server on a modern high-clock core can comfortably beat a dedicated older machine at a single-threaded game simulation, and often does. Compare the CPU model and clock speed before assuming, which is only possible when the host states the model.


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