On servers with more than one CPU socket (and on some single large CPUs), memory isn’t equally fast for every core. Each CPU has its “own” memory, and reaching memory attached to another CPU takes longer. This design is called NUMA (Non-Uniform Memory Access). It matters for big databases and high-performance workloads on dedicated hardware, and much less on typical VPS plans.

The idea

   Node 0                    Node 1
 ┌──────────┐  interconnect ┌──────────┐
 │ CPU 0–15 │◀────────────▶│ CPU 16–31 │
 │  RAM 64G │               │  RAM 64G │
 └──────────┘               └──────────┘
  • A core accessing memory on its own node = local access (fast).
  • Accessing the other node’s memory = remote access (slower, and uses interconnect bandwidth).

The Linux kernel tries to keep processes and their memory on the same node, but large processes, such as a database with a 100 GB buffer pool, inevitably span nodes.

Check your topology

lscpu | grep -i numa
numactl --hardware          # sudo apt install numactl
numastat                     # per-node allocation statistics

Example:

NUMA node(s):        2
NUMA node0 CPU(s):   0-15,32-47
NUMA node1 CPU(s):   16-31,48-63

A single NUMA node means NUMA doesn’t affect you.

On a VPS

Most VPS plans present one NUMA node to the guest, and the hypervisor handles placement on the host. lscpu shows NUMA node(s): 1, and there’s nothing to tune inside the VM. NUMA tuning becomes relevant on:

  • dedicated servers with two sockets, or large multi-chiplet CPUs. See VPS vs dedicated server;
  • very large VMs that span host nodes (the hypervisor may expose a virtual NUMA topology).

When NUMA matters

Workload Symptom / concern
Large databases (MySQL, PostgreSQL, MongoDB) Uneven memory allocation, one node swapping while the other has free RAM
In-memory caches (Redis, Memcached) Latency from remote memory
HPC, scientific computing Memory bandwidth limits
High packet-rate networking NIC attached to one node; process on the other

The classic problem: the “swap insanity” pattern, where a big database fills one node’s memory, and the kernel swaps even though the other node has plenty free.

Tools and settings

Interleave memory across nodes (common for big single-process databases):

numactl --interleave=all mysqld …

MongoDB’s documentation, for example, recommends starting mongod with numactl --interleave=all on NUMA hardware. Packaged systemd units sometimes include this, or you add it with an override (systemctl edit). See systemd unit files.

Pin a process to one node (keeps CPU and memory local, for multiple independent instances):

numactl --cpunodebind=0 --membind=0 ./worker

Kernel setting: keep vm.zone_reclaim_mode = 0 (the default on modern kernels) for most server workloads. Enabling it makes the kernel reclaim local memory aggressively, instead of using remote memory, which often hurts databases.

sysctl vm.zone_reclaim_mode

Automatic NUMA balancing (kernel.numa_balancing) moves memory pages closer to the threads using them. It’s enabled by default, and usually helpful. Some latency-sensitive databases recommend disabling it. Follow your database’s documentation.

Monitoring

numastat -m           # memory per node
numastat -p mysqld    # one process's memory per node

Strongly unbalanced usage, or high numa_miss counts, suggest that pinning or interleaving could help.

Quick reference

Task Command
How many nodes lscpu | grep -i numa
Topology numactl --hardware
Per-node stats numastat -m
Interleave memory numactl --interleave=all <cmd>
Pin to a node numactl --cpunodebind=0 --membind=0 <cmd>

Related: Linux HugePages and vCPU explained.

  • Linux
  • Performance
  • Hardware