When enterprises provision high-end dedicated servers featuring modern AMD EPYC or dual-socket Intel Xeon processors, they expect uncompromising performance. However, out of the box, standard Linux distributions can silently strangle the performance of databases, hypervisors, and AI workloads due to a hardware concept known as NUMA (Non-Uniform Memory Access).
Modern high-core servers do not treat all RAM equally. They divide their CPU cores and physical memory into separate zones called NUMA nodes. If an application (like Redis, PostgreSQL, or a virtual machine) is running on a CPU core in Node 0, but the Linux kernel allocates its memory from the RAM sticks physically attached to Node 1, the data must travel across a physical bridge on the motherboard (like Intel's UPI or AMD's Infinity Fabric).
This cross-node traffic introduces massive microsecond latency, silently crippling throughput. The solution is straightforward but often overlooked: explicitly pinning high-performance applications to specific CPU cores and their local memory banks using numactl and hwloc. In this tutorial, we will show you how to map your hardware and enforce strict NUMA policies for maximum speed.
What You'll Learn
Understanding the NUMA Node Bottleneck
Step 1: Installing hwloc and numactl
Step 2: Analyzing Your Server's NUMA Topology
Step 3: Pinning Processes to Specific NUMA Nodes
Step 4: Automating NUMA Policies with Systemd
Understanding the NUMA Node Bottleneck
In older SMP (Symmetric Multiprocessing) architectures, all CPUs accessed a single, shared pool of memory at the same speed. As core counts exploded, this shared bus became a massive traffic jam. Hardware manufacturers solved this with NUMA.
In a NUMA architecture, a processor and the memory slots closest to it form a "Node".
Local Memory Access: When a CPU reads memory from its own node, it is extremely fast and high-bandwidth.
Remote Memory Access: When a CPU needs to read memory from another node, the request must traverse the QPI/UPI (Intel) or Infinity Fabric (AMD) interconnect. This adds latency and reduces bandwidth.
By default, the Linux CPU scheduler moves processes around to balance the load. A Redis thread might start on Node 0, allocate 10GB of RAM on Node 0, and then get moved by the kernel to Node 1. Suddenly, every RAM access by Redis becomes a "remote" access. By pinning the process, we eliminate this scheduler jitter and guarantee 100% localized memory access.
Step 1: Installing hwloc and numactl
To fix the bottleneck, we need the right tools. We will use hwloc to visually and structurally map our hardware, and numactl to enforce the binding.
Install on Ubuntu/Debian
Log into your server via SSH and execute the following:
sudo apt update
sudo apt install numactl hwloc hwloc-nox -y
Install on CentOS/RHEL/AlmaLinux
sudo dnf install numactl hwloc -y
Step 2: Analyzing Your Server's NUMA Topology
Before we pin a process, we must understand the physical layout of the server. Let's inspect the hardware.
View Hardware Layout with lstopo
The lstopo command (part of the hwloc package) provides an excellent text-based or graphical representation of your system's architecture.
lstopo-no-graphics
The output will display your NUMA nodes, L3 caches, and the specific CPU core numbers attached to each node. Pay close attention to which PCI devices (like your NVMe drives or 10Gbps NICs) are physically wired to which NUMA node.
Map Memory and Cores with numactl
To get a clear matrix of memory and distances, use numactl:
numactl --hardware
You will see output similar to this:
available: 2 nodes (0-1)
node 0 cpus: 0 1 2 3 4 5 6 7
node 0 size: 64123 MB
node 0 free: 32000 MB
node 1 cpus: 8 9 10 11 12 13 14 15
node 1 size: 64511 MB
node 1 free: 41000 MB
node distances:
node 0 1
0: 10 21
1: 21 10
Notice the "node distances" matrix. A distance of 10 represents local memory access. A distance of 21 represents the penalty of accessing memory on the remote node.
Step 3: Pinning Processes to Specific NUMA Nodes
Now that we know which cores belong to which node, we can bind an application. The numactl command acts as a wrapper around the application you want to run.
Basic CPU and Memory Binding
To force an application to run exclusively on the CPU cores of Node 0, and only allocate RAM from Node 0, use the following syntax:
numactl --cpunodebind=0 --membind=0 my_application --start
Key Flags:
--cpunodebind=0: Restricts the process to only execute on the logical CPU cores attached to Node 0.--membind=0: Forces the kernel to strictly allocate memory from Node 0. If Node 0 runs out of RAM, the application will throw an Out of Memory (OOM) error rather than using slower remote memory. (If you prefer to allow fallback to Node 1 rather than crashing, use--preferred=0instead of membind).
Pinning to Specific Cores
If you want even finer control, you can pin a process to exact CPU core numbers (e.g., cores 2, 3, and 4) using the --physcpubind flag:
numactl --physcpubind=2,3,4 --membind=0 redis-server /etc/redis/redis.conf
Step 4: Automating NUMA Policies with Systemd
Running commands manually is great for testing, but in a production environment, services are managed by systemd. You must modify the service files to ensure your databases or web servers are pinned automatically on startup.
Modifying a Systemd Service File
Let's say we want to optimize a high-traffic Redis instance. Edit the Redis service override file:
sudo systemctl edit redis-server.service
This will open a blank override file. Add the following lines to clear the default ExecStart command and replace it with a numactl wrapped version:
[Service]
ExecStart=
ExecStart=/usr/bin/numactl --cpunodebind=0 --preferred=0 /usr/bin/redis-server /etc/redis/redis.conf
Save the file, reload the systemd daemon, and restart the service:
sudo systemctl daemon-reload
sudo systemctl restart redis-server
Verify that the process is correctly pinned by checking its NUMA map:
numastat -p $(pidof redis-server)
You should see memory allocations heavily concentrated in the specific node you requested.
Conclusion
By taking manual control over your CPU cache and memory allocations with numactl, you stop the Linux kernel from making inefficient scheduling guesses. Pinning your heavy workloads to specific NUMA nodes ensures that memory access remains local, latency remains low, and your bare-metal hardware performs exactly the way enterprise infrastructure should.
iDatam Recommended Tutorials
Cpu
Troubleshooting: 'CPU Over Temperature Error' After Bootup
Learn how to troubleshoot and resolve the 'CPU Over Temperature Error' issue after bootup. Follow step-by-step instructions to check CPU cooler fans, reapply thermal paste, restore BIOS settings, and more.
Dedicated Server
Why Is Your Dedicated Server Slowing Down? Tips on How to Fix It
Discover why your dedicated server is slowing down and learn practical tips to resolve performance issues, ensuring optimal speed and reliability.
Dedicated Server
Optimize a Dedicated Server for High-Speed Solana RPC Node
Discover the 2026 hardware specs, kernel optimizations, and NVMe disk setups required to stay at the network tip.
Discover iDatam Dedicated Server Locations
iDatam servers are available around the world, providing diverse options for hosting websites. Each region offers unique advantages, making it easier to choose a location that best suits your specific hosting needs.