Hardware interrupts are the backbone of Linux system responsiveness — every network packet, disk I/O operation, and USB event triggers an interrupt request (IRQ) that the CPU must handle. On busy servers with high-throughput network interfaces, NVMe storage arrays, or multi-GPU configurations, unbalanced interrupt distribution can create CPU hotspots, increase latency, and degrade overall system performance. This guide compares three approaches to Linux interrupt management: irqbalance (automatic daemon), tuned (profile-based tuning), and manual IRQ affinity configuration (fine-grained control via /proc/irq).
Understanding Linux Interrupt Handling
When a hardware device needs CPU attention, it signals an interrupt request. The Linux kernel routes each IRQ to a specific CPU core via the IRQ affinity mask — a bitmask stored in /proc/irq/<IRQ_NUMBER>/smp_affinity. By default, the kernel distributes interrupts across all available CPUs, but this naive distribution rarely matches workload patterns.
Consider a high-frequency trading server with a 100GbE network interface card (NIC). If the NIC interrupts land on CPU cores also running the trading application event loop, context switches increase and packet processing latency spikes. Proper interrupt management isolates these workloads, ensuring interrupts are handled on dedicated cores while application threads run undisturbed on others.
The three primary tools for managing this are:
| Feature | irqbalance | tuned | Manual IRQ Affinity |
|---|---|---|---|
| Approach | Automatic daemon | Profile-based | Manual configuration |
| Setup Complexity | Low (install and run) | Medium (select profile) | High (per-IRQ configuration) |
| Dynamic Rebalancing | Yes | No (static profile) | No |
| NUMA Awareness | Yes | Yes | Manual |
| Fine-Grained Control | Limited | Limited | Full |
| Persistence | Service-based | Profile-based | Requires script |
| Best For | General servers | Red Hat systems | Performance-critical workloads |
| GitHub Stars | 666 | 1,208 | N/A (kernel feature) |
| Last Updated | 2026-04-27 | 2026-05-14 | Kernel mainline |
irqbalance — Automatic Interrupt Distribution
irqbalance is the default interrupt balancer on most Linux distributions. It runs as a daemon, periodically scanning /proc/interrupts and redistributing IRQ affinity masks to balance load across CPUs. It considers NUMA topology, CPU idle states, and interrupt frequency when making decisions.
Installation and Configuration
On Debian/Ubuntu:
| |
On RHEL/CentOS/Fedora:
| |
The main configuration file is /etc/sysconfig/irqbalance (RHEL) or /etc/default/irqbalance (Debian):
| |
Docker Compose for Interrupt Monitoring
While irqbalance runs directly on the host, you can monitor its effectiveness from a containerized dashboard:
| |
When irqbalance Falls Short
irqbalance works well for general-purpose servers but has limitations:
- No application awareness: It does not know which CPUs run your database or web server threads
- Rebalancing latency: It runs every 10 seconds by default, missing burst interrupt patterns
- Limited NUMA optimization: While NUMA-aware, it may not optimize for your specific workload topology
tuned — Profile-Based System Tuning
tuned is Red Hat system tuning daemon that applies predefined or custom profiles covering CPU governor, disk scheduler, network settings, and IRQ affinity simultaneously. Unlike irqbalance, tuned applies static configurations based on workload profiles rather than dynamically rebalancing.
Installation and Configuration
| |
tuned includes several built-in profiles that affect interrupt handling:
| |
Creating a Custom Profile for IRQ Management
| |
Activate the profile:
| |
Docker Compose for tuned Monitoring
| |
Manual IRQ Affinity — Fine-Grained Control
Manual IRQ affinity configuration provides the highest level of control. You directly write CPU affinity bitmasks to /proc/irq/<IRQ>/smp_affinity, giving you precise placement of each interrupt source.
Finding IRQ Numbers
| |
Example output:
| |
Setting IRQ Affinity
CPU affinity is specified as a hexadecimal bitmask. CPU 0 = 0x1, CPU 1 = 0x2, CPU 2 = 0x4, CPUs 0-3 = 0xF:
| |
Persistent Configuration via systemd
| |
| |
Why Self-Host and Manage Interrupts Locally?
Managing interrupt affinity on your own infrastructure provides benefits that cloud instances cannot match. When you control bare-metal servers, you have direct access to /proc/irq and can tune interrupt distribution to your exact workload patterns.
For high-throughput database servers, isolating storage IRQs to dedicated CPU cores eliminates interrupt-induced latency spikes in query processing. Network-intensive applications like load balancers and reverse proxies benefit from pinning NIC interrupts to cores adjacent to the application threads in the CPU topology.
For related Linux performance tuning, see our Linux I/O Schedulers comparison and CPU Governor management guide. For NUMA-aware memory management, check our HugePages guide.
Choosing the Right Interrupt Management Strategy
| Scenario | Recommended Tool |
|---|---|
| General-purpose web server | irqbalance (default) |
| Red Hat Enterprise Linux | tuned with throughput-performance profile |
| High-frequency trading | Manual IRQ affinity (pin NIC IRQs) |
| Database server (NVMe storage) | Manual IRQ affinity plus tuned profile |
| Virtualization host | tuned with virtual-host profile |
| Mixed workload | irqbalance with banned IRQs for critical devices |
FAQ
What is IRQ affinity in Linux?
IRQ (Interrupt Request) affinity is a Linux kernel feature that controls which CPU cores handle hardware interrupts from specific devices. Each IRQ has an affinity mask stored in /proc/irq/<number>/smp_affinity that specifies the allowed CPU cores. Proper IRQ affinity prevents interrupt storms on single cores and reduces latency for latency-sensitive applications.
Does irqbalance work on NUMA systems?
Yes, irqbalance is NUMA-aware by default. It considers the NUMA topology when distributing interrupts, preferring to assign device IRQs to CPU cores on the same NUMA node as the device PCIe slot. This minimizes cross-NUMA memory access latency. You can verify NUMA awareness by checking irqbalance logs: journalctl -u irqbalance | grep -i numa.
Can I use irqbalance and tuned together?
Yes, they serve different purposes. irqbalance handles dynamic interrupt redistribution, while tuned manages broader system tuning profiles (CPU governor, disk scheduler, kernel parameters). You can run irqbalance inside a tuned profile. However, some tuned profiles (like latency-performance) may disable irqbalance in favor of static IRQ pinning — check the profile irqbalance section in /usr/lib/tuned/<profile>/tuned.conf.
How do I verify IRQ affinity is working correctly?
Use cat /proc/interrupts to see interrupt counts per CPU. If one CPU shows significantly higher counts for a specific IRQ than others, the affinity mask may not be set correctly. You can also use grep . /proc/irq/*/smp_affinity to see all current affinity masks. For real-time monitoring, watch -n 1 cat /proc/interrupts shows how counts change over time.
What happens to IRQ affinity after a reboot?
Manual IRQ affinity settings in /proc/irq are lost on reboot. To persist them, use a systemd service (as shown above), a tuned profile, or add the configuration to /etc/rc.local. irqbalance and tuned settings persist automatically through their respective systemd services.
Should I disable irqbalance on a production server?
Not necessarily. irqbalance works well for most workloads. Disable it only if you need fine-grained manual control over specific IRQs (e.g., pinning NIC interrupts for a low-latency trading application). If you disable it, you must manually configure IRQ affinity for all devices, or use tuned profiles that handle IRQ placement automatically.
What is the difference between MSI and legacy interrupts?
MSI (Message Signaled Interrupts) and MSI-X are modern interrupt mechanisms where devices write to a specific memory address to signal interrupts, rather than using dedicated physical interrupt lines. MSI-X supports more vectors per device (up to 2048) and allows per-queue interrupt assignment. Check if your device uses MSI: lspci -vvv | grep -i "msi". Modern NICs and NVMe controllers use MSI-X by default.