Linux kernel performance tuning with sysctl
sysctl.conf and updated for modern Linux kernels. Values should be validated with measurements on the actual hardware, kernel, network path and application.Kernel tuning should follow this order:
1. Measure the bottleneck. 2. Change one related group of parameters. 3. Benchmark again. 4. Keep only settings that produce a measurable improvement. 5. Record the workload assumptions together with the configuration.
A practical layout is:
output 2 lines
/etc/sysctl.d/70-performance-common.conf /etc/sysctl.d/80-performance-role.conf
Apply with:
$ sysctl --systemCommon performance profile
Create /etc/sysctl.d/70-performance-common.conf:
# ----------------------------------------------------------------------------- # Virtual memory # ----------------------------------------------------------------------------- # Keep most application memory resident while still allowing the kernel to # reclaim very cold anonymous pages when useful. vm.swappiness = 10 # Start background writeback relatively early. vm.dirty_background_ratio = 5 # Avoid the very large writeback bursts caused by the historical value of 60. vm.dirty_ratio = 20 # Retain inode/dentry caches longer than the default on filesystem-heavy hosts. vm.vfs_cache_pressure = 50 # ----------------------------------------------------------------------------- # File descriptor capacity # ----------------------------------------------------------------------------- # System-wide file-handle ceiling. # Per-process RLIMIT_NOFILE / systemd LimitNOFILE must also be configured. fs.file-max = 2097152 # ----------------------------------------------------------------------------- # Socket buffer ceilings # ----------------------------------------------------------------------------- # Useful starting point for high-bandwidth or higher-RTT servers. net.core.rmem_max = 16777216 net.core.wmem_max = 16777216 # Keep default per-socket allocation moderate. net.core.rmem_default = 262144 net.core.wmem_default = 262144 # ----------------------------------------------------------------------------- # TCP auto-tuning # ----------------------------------------------------------------------------- # min / default / max net.ipv4.tcp_rmem = 4096 131072 16777216 net.ipv4.tcp_wmem = 4096 16384 16777216 net.ipv4.tcp_window_scaling = 1 # ----------------------------------------------------------------------------- # Connection queues # ----------------------------------------------------------------------------- # Modern kernels commonly default to 4096; keep explicit for server roles. net.core.somaxconn = 4096 # Increased SYN queue for bursty server workloads. net.ipv4.tcp_max_syn_backlog = 16384 # Increased device receive backlog for traffic bursts. net.core.netdev_max_backlog = 16384 # ----------------------------------------------------------------------------- # Outbound connection capacity # ----------------------------------------------------------------------------- # Useful for reverse proxies and clients opening many outbound connections. net.ipv4.ip_local_port_range = 10240 65535 # ----------------------------------------------------------------------------- # TCP behavior # ----------------------------------------------------------------------------- net.ipv4.tcp_timestamps = 1 net.ipv4.tcp_sack = 1 net.ipv4.tcp_tw_reuse = 1 # Keep current kernel behavior unless measurements show a specific problem. net.ipv4.tcp_fin_timeout = 60 net.ipv4.tcp_no_metrics_save = 0
High connection-rate web server or reverse proxy
For NGINX, HAProxy, Envoy, Apache event MPM, API gateways and similar workloads:
net.ipv4.tcp_max_syn_backlog = 32768 net.core.somaxconn = 16384 net.core.netdev_max_backlog = 32768 net.ipv4.ip_local_port_range = 10240 65535
Also check application-side limits:
output 5 lines
listen() backlog worker/process count RLIMIT_NOFILE systemd LimitNOFILE application connection limits
Measure queue overflows:
$ nstat -az | grep -E 'ListenOverflows|ListenDrops|Syncookies' $ ss -lnt
Do not enable this as a generic optimization:
net.ipv4.tcp_abort_on_overflow = 1
The kernel documentation warns that it can harm clients. Increase queue capacity or improve the application's accept rate first.
High-throughput load balancer or network server
For an L4 load balancer, forwarding appliance, proxy or high-throughput gateway:
net.core.somaxconn = 16384 net.ipv4.tcp_max_syn_backlog = 32768 net.core.netdev_max_backlog = 32768 net.core.rmem_max = 33554432 net.core.wmem_max = 33554432 net.ipv4.tcp_rmem = 4096 131072 33554432 net.ipv4.tcp_wmem = 4096 16384 33554432 net.ipv4.ip_local_port_range = 10240 65535
Floating / virtual IP addresses
High-availability load balancers may also need:
net.ipv4.ip_nonlocal_bind = 1
This lets the service bind a VIP before the address is present locally.
IP forwarding
Enable only for designs that actually forward packets:
net.ipv4.ip_forward = 1
For IPv6 forwarding:
net.ipv6.conf.all.forwarding = 1
A userspace reverse proxy normally does not need IP forwarding.
Bandwidth-delay product
TCP buffer sizing should be related to bandwidth-delay product:
output 1 line
BDP = bandwidth x round-trip time
Example for a 10 Gbit/s path with 20 ms RTT:
output 3 lines
10,000,000,000 bit/s x 0.020 s = 200,000,000 bits = approximately 25 MB
A socket window materially below the BDP can limit single-flow throughput.
This is why 16-32 MiB maximum buffers may be reasonable for WAN-facing high-bandwidth systems while providing little benefit on ordinary low-latency LAN servers.
Congestion control
Inspect what the kernel provides:
$ sysctl net.ipv4.tcp_available_congestion_control $ sysctl net.ipv4.tcp_congestion_control
cubic is a strong general-purpose default.
If the installed kernel provides BBR, benchmark it under the actual workload before enabling it:
net.ipv4.tcp_congestion_control = bbrAlso inspect the queuing discipline:
$ sysctl net.core.default_qdisc $ tc qdisc show
Congestion control should be evaluated using realistic RTT, packet loss and bandwidth conditions.
Low-latency networking
Modern Linux supports NAPI busy polling. It trades CPU cycles and power consumption for lower packet-delivery latency.
Global examples:
# Example only - microseconds. # net.core.busy_read = 50 # net.core.busy_poll = 50
Do not put these in a generic profile. Current kernels support per-socket and per-epoll controls, which are preferable where the application supports them.
Measure:
output 5 lines
p50 / p95 / p99 latency CPU utilization softirq utilization IRQ distribution power consumption
Database or storage-heavy server
A conservative latency-oriented starting point is:
vm.swappiness = 1 vm.dirty_background_ratio = 5 vm.dirty_ratio = 15 vm.vfs_cache_pressure = 50 fs.file-max = 2097152
Vendor recommendations should take precedence.
Do not raise:
vm.max_map_count
without an application requirement. The historical value 2097152 is not a universal performance optimization.
Applications such as Elasticsearch/OpenSearch may explicitly require a higher value, but that belongs in the application profile rather than a generic Linux performance file.
File descriptor capacity
The historical:
fs.file-max = 2097152
is still a reasonable ceiling for connection-heavy servers.
However, the service also needs an adequate process limit:
$ ulimit -n $ cat /proc/<PID>/limits
For systemd:
[Service] LimitNOFILE=1048576
A higher descriptor limit does not by itself improve performance; it only prevents the descriptor ceiling from becoming the bottleneck.
NIC and CPU tuning
For high packet rates, NIC/CPU placement often matters more than sysctl values.
Inspect queue and ring configuration:
$ ethtool -l eth0 $ ethtool -g eth0 $ ethtool -S eth0
Inspect interrupt distribution:
$ grep -i eth0 /proc/interrupts
Inspect kernel receive processing:
$ cat /proc/net/softnet_stat $ ip -s -s link show dev eth0
Evaluate:
output 8 lines
RSS RPS / RFS IRQ affinity NUMA locality NIC ring sizes GRO / GSO / TSO CPU frequency governor application worker-to-CPU placement
Measurement checklist
Before and after changing sysctl values:
$ ss -s $ nstat -az $ cat /proc/net/sockstat $ cat /proc/net/softnet_stat $ vmstat 1 $ iostat -xz 1 $ sar -n DEV,TCP,ETCP 1
Important indicators:
output 11 lines
TCP ListenOverflows / ListenDrops SYN retransmissions TCP retransmissions TIME-WAIT count socket memory consumption softnet drops NIC RX/TX drops disk await / queue depth dirty-memory writeback spikes CPU softirq utilization p50 / p95 / p99 application latency
Suggested profiles
General server
vm.swappiness = 10 vm.dirty_background_ratio = 5 vm.dirty_ratio = 20 vm.vfs_cache_pressure = 50 fs.file-max = 2097152 net.core.rmem_max = 16777216 net.core.wmem_max = 16777216 net.core.rmem_default = 262144 net.core.wmem_default = 262144 net.ipv4.tcp_rmem = 4096 131072 16777216 net.ipv4.tcp_wmem = 4096 16384 16777216 net.core.somaxconn = 4096 net.ipv4.tcp_max_syn_backlog = 16384 net.core.netdev_max_backlog = 16384 net.ipv4.ip_local_port_range = 10240 65535 net.ipv4.tcp_window_scaling = 1 net.ipv4.tcp_timestamps = 1 net.ipv4.tcp_sack = 1 net.ipv4.tcp_tw_reuse = 1
High-throughput proxy / load balancer
fs.file-max = 2097152 net.core.rmem_max = 33554432 net.core.wmem_max = 33554432 net.ipv4.tcp_rmem = 4096 131072 33554432 net.ipv4.tcp_wmem = 4096 16384 33554432 net.core.somaxconn = 16384 net.ipv4.tcp_max_syn_backlog = 32768 net.core.netdev_max_backlog = 32768 net.ipv4.ip_local_port_range = 10240 65535 net.ipv4.tcp_window_scaling = 1 net.ipv4.tcp_timestamps = 1 net.ipv4.tcp_sack = 1 net.ipv4.tcp_tw_reuse = 1 # HA/VIP deployments only: # net.ipv4.ip_nonlocal_bind = 1 # L3/L4 forwarding only: # net.ipv4.ip_forward = 1
Database / storage starting point
vm.swappiness = 1 vm.dirty_background_ratio = 5 vm.dirty_ratio = 15 vm.vfs_cache_pressure = 50 fs.file-max = 2097152
References
- Linux kernel - IP sysctl: https://docs.kernel.org/networking/ip-sysctl.html
- Linux kernel - network sysctl: https://docs.kernel.org/admin-guide/sysctl/net.html
- Linux kernel - VM sysctl: https://docs.kernel.org/admin-guide/sysctl/vm.html
- Linux kernel - filesystem sysctl: https://docs.kernel.org/admin-guide/sysctl/fs.html
- Linux kernel - NAPI: https://docs.kernel.org/networking/napi.html
- Linux kernel - network interface statistics: https://docs.kernel.org/networking/statistics.html
- ss(8): https://man7.org/linux/man-pages/man8/ss.8.html
- ethtool(8): https://man7.org/linux/man-pages/man8/ethtool.8.html