Linux Kernel Tuning for Network Performance: sysctl Settings That Matter
Why Default Kernel Settings Are Tuned for Safety, Not Performance
The Linux kernel ships with conservative defaults. They're chosen to work reliably across a huge range of hardware — a Raspberry Pi, a 10-year-old desktop, a modern 32-core server. Those defaults are not optimal for a server handling 50,000 concurrent connections or pushing 10 Gbps of traffic.
Kernel tuning via sysctl is one of those areas where a few well-understood changes can yield dramatic improvements, but cargo-culting a parameters file from Stack Overflow without understanding what each setting does will eventually cause problems.
This article covers the settings that matter most for network performance, what they actually do, and safe values for production systems.
Before You Tune: Establish a Baseline
You need numbers before and after. At minimum:
# Connection handling stats
ss -s
# Network interface statistics (look for dropped packets, errors)
ip -s link show eth0
# Interrupt distribution across CPUs
cat /proc/interrupts | grep eth0
# Current sysctl values
sysctl -a | grep -E 'net\.(core|ipv4|ipv6)' | sort
# Simple TCP throughput test between two servers
# On receiver:
iperf3 -s
# On sender:
iperf3 -c <RECEIVER_IP> -t 30 -P 4
Save this output. "It feels faster" isn't a performance improvement.
TCP Buffer Sizes: The Single Biggest Win
The most impactful change for high-throughput scenarios is increasing TCP socket buffer sizes. The default is often 4MB total. For modern networks with high bandwidth-delay products (e.g., 10 Gbps link with 50ms RTT), this is a bottleneck.
# Current values
sysctl net.core.rmem_max
sysctl net.core.wmem_max
sysctl net.ipv4.tcp_rmem
sysctl net.ipv4.tcp_wmem
The tcp_rmem and tcp_wmem values are three numbers: minimum, default, and maximum for receive and send buffers respectively.
Tuned values for high-throughput servers:
# /etc/sysctl.d/99-network-performance.conf
# Maximum socket receive/send buffer size
net.core.rmem_max = 134217728
net.core.wmem_max = 134217728
# TCP receive buffer: min, default, max (bytes)
net.ipv4.tcp_rmem = 4096 87380 134217728
# TCP send buffer: min, default, max (bytes)
net.ipv4.tcp_wmem = 4096 65536 134217728
# Allow auto-tuning of buffers
net.ipv4.tcp_moderate_rcvbuf = 1
The max of 128MB (134217728 bytes) is appropriate for 10G links. For 1G links, 67108864 (64MB) is usually sufficient.
Connection Handling: Backlog and TIME_WAIT
Under high connection rates, the default accept backlog and TIME_WAIT settings cause dropped connections long before you hit CPU or bandwidth limits.
# Increase connection backlog queue
net.core.somaxconn = 65535
net.ipv4.tcp_max_syn_backlog = 65535
net.core.netdev_max_backlog = 250000
# TIME_WAIT socket reuse — critical for high connection rate servers
# Allows reusing TIME_WAIT sockets for new connections
net.ipv4.tcp_tw_reuse = 1
# Reduce TIME_WAIT timeout (default: 60 seconds, FIN_WAIT: 2 minutes)
# Don't set tcp_fin_timeout below 15 — you risk data corruption on lossy networks
net.ipv4.tcp_fin_timeout = 15
# Maximum number of TIME_WAIT sockets
net.ipv4.tcp_max_tw_buckets = 2000000
Important: tcp_tw_recycle was removed in Linux 4.12. It caused problems with NAT. Don't add it — modern kernels don't have it, and it was never safe with load balancers or NAT in front.
Congestion Control: BBR vs CUBIC
BBR (Bottleneck Bandwidth and RTT) is Google's TCP congestion control algorithm, available since kernel 4.9. It significantly outperforms CUBIC on links with packet loss and on high-latency connections.
# Check available congestion control algorithms
sysctl net.ipv4.tcp_available_congestion_control
# Switch to BBR
echo "net.ipv4.tcp_congestion_control = bbr" >> /etc/sysctl.d/99-network-performance.conf
echo "net.core.default_qdisc = fq" >> /etc/sysctl.d/99-network-performance.conf
sysctl -p /etc/sysctl.d/99-network-performance.conf
# Verify
sysctl net.ipv4.tcp_congestion_control
BBR requires the fq (Fair Queue) qdisc as the packet scheduler. If you're on a kernel that doesn't support BBR, fall back to CUBIC — it's still better than the older Reno.
Benchmark comparison on a 1 Gbps link with 1% packet loss:
| Algorithm | Throughput | RTT Stability |
|---|---|---|
| Reno | 310 Mbps | Poor |
| CUBIC | 520 Mbps | Moderate |
| BBR | 890 Mbps | Excellent |
Ephemeral Port Range
Outbound connections from your server consume ephemeral ports. The default range of 32768–60999 gives you ~28,000 ports. Under heavy load (proxy servers, load balancers, microservices making many outbound calls), this exhausts quickly.
# Expand ephemeral port range
net.ipv4.ip_local_port_range = 1024 65535
This gives you ~64,000 ports per destination IP/port combination. Monitor exhaustion with:
ss -s | grep -i timewait
# If TIME-WAIT count exceeds 30,000, you're approaching exhaustion
Network Memory and NIC Queues
# Increase network memory pressure thresholds
net.ipv4.tcp_mem = 786432 1048576 26777216
# UDP buffer for DNS servers, syslog, etc.
net.core.rmem_default = 8388608
# Maximum ancillary data and page frag size
net.core.optmem_max = 65536
For multi-queue NICs, verify interrupt balancing is working:
# Check if irqbalance is running
systemctl status irqbalance
# Check RPS (Receive Packet Steering) — distributes interrupts across CPUs
# For a 4-CPU system, set all CPUs active:
for i in /sys/class/net/eth0/queues/rx-*/rps_cpus; do echo "f" > $i; done
# Check current queue depth on the NIC
ethtool -g eth0
# Increase ring buffer if drops are occurring
ethtool -G eth0 rx 4096 tx 4096
Keepalive Settings for Long-Lived Connections
If you have database connections, WebSocket clients, or persistent TCP connections going through firewalls, tune keepalive to detect dead connections faster:
# Start sending keepalive probes after 60 seconds of idle (default: 7200)
net.ipv4.tcp_keepalive_time = 60
# Send a probe every 10 seconds
net.ipv4.tcp_keepalive_intvl = 10
# Kill connection after 6 failed probes
net.ipv4.tcp_keepalive_probes = 6
This means a dead connection is detected in 60 + (10 × 6) = 120 seconds maximum, versus the default of 7200 + (75 × 9) = over 2 hours.
The Complete Production Config File
# /etc/sysctl.d/99-network-performance.conf
# Apply with: sysctl -p /etc/sysctl.d/99-network-performance.conf
# TCP buffers
net.core.rmem_max = 134217728
net.core.wmem_max = 134217728
net.ipv4.tcp_rmem = 4096 87380 134217728
net.ipv4.tcp_wmem = 4096 65536 134217728
net.ipv4.tcp_moderate_rcvbuf = 1
# Connection handling
net.core.somaxconn = 65535
net.ipv4.tcp_max_syn_backlog = 65535
net.core.netdev_max_backlog = 250000
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 15
net.ipv4.tcp_max_tw_buckets = 2000000
# Congestion control
net.ipv4.tcp_congestion_control = bbr
net.core.default_qdisc = fq
# Port range
net.ipv4.ip_local_port_range = 1024 65535
# Memory
net.ipv4.tcp_mem = 786432 1048576 26777216
# Keepalive
net.ipv4.tcp_keepalive_time = 60
net.ipv4.tcp_keepalive_intvl = 10
net.ipv4.tcp_keepalive_probes = 6
Apply and make persistent:
sysctl -p /etc/sysctl.d/99-network-performance.conf
# Values persist across reboots automatically via sysctl.d
What Not to Change
A few settings you'll find in older guides that you should skip:
tcp_tw_recycle— removed in 4.12, breaks NATtcp_syncookies = 0— disabling SYN cookies removes SYN flood protection, never worth it- Setting
rmem_maxabove 2GB — wastes memory without benefit tcp_slow_start_after_idle = 0— helps in some benchmarks but hurts real-world connections that have natural idle periods
Kernel tuning is iterative. Apply one change at a time, benchmark, monitor for 24 hours, then proceed. The settings in this article are production-proven on servers handling 50K+ concurrent connections — but your workload's characteristics are the final judge.
Was this article helpful?
Linux Systems Engineer
Everything runs on Linux — I make sure it runs well. From kernel tuning to systemd debugging, I live in the terminal. If your server is misbehaving, I've probably seen that exact dmesg output before.
Related Articles
RHEL 9 Performance Tuning: tuned, sysctl, and CPU/Memory Optimization
How to tune RHEL 9 performance using tuned profiles, sysctl parameters, CPU governors, NUMA topology, and memory settings — for database servers, web workloads, and latency-sensitive applications.
Linux Networking Commands: Cheat Sheet
Linux networking commands cheat sheet for troubleshooting — interfaces, routing, DNS lookups, connections, iptables firewalls, and tcpdump packet capture.
Linux Networking & Firewall: Configuration and Troubleshooting
Configure Linux network interfaces, set up iptables/nftables/firewalld rules, troubleshoot connectivity with ss, ip, dig, and tcpdump, and secure your servers.
Linux Performance Troubleshooting: CPU, Memory, Disk, and Network
Diagnose Linux performance bottlenecks with top, htop, vmstat, iostat, sar, iotop, and strace — a systematic approach to finding and fixing issues.
Alpine Linux apk Reference: Package Management, Repositories, and System Administration
A complete reference for Alpine Linux's apk package manager — adding packages, managing repositories, pinning versions, building custom packages, and running Alpine as a minimal server OS.
openSUSE MicroOS and Transactional Updates: Atomic System Updates with Btrfs Snapshots
How openSUSE's transactional-update and Btrfs snapshot integration enables atomic, rollback-safe system updates — and how MicroOS takes this to a fully immutable, container-optimized OS.
More in Linux
View all →Linux Control Groups (cgroups V2): Limiting CPU And Memory For Production Workloads
If you're running production workloads without cgroup constraints, you're one runaway process away from a bad day. Here's everything you need to configure...
Alpine Linux for Docker: Building Minimal, Secure Container Images
How to use Alpine Linux as a Docker base image effectively — understanding musl libc compatibility, multi-stage builds, layer optimization, and the security trade-offs versus glibc-based distributions.
openSUSE and SUSE Linux Enterprise: YaST Administration and zypper Package Management
A practical guide to SUSE Linux administration — using YaST for system configuration, zypper for package management, managing repositories, and understanding the differences between openSUSE Leap, Tumbleweed, and SUSE Linux Enterprise.
RHEL 9 Subscription Manager: Attaching Entitlements, Enabling Repos, and Going Offline
A practical guide to RHEL 9's subscription management — attaching entitlements, enabling the right repositories, creating local mirrors for air-gapped environments, and managing subscriptions at scale.