A powerful Bash script for optimizing Linux physical network card performance parameters, significantly improving network performance in high-throughput environments.
⚠️ IMPORTANT: This script is designed ONLY for maximizing throughput in high-throughput network environments. It is NOT suitable for latency-sensitive scenarios (e.g., financial trading, real-time gaming, VoIP). The optimizations may actually INCREASE latency due to larger buffers and batch processing.
⚠️ DISCLAIMER: This script was originally written several years ago and included some non-universal logic specific to the original environment. AI has been used to remove these environment-specific portions, retain only the universal logic, optimize the code, and write documentation. However, due to changes in work circumstances, all logic in this script has not been fully verified in production. AI has analyzed the code logic for correctness (see verification-report.md), but before executing this script, please carefully read the code to understand what it does to avoid unnecessary risks and losses.
linux_ethernet_optimization.sh is a production-grade script that optimizes Linux network card performance by tuning the following key network parameters:
- Queue Numbers: Optimize parallel processing capability of multi-queue NICs
- Ringbuffer: Reduce packet drops under high traffic
- IRQ Affinity: Balance interrupt load across multiple CPU cores
- RPS (Receive Packet Steering): Software-level receive packet distribution
- XPS (Transmit Packet Steering): Software-level transmit packet distribution
- RFS (Receive Flow Steering): Flow-level CPU affinity (disabled by default)
- 🚀 High-throughput network environments (data centers, CDN, storage clusters)
- 📦 Bulk data transfer workloads
- 🔧 Multi-queue NIC performance tuning
- 🖥️ Multi-core server network optimization
- 🔄 systemd service integration
- ❌ Low-latency applications (financial trading, real-time communication)
- ❌ Latency-sensitive workloads (gaming servers, VoIP)
- ❌ Scenarios where response time is more important than throughput
A NUMA-aware version is available as linux_ethernet_optimization_numa.sh.
This variant automatically detects each NIC's NUMA node and binds IRQ/RPS/XPS only to CPUs local to that node, avoiding cross-NUMA memory access penalties.
Key differences from original:
- Auto-detects NIC's NUMA node from
/sys/class/net/<dev>/device/numa_node - Binds IRQ/RPS/XPS only to CPUs local to that NUMA node
- Falls back to original round-robin behavior if NUMA detection fails
- See script header for detailed warnings, testing recommendations, and known limitations
Recommended for:
- Multi-socket servers (2+ CPUs with separate NUMA domains)
- Systems where NIC NUMA locality matters for performance
- Users who can thoroughly test before production deployment
NOT recommended for:
- Single-socket systems (no NUMA benefit)
- Production use without extensive testing
- Users unfamiliar with NUMA topology
See the script header comments for comprehensive testing procedures and known limitations.
Principle: Multi-queue NICs support distributing network traffic to multiple hardware queues, each can be processed independently, fully utilizing multi-core CPUs.
Target Value: min(CPU cores, max NIC queues)
Effects:
- Improve concurrent processing capability
- Reduce single-core bottleneck
- Enhance overall throughput
# Example: 8-core CPU, NIC supports 16 queues → set to 8 queuesPrinciple: Ringbuffer is the data buffer between NIC and kernel. Larger buffers reduce packet drops during traffic bursts.
Target Value: Hardware maximum (for both RX and TX)
Effects:
- Lower packet drop rate (especially during traffic bursts)
- Improve stability
- Adapt to traffic fluctuations
Principle: Distribute NIC interrupt requests (IRQ) to different CPU cores, avoiding single-core overload.
Strategy: Round-robin distribution
Effects:
- Balance CPU load
- Avoid interrupt handling bottleneck
- Improve interrupt response speed
Principle: Distribute received packets to multiple CPUs at the software level, compensating for insufficient hardware queues.
Enable Conditions:
- ✅ Queues < CPU cores → Enable RPS
- ❌ Queues ≥ CPU cores → Disable RPS (hardware already distributes enough)
Effects:
- Improve receive-side parallelism
- Fully utilize all CPU cores
- Reduce CPU idle waste
Principle: Similar to RPS but for the transmit side.
Effects:
- Improve transmit-side parallelism
- Optimize cache locality
Reasons for Disabling:
- Hash Collision Issues: RFS uses hash tables; collisions cause
ksoftirqCPU usage to spike to 100% - Firmware Bugs: Some NIC firmware has random RFS-related failures
- Latency Fluctuations: May introduce unpredictable ping latency jitter
Production Recommendation: Keep disabled unless specifically needed and thoroughly tested.
# If you really need to enable (at your own risk)
./linux_ethernet_optimization.sh -y -a rfsRequired Dependencies:
# Check if installed
command -v ethtool lspci bash grep sed column
# Debian/Ubuntu installation
apt-get install ethtool pciutils bash grep sed coreutils
# CentOS/RHEL installation
yum install ethtool pciutils bash grep sed coreutilsOptional Dependencies (only needed for CPU > 64 cores):
bc(recommended, for math calculations)python3(fallback)calc(last fallback)
- Must run as root (needs to modify
/sysand/procfiles) - Requires physical NICs (virtual NICs and virtualization drivers are automatically excluded)
- Bash Version: >= 4.0
- Kernel Version: >= 2.6.35 (supports RPS/RFS features)
- GNU sed: Required (BSD sed incompatible)
# Method 1: Clone repository
git clone https://github.com/SteamedFish/network-adjust.git
cd network-adjust
# Method 2: Direct download single file
wget https://raw.githubusercontent.com/SteamedFish/network-adjust/master/linux_ethernet_optimization.sh
chmod +x linux_ethernet_optimization.sh# Script automatically checks dependencies, or manually verify
./linux_ethernet_optimization.sh -n# Only show operations to be performed, don't actually modify
sudo ./linux_ethernet_optimization.sh -n# Show operations and ask for confirmation
sudo ./linux_ethernet_optimization.sh# Automatically execute all optimizations without asking
sudo ./linux_ethernet_optimization.sh -y| Option | Description | Example |
|---|---|---|
-n |
Dry-run mode (show only, don't execute) | sudo ./script.sh -n |
-y |
Auto-confirm mode (skip interactive prompts) | sudo ./script.sh -y |
| Option | Description | Example |
|---|---|---|
-a <action> |
Include specified optimization | -a queue -a ringbuffer |
-A <action> |
Exclude specified optimization | -A rfs |
Available action values:
queue- Queue number optimizationringbuffer- Ringbuffer optimizationirq- IRQ affinity optimizationrps- RPS optimizationxps- XPS optimizationrfs- RFS optimization (excluded by default)
| Option | Description | Match Rule | Example |
|---|---|---|---|
-e <name> |
Include specified NIC | Exact match | -e enp4s0 |
-E <name> |
Exclude specified NIC | Exact match | -E enp4s0 |
| Option | Description | Match Rule | Example |
|---|---|---|---|
-i <device> |
Include device name | Partial match, case-insensitive | -i X722 |
-I <device> |
Exclude device name | Partial match, case-insensitive | -I X710 |
| Option | Description | Match Rule | Example |
|---|---|---|---|
-d <driver> |
Include driver | Exact match | -d i40e |
-D <driver> |
Exclude driver | Exact match | -D ixgbe |
| Option | Description | Match Rule | Example |
|---|---|---|---|
-v <vendor> |
Include vendor | Partial match, case-insensitive | -v Intel |
-V <vendor> |
Exclude vendor | Partial match, case-insensitive | -V Broadcom |
sudo ./linux_ethernet_optimization.sh -y -a queue -a ringbuffersudo ./linux_ethernet_optimization.sh -y -v Intelsudo ./linux_ethernet_optimization.sh -y -D i40esudo ./linux_ethernet_optimization.sh -y -e enp4s0 -A rfssudo ./linux_ethernet_optimization.sh -y -v Intel -v Broadcom -D ixgbe# Use systemd-run to execute at boot
systemd-run --on-boot=5s /path/to/linux_ethernet_optimization.sh -yCreate systemd service file:
# Create service file
sudo nano /etc/systemd/system/network-optimization.service[Unit]
Description=Optimize Ethernet Card Performance
After=network-pre.target
Before=network.target
Wants=network-pre.target
[Service]
Type=oneshot
ExecStart=/usr/local/bin/linux_ethernet_optimization.sh -y
RemainAfterExit=yes
StandardOutput=journal
StandardError=journal
# Set environment variable to identify systemd launch (optional)
Environment="LAUNCHED_BY_SYSTEMD=1"
[Install]
WantedBy=multi-user.targetEnable service:
# Copy script to system path
sudo cp linux_ethernet_optimization.sh /usr/local/bin/
sudo chmod +x /usr/local/bin/linux_ethernet_optimization.sh
# Reload systemd configuration
sudo systemctl daemon-reload
# Enable service (auto-start at boot)
sudo systemctl enable network-optimization.service
# Start immediately
sudo systemctl start network-optimization.service
# Check status
sudo systemctl status network-optimization.serviceFor systems using systemd-networkd, you can persist some configurations via .link files:
# Create link file
sudo nano /etc/systemd/network/10-eth.link[Match]
MACAddress=xx:xx:xx:xx:xx:xx
[Link]
# Set Ringbuffer size (systemd v246+)
RxBufferSize=max
TxBufferSize=max
# Set queue numbers to maximum (systemd v246+)
RxChannels=max
TxChannels=max
CombinedChannels=max
# OtherChannels=max # Uncomment if neededNote: .link files can only configure some parameters; IRQ/RPS/XPS still need script configuration.
The script can be sourced by other scripts to directly call internal functions:
# RPS optimization
set_ethernet_rps_to_optimum <nic_name>
# XPS optimization
set_ethernet_xps_to_optimum <nic_name>
# Queue number optimization
set_ethernet_queue_to_optimum <nic_name>
# Ringbuffer optimization
set_ethernet_ringbuffer_to_optimum <nic_name>
# IRQ affinity optimization
set_ethernet_irq_affinity_to_optimum <nic_name>
# RFS optimization (use with caution)
set_ethernet_rfs_to_optimum <nic_name># Check RPS status
check_ethernet_rps <nic_name>
# Check XPS status
check_ethernet_xps <nic_name>
# Check queue configuration
check_ethernet_queue <nic_name>
# Check Ringbuffer configuration
check_ethernet_ringbuffer <nic_name>
# Check IRQ affinity
check_ethernet_irq_affinity <nic_name>
# Check RFS configuration
check_ethernet_rfs <nic_name># Get NIC hardware queue count
get_ethernet_hardware_queues_number <nic_name>
# Get NIC kernel queue count
get_ethernet_kernel_queues_number <nic_name>
# Get physical NIC list
get_physical_ethernet_card_list
# Generate CPU mask (all CPUs)
generate_cpus_mask <cpu_count>#!/bin/bash
# Custom optimization script
# Load function library
source /path/to/linux_ethernet_optimization.sh
# Optimize only eth0's RPS and XPS
set_ethernet_rps_to_optimum eth0
set_ethernet_xps_to_optimum eth0
# Check optimization results
check_ethernet_rps eth0
check_ethernet_xps eth0
echo "Optimization complete"Cause: Script needs root permission to modify system files.
Solution:
sudo ./linux_ethernet_optimization.sh -yCause: Missing required system commands.
Solution:
# Debian/Ubuntu
sudo apt-get install ethtool pciutils
# CentOS/RHEL
sudo yum install ethtool pciutilsCause: System uses BSD sed (common on macOS).
Solution:
# macOS install GNU sed
brew install gnu-sed
# Use gsed instead of sedCause: Modifying NIC parameters requires NIC reset, causing 500ms-30s brief disconnection.
Solution:
- Normal behavior, just wait
- In bonding scenarios, script automatically delays 10 seconds to avoid resetting both NICs simultaneously
- Execute during maintenance window to avoid business impact
ethtool -l eth0ethtool -g eth0grep eth0 /proc/interrupts
cat /proc/irq/*/smp_affinity_listcat /sys/class/net/eth0/queues/rx-0/rps_cpus
cat /sys/class/net/eth0/queues/tx-0/xps_cpus# Test throughput with iperf3
iperf3 -c <server> -P 8
# Test latency with netperf
netperf -H <server> -t TCP_RROptimizations are lost after reboot. For immediate rollback:
# Method 1: Restart NIC
sudo ifdown eth0 && sudo ifup eth0
# Method 2: Reboot system
sudo reboot
# Method 3: Manually restore defaults
# Disable RPS
echo 0 | sudo tee /sys/class/net/eth0/queues/rx-*/rps_cpus
# Disable XPS
echo 0 | sudo tee /sys/class/net/eth0/queues/tx-*/xps_cpus
# Restore default queue count (needs ethtool)
sudo ethtool -L eth0 combined <original_queue_count>-
NIC Reset Risk
- Modifying parameters causes NIC reset
- Downtime: 500ms ~ 30 seconds (depends on NIC model)
- Recommend executing during maintenance window
-
Bonding Scenario Delay
- Script delays 10 seconds between NICs
- Prevents longer downtime from resetting both ports simultaneously
- systemd boot scenario skips delay (boot-time optimization)
-
Configuration Persistence Issue
- Optimizations do NOT persist after reboot
- Must use systemd service or cron for persistence
- Or use systemd-networkd
.linkfiles (partial parameters)
-
Virtual NIC Compatibility
- Script designed for physical NICs
- Virtual NICs (veth, bridge, tun/tap) are automatically excluded
- Virtualization drivers (virtio_net, vmxnet3, xen-netfront, hv_netvsc) are automatically excluded
-
RFS Disabled by Default
- Strongly recommended to keep disabled in production
- Must thoroughly test before enabling to confirm no hash collisions or firmware bugs
- See "Features" section for detailed RFS explanation
-
NUMA Affinity Not Considered
⚠️ - Critical limitation on multi-socket servers
- Affected optimizations: IRQ affinity, RPS, XPS (all use round-robin across ALL CPUs)
- Does NOT respect NUMA node boundaries
- Impact: On NUMA systems, network processing may occur on remote CPUs, causing:
- 2-3× memory access latency penalty
- Reduced throughput due to cross-node traffic
- Cache inefficiency and increased CPU overhead
- Affected systems: Multi-socket servers (2+ CPUs), AMD EPYC, Intel Xeon multi-socket, any system with multiple NUMA nodes
- Workaround: Manually check NIC's NUMA node and bind IRQ/RPS/XPS to local CPUs:
# Check NIC's NUMA node cat /sys/class/net/eth0/device/numa_node # Get local CPUs cat /sys/devices/system/node/node0/cpulist # Bind IRQ/RPS/XPS to local CPUs only
- Recommendation: For NUMA systems, use NUMA-aware tuning tools (e.g.,
tuned,irqbalance --hintpolicy=subset)
-
Dry-run First for Initial Use
sudo ./linux_ethernet_optimization.sh -n
-
Verify in Test Environment
- Validate effects on test servers first
- Apply to production only after confirming no issues
-
Use systemd for Persistence
- Create systemd service
- Set auto-start at boot
-
Monitor Optimization Effects
- Use monitoring tools to observe CPU usage, network throughput
- Record performance metrics before and after optimization
-
Gradual Optimization
- Optimize single parameters first, observe effects
- For example, optimize queue and ringbuffer first, then others after confirming stability
Challenge: Bash cannot handle integer arithmetic over 64 bits.
Solution: Tiered fallback strategy
# CPU ≤ 64: Pure Bash bitwise operations
mask=$((2 ** cpus - 1))
printf "%x" "$mask"
# CPU > 64: Try in order
# 1) bc (most common, recommended)
echo "obase=16; 2^$cpus - 1" | bc
# 2) python3 (fallback)
python3 -c "print(hex((2**$cpus)-1)[2:])"
# 3) calc (original solution, last fallback)
calc "hex(2^$cpus - 1)"Formatting: Insert comma every 8 hex characters
Example: 256 CPUs → ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
Formatted → ffffffff,ffffffff,ffffffff,ffffffff,ffffffff,ffffffff,ffffffff,ffffffff
Issue: mlx5e driver reports normal and XSK queues together, doubling queue count.
Solution: Auto-detect and divide by 2
get_ethernet_kernel_queues_number() {
local ETH=$1
local queues=$(find /sys/class/net/"${ETH}"/queues/ -type d -name "rx-*" | wc -l)
# mlx5 special handling
local driver=$(basename "$(readlink /sys/class/net/"${ETH}"/device/driver)")
if [[ "$driver" == "mlx5_core" ]]; then
queues=$((queues / 2))
fi
echo "$queues"
}Most Drivers: IRQ name format is <prefix>-<queue> or <prefix>@<queue>
Example: eth0-TxRx-0, i40e-eth0@0
Extract: Use sed 's/.*[-@]//'
mlx5 Exception: Format is mlx5_comp<queue>@pci:<id>
Example: mlx5_comp0@pci:0000:04:00.0
Extract: Use sed 's/mlx5_comp\([0-9]*\)@.*/\1/'
This script uses a modular design with the following characteristics:
-
Clean Code Organization
- Unified sysfs operations via
_sysfs_read()/_sysfs_write() - Unified ethtool parsing via
_ethtool_parse() - Filter logic handled by
_filter_list() - Clean main loop logic
- Unified sysfs operations via
-
Minimal Dependencies
- No external tools needed for CPU ≤64 (pure Bash, covers 95% scenarios)
- CPU >64 supports
bc/python3/calcwith automatic fallback
-
Readability First
- Detailed comments for complex logic
- Unified naming conventions
- Clear constant definitions
-
Performance Optimized
- Minimized subshell invocations
- Maintains efficient execution speed
-
Key Design Decisions
- RFS disabled by default (see comments for reasoning)
- mlx5 queue count special handling (/2)
- Automatic systemd environment detection
- Sourceable as library
-
64 CPU mask calculation support
- 10-second delay for bonding scenarios
linux_ethernet_optimization.sh
├── Core logic protection comments
├── Dependency check functions
│ ├── check_script_requirements()
│ ├── check_root()
│ └── run_by_systemd()
├── Utility functions
│ ├── _sysfs_read()
│ ├── _sysfs_write()
│ └── _ethtool_extract_value()
├── CPU Mask calculation
│ ├── generate_cpus_mask() # supports bash/bc/python3/calc fallback
│ └── format_cpumask()
├── Hardware query functions
│ ├── get_ethernet_hardware_*()
│ └── get_ethernet_kernel_*()
├── Optimization functions (6 pairs)
│ ├── check_ethernet_{rps,xps,rfs,queue,ringbuffer,irq_affinity}()
│ └── set_ethernet_*_to_optimum()
├── Filtering and main loop
│ ├── get_filtered_ethernet_card_list()
│ └── main()
└── Script entry (is_sourced detection)
- Create
check_ethernet_<new_feature>()function - Create
set_ethernet_<new_feature>_to_optimum()function - Add new item to action list in
main() - Add command-line option parsing
- Add special handling logic in
get_ethernet_kernel_queues_number() - Add new naming patterns in IRQ extraction logic
- Test and verify
# 1. Syntax check
shellcheck linux_ethernet_optimization.sh
# 2. Dry-run test
sudo ./linux_ethernet_optimization.sh -n
# 3. Single-item test
sudo ./linux_ethernet_optimization.sh -y -a queue
# 4. Comparison test (record before/after states)
ethtool -l eth0 > before.txt
sudo ./linux_ethernet_optimization.sh -y
ethtool -l eth0 > after.txt
diff before.txt after.txtMIT License
Copyright (c) 2026 Network Optimization Project
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Issues and Pull Requests are welcome.
- Kernel Documentation:
man 7 cpuset - systemd Documentation:
man 5 systemd.link,man 5 systemd.service - RPS/RFS Official Documentation: https://www.kernel.org/doc/Documentation/networking/scaling.txt
For questions or suggestions, please provide feedback via Issues.