Hardware & Performance·7 min read·

Interrupts, NUMA and the Scheduler on a VPS

How interrupt handling, NUMA topology, and the Linux CPU scheduler behave inside a VPS, and exactly when you can realistically improve them.

NB

Netbay Cloud Team

Netbay Engineering

On this page

A VPS kernel thinks it runs on a normal machine: memory is local, CPUs are uniform, and every interrupt arrives on a nearby core. Reality on a shared host is looser. Understanding how interrupts, NUMA, and the scheduler degrade in a virtualized environment separates tuning that helps from tuning that is theatre.

Interrupts on a shared host

Interrupts notify the kernel that hardware needs attention. On a physical box you can pin the network interrupt to the core that services the fast path, a classic throughput win. On a VPS you usually cannot, because the device is a virtual interface shared with the hypervisor and the actual interrupt is handled outside your guest.

What you can still do is keep interrupts from flooding a single core inside your guest by tuning the receive queues, and use a polling approach for high-throughput paths. The modern tool is ethtool and the receive-side scaling settings:

bash
# list queues your virtual NIC exposes
ethtool -l eth0
# enable RSS to spread the work across queues
ethtool -X eth0 equal 4
# watch softirq share across cores
top -n 1 -b | grep -i 'si'

If softirq time concentrates on one core while you transfer data at high rate, spreading RSS queues helps. On a VPS the win is smaller than on bare metal, but it is real.

NUMA inside a guest

NUMA is how a real machine organizes memory and CPUs: each socket owns some memory, and accessing memory on another socket is slower. A one- or two-vCPU VPS rarely maps to a clean NUMA node, so the guest topology is often a single node or an approximation of the host.

That means NUMA tuning that helps on bare metal can misfire in a guest. Pinning a database to one node assumes local memory, but inside a VPS the guest cannot know where the physical pages live. For your typical small VPS, the practical guidance is simple: let the kernel place memory and processes freely rather than forcing node binds.

bash
nproc
lscpu | grep -E 'NUMA|CPU(s)'
# show current memory placement
numastat -m | head -n 20

If a single NUMA node is reported and you could not possibly move memory between nodes, node-pinning tools are pointless. Spend that effort on the scheduler instead.

The scheduler and the steal interaction

The scheduler decides which runnable task gets a core. In a VPS it operates as if it owns all its vCPUs, but steal from the stack can make tasks wait on top of its own decisions. The practical consequence: a host with a sensible load can still show scheduler stalls during contention.

You can help by keeping the run queue shallow and the workload shaped for the cores you have. CFS group scheduling via cgroups gives fairness that matters when several services share one VPS:

bash
# create a cpu-share group so one noisy service cannot starve another
mkdir -p /sys/fs/cgroup/cpu/app
echo 1024 > /sys/fs/cgroup/cpu/app/cpu.shares
echo 512 > /sys/fs/cgroup/cpu/background/cpu.shares
# assign the pids of the background service...

Shaving tunables matters less than recognising the ceiling: on a virtualized host you tune your side of the wall, and the wall itself stays fixed.

Baseline the scheduler before you touch it

Scheduler regressions are easy to invent and hard to notice. Before changing shares, shares values, or nice levels, capture a clean baseline under the load you care about: per-service latency and the amount of time each service waits on the run queue. Only a change that moves those numbers is a win; everything else is folklore.

bash
# capture per-service CPU wait distribution before tuning
pidstat -d 1 10
# and the run-queue depth, the direct measure of contention
cat /proc/pressure/cpu

The per-service wait in pidstat and the pressure/cpu some-and-avg values are the honest baselines. Re-run them after a scheduler change and compare. Most of the time a scheduler tweak that looked like an improvement was really just less steal that day, which is why you record the steal column alongside and judge the change only when both are steady.

scheduler / interrupts inside a VPS interrupts RSS queues, softirq NUMA one node, let it place freely scheduler cgroup fairness, shallow run queue shared host wall steal, neighbour noise reduce at the edges you tune your side only smaller wins than bare metal, real headroom

Don't chase a single scheduler knob. Baseline your latency, change one thing, and measure under real load. Most scheduler regressions on a VPS are misdiagnosed steal or I/O, not a scheduler bug.

Takeaway

Interrupt pinning and NUMA tricks that shine on bare metal have smaller scope inside a VPS; spend your effort on queue spread, cgroup fairness, and honest measurement. When you need a clean test box for scheduler experiments, deploy an instance on Netbay in under 60 seconds from netbayhosts.in.

Keep reading

Follow along on a real VPS

Deploy Linux in under 60 seconds

These guides are written against Ubuntu, Debian, and RHEL-family images — the same ones on NetBay.

Deploy an instance