Linux Kernel Tuning for High-Traffic WordPress: sysctl and TCP Optimisations

The default Linux kernel parameters are tuned conservatively for general workloads. A WordPress server handling thousands of concurrent visitors benefits from a small set of sysctl tweaks: larger TCP buffers, faster TIME_WAIT recycling, increased file-descriptor limits, and a more aggressive vm.swappiness. These changes require no reboot and can be applied live.

Problem: A WordPress server under traffic spikes shows high kernel CPU usage, TCP backlog drops, and connection timeouts — the Linux network stack is tuned for desktop defaults, not for a high-concurrency web server.

Solution: Tune net.core.somaxconn, net.ipv4.tcp_max_syn_backlog, and net.ipv4.tcp_fin_timeout via sysctl to handle more concurrent connections. Enable TCP BBR congestion control with net.ipv4.tcp_congestion_control = bbr for better throughput. Set fs.file-max and PHP-FPM rlimit_files to avoid file descriptor exhaustion.


The configuration below covers the most impactful TCP and memory settings for a LEMP (Linux, Nginx, MySQL, PHP-FPM) WordPress stack, explains each parameter, and shows how to make the changes permanent via /etc/sysctl.d/.


# /etc/sysctl.d/99-wordpress.conf
# Apply with: sysctl --system   (or: sysctl -p /etc/sysctl.d/99-wordpress.conf)

# ── TCP send/receive buffer sizes (bytes) ────────────────────────────────
net.core.rmem_max          = 134217728   # 128 MiB max read  buffer
net.core.wmem_max          = 134217728   # 128 MiB max write buffer
net.ipv4.tcp_rmem          = 4096 87380 67108864
net.ipv4.tcp_wmem          = 4096 65536 67108864

# ── Increase listen backlog and connection queue ──────────────────────────
net.core.somaxconn         = 65535
net.ipv4.tcp_max_syn_backlog = 65535

# ── Recycle TIME_WAIT sockets faster (safe behind a load balancer) ────────
net.ipv4.tcp_tw_reuse      = 1
net.ipv4.tcp_fin_timeout   = 15

# ── BBR congestion control (requires kernel 4.9+) ─────────────────────────
net.core.default_qdisc     = fq
net.ipv4.tcp_congestion_control = bbr

# ── File descriptor limits ────────────────────────────────────────────────
fs.file-max                = 2097152

# ── Virtual memory: avoid swapping unless absolutely necessary ────────────
vm.swappiness              = 10          # default 60 — reduce swap use
vm.dirty_ratio             = 15
vm.dirty_background_ratio  = 5


# Verify BBR is active after reboot
sysctl net.ipv4.tcp_congestion_control
# Expected: net.ipv4.tcp_congestion_control = bbr

# Check current open file descriptors vs limit
cat /proc/sys/fs/file-nr
# columns: open_fds  0  max_fds

# Raise per-process fd limit for Nginx and PHP-FPM in their systemd unit
# /etc/systemd/system/nginx.service.d/override.conf
# [Service]
# LimitNOFILE=65535
systemctl daemon-reload && systemctl restart nginx php8.2-fpm


NOTE: net.ipv4.tcp_tw_reuse = 1 is only safe when your server sits behind a NAT or load balancer; on a server with a direct public IP, enabling it alongside tcp_timestamps = 1 (the default) is sufficient — never enable the deprecated tcp_tw_recycle option as it breaks connections from NAT'd clients.