The Linux kernel exposes hundreds of tunable parameters through the sysctl interface, allowing system administrators to optimize network throughput, memory management, and file descriptor limits without recompiling the kernel. On RHEL 9, these tunables persist across reboots when placed in drop-in configuration files under /etc/sysctl.d/. Proper kernel tuning can dramatically reduce latency and increase throughput for high-traffic web servers, databases, and message brokers. This tutorial walks through creating a comprehensive performance tuning configuration and verifying the results with benchmarking tools.
Prerequisites
- RHEL 9 system with root or sudo access
- Basic familiarity with the Linux command line
httpd-toolsorwrkinstalled for benchmarking (optional but recommended)- A running web service to benchmark (optional)
Step 1 — Understanding sysctl and the Drop-in Directory
The sysctl command reads and writes kernel parameters exposed under /proc/sys/. On RHEL 9, the recommended approach is to place custom settings in a numbered file inside /etc/sysctl.d/ so they load at boot and are easy to manage. Files are processed in lexicographic order, and higher-numbered files take precedence. The 99-performance.conf naming ensures your settings load after distribution defaults.
First, check the current value of a parameter to establish a baseline:
sysctl net.core.somaxconn
sysctl vm.swappiness
sysctl fs.file-max
Step 2 — Creating the Performance Tuning Configuration File
Create the drop-in configuration file with all performance settings. Each section targets a different subsystem: networking, virtual memory, and file descriptors.
sudo tee /etc/sysctl.d/99-performance.conf > /dev/null <<'EOF'
# =============================================================
# Network Performance
# =============================================================
# Maximum number of connections that can be queued on a listening socket
net.core.somaxconn = 65535
# Maximum length of the SYN queue for incomplete connections
net.ipv4.tcp_max_syn_backlog = 65535
# Maximum number of packets queued on the INPUT side of a network device
net.core.netdev_max_backlog = 65535
# Allow TIME_WAIT sockets to be reused for new connections
net.ipv4.tcp_tw_reuse = 1
# Ephemeral port range available to outbound connections
net.ipv4.ip_local_port_range = 1024 65535
# =============================================================
# Memory / Virtual Memory Performance
# =============================================================
# How aggressively the kernel swaps anonymous memory (0=never, 100=always)
vm.swappiness = 10
# Maximum percentage of dirty pages before the process writes them out
vm.dirty_ratio = 15
# Percentage of dirty pages that triggers background writeback
vm.dirty_background_ratio = 5
# =============================================================
# File Descriptor Limits
# =============================================================
# Maximum number of file handles the kernel will allocate system-wide
fs.file-max = 2097152
EOF
Step 3 — Applying the Changes
The sysctl --system command reads all configuration files from /etc/sysctl.d/, /run/sysctl.d/, and /usr/lib/sysctl.d/ in order, applying all parameters. This is the preferred method over sysctl -p because it respects the full drop-in directory hierarchy.
sudo sysctl --system
Verify each key parameter was applied successfully:
sysctl -n net.core.somaxconn
sysctl -n net.ipv4.tcp_max_syn_backlog
sysctl -n vm.swappiness
sysctl -n fs.file-max
sysctl -n net.ipv4.ip_local_port_range
All values should match what you wrote into the configuration file. If a value does not match, check for conflicting entries in other files:
grep -r "somaxconn" /etc/sysctl.d/ /etc/sysctl.conf /usr/lib/sysctl.d/
Step 4 — Adjusting Per-Process File Descriptor Limits
The kernel-wide fs.file-max setting sets the system ceiling, but each process is still subject to per-process limits controlled by ulimit and PAM limits. For services like Nginx or PostgreSQL, raise the per-process limit in their systemd unit or in /etc/security/limits.conf.
# For a specific service via systemd override
sudo systemctl edit nginx
# Add to the override file that opens in your editor:
[Service]
LimitNOFILE=1048576
For system-wide shell session limits, add entries to /etc/security/limits.conf:
sudo tee -a /etc/security/limits.conf <<'EOF'
* soft nofile 1048576
* hard nofile 1048576
EOF
Step 5 — Benchmarking Before and After
Use ab (Apache Bench, from the httpd-tools package) or wrk to measure the impact of tuning. Run a baseline before changes and compare after applying settings. Install the tools first if needed:
sudo dnf install -y httpd-tools
# Benchmark: 10,000 requests, 200 concurrent connections
ab -n 10000 -c 200 http://127.0.0.1/
# Or with wrk (if installed): 30 seconds, 4 threads, 200 connections
wrk -t4 -c200 -d30s http://127.0.0.1/
Key metrics to compare are Requests per second, Mean latency, and the number of failed requests. High-traffic environments typically see 15–40% improvement in throughput after applying these network tunables, especially under connection-heavy workloads.
Step 6 — Verifying Persistence Across Reboots
Because the file lives in /etc/sysctl.d/, systemd will load it automatically at boot via the systemd-sysctl.service unit. Confirm the service is enabled:
systemctl status systemd-sysctl.service
systemctl is-enabled systemd-sysctl.service
After a reboot, re-verify your values are still applied:
sudo reboot
# After reboot:
sysctl -n net.core.somaxconn
sysctl -n vm.swappiness
Conclusion
You have created a persistent kernel tuning configuration on RHEL 9 that improves network connection handling, reduces swap pressure, and raises file descriptor limits. By placing settings in /etc/sysctl.d/99-performance.conf, they survive reboots and remain clearly separated from distribution-managed defaults. Combining kernel tuning with application-level configuration changes produces the greatest performance gains for production workloads.
Next steps: How to Profile Application Performance with perf on RHEL 9, How to Configure Huge Pages for Database Performance on RHEL 9, and How to Monitor System Resources with BPF Tools on RHEL 9.