VPS Network and TCP Kernel Tuning: Maximize Throughput, Reduce Latency, and Handle More Connections
The Linux kernel’s default network settings are conservative — optimized for general compatibility rather than VPS workloads handling thousands of concurrent connections. A few sysctl parameter changes can significantly reduce latency, increase throughput, and allow your VPS to handle 10–100× more simultaneous connections without additional hardware. This guide covers the highest-impact TCP kernel settings for web server workloads.
Before Tuning: Understand the Baseline
<code"># Current connection count ss -s # Or: netstat -s | grep -E "connections|packets" # Maximum file descriptors (related to max connections) ulimit -n # Per-process limit cat /proc/sys/fs/file-max # System-wide limit # Current TCP settings sysctl net.core.somaxconn sysctl net.ipv4.tcp_max_syn_backlog sysctl net.core.rmem_max sysctl net.core.wmem_max
Step 1: Apply Kernel Tuning Settings
<code">sudo nano /etc/sysctl.d/99-vps-performance.conf
<code"># ───────────────────────────────────────────────────────── # FILE DESCRIPTORS — maximum open files (connections) # ───────────────────────────────────────────────────────── fs.file-max = 2097152 fs.nr_open = 2097152 # ───────────────────────────────────────────────────────── # NETWORK — connection queues # ───────────────────────────────────────────────────────── # Maximum connections waiting in Nginx/application accept queue # Default is 128 — too low for busy servers net.core.somaxconn = 65535 # Maximum SYN backlog (half-open connections waiting to complete handshake) net.ipv4.tcp_max_syn_backlog = 65535 # ───────────────────────────────────────────────────────── # TCP BUFFERS — throughput for high-bandwidth connections # ───────────────────────────────────────────────────────── # Socket receive buffer size (min, default, max) in bytes net.core.rmem_default = 262144 net.core.rmem_max = 67108864 # 64 MB max receive buffer # Socket send buffer size net.core.wmem_default = 262144 net.core.wmem_max = 67108864 # 64 MB max send buffer # TCP receive/send buffer auto-tuning range (min, pressure, max) net.ipv4.tcp_rmem = 4096 87380 67108864 net.ipv4.tcp_wmem = 4096 65536 67108864 # ───────────────────────────────────────────────────────── # BBR CONGESTION CONTROL — better throughput over the internet # ───────────────────────────────────────────────────────── # Enable BBR (Google's TCP congestion control algorithm — better than CUBIC) # BBR significantly improves throughput for connections with packet loss net.core.default_qdisc = fq net.ipv4.tcp_congestion_control = bbr # ───────────────────────────────────────────────────────── # TIME_WAIT — reclaim ports faster # ───────────────────────────────────────────────────────── # Maximum number of TIME_WAIT sockets net.ipv4.tcp_max_tw_buckets = 2000000 # Reuse TIME_WAIT sockets for outbound connections net.ipv4.tcp_tw_reuse = 1 # Fast recycling of TIME_WAIT sockets (for outbound connections) # net.ipv4.tcp_tw_recycle = 1 # REMOVED in kernel 4.12+ — don't use # TIME_WAIT timeout (seconds) — default is 60 # Lower to free ports faster (safe for web server workloads) net.ipv4.tcp_fin_timeout = 30 # ───────────────────────────────────────────────────────── # CONNECTION HANDLING # ───────────────────────────────────────────────────────── # Number of times to retry SYN before giving up net.ipv4.tcp_syn_retries = 2 # Reduce SYN-ACK retry count (faster failure detection) net.ipv4.tcp_synack_retries = 2 # Keepalive: detect dead connections faster net.ipv4.tcp_keepalive_time = 600 # Start keepalive probes after 600s idle net.ipv4.tcp_keepalive_intvl = 30 # Probe interval net.ipv4.tcp_keepalive_probes = 5 # Probes before declaring dead # ───────────────────────────────────────────────────────── # SECURITY # ───────────────────────────────────────────────────────── # SYN cookies: protection against SYN flood attacks net.ipv4.tcp_syncookies = 1 # Ignore ICMP redirects (prevent routing manipulation) net.ipv4.conf.all.accept_redirects = 0 net.ipv6.conf.all.accept_redirects = 0 # Ignore source routing (prevent IP spoofing) net.ipv4.conf.all.accept_source_route = 0 # Log martian packets (packets with impossible source addresses) net.ipv4.conf.all.log_martians = 1 # ───────────────────────────────────────────────────────── # MEMORY # ───────────────────────────────────────────────────────── # Shared memory (for PostgreSQL, etc.) kernel.shmmax = 68719476736 # 64 GB kernel.shmall = 4294967296
<code"># Apply settings immediately sudo sysctl -p /etc/sysctl.d/99-vps-performance.conf # Verify BBR is active sysctl net.ipv4.tcp_congestion_control # Should output: net.ipv4.tcp_congestion_control = bbr
Step 2: Increase File Descriptor Limits
<code">sudo nano /etc/security/limits.conf
<code"># Add at the end: * soft nofile 1048576 * hard nofile 1048576 root soft nofile 1048576 root hard nofile 1048576
<code">sudo nano /etc/systemd/system.conf
<code">[Manager] DefaultLimitNOFILE=1048576
<code">sudo systemctl daemon-reexec # Log out and back in, then verify: ulimit -n # Should show 1048576
Step 3: Configure Nginx to Use Increased Limits
<code">sudo nano /etc/nginx/nginx.conf
<code">worker_processes auto;
worker_rlimit_nofile 1048576; # Match the system limit
events {
worker_connections 65535; # Was 1024 — now much higher
use epoll; # Linux event model (fastest on Linux)
multi_accept on; # Accept multiple connections at once
}
http {
# TCP optimizations
tcp_nopush on; # Send headers + first data chunk together
tcp_nodelay on; # Disable Nagle algorithm — lower latency
sendfile on; # Use sendfile() for static files (zero-copy)
}
<code">sudo nginx -t && sudo systemctl reload nginx
Step 4: Verify BBR is Working
<code"># Confirm BBR is the active congestion control algorithm sysctl net.ipv4.tcp_congestion_control # Output: net.ipv4.tcp_congestion_control = bbr # Check available congestion control algorithms sysctl net.ipv4.tcp_available_congestion_control # If BBR is not available (older kernel): sudo modprobe tcp_bbr echo "tcp_bbr" | sudo tee -a /etc/modules-load.d/bbr.conf sudo sysctl net.ipv4.tcp_congestion_control=bbr
Step 5: Benchmark Before and After
<code"># Test TCP connection handling ab -n 10000 -c 500 https://yourdomain.com/ # Test with a tool that handles connection reuse better wrk -t4 -c400 -d30s https://yourdomain.com/ # Network throughput test (from another server) iperf3 -s # Server mode on VPS iperf3 -c YOUR_VPS_IP # Client mode from another server
Settings to Avoid
net.ipv4.tcp_tw_recycle: Removed in kernel 4.12 — using it causes problems with NAT and load balancersnet.ipv4.ip_local_port_rangeextremes: Default32768 60999is usually fine; only expand if seeing “Cannot assign requested address” errors- Very large
somaxconn(>65535): Generally diminishing returns; fix your application’s connection handling instead
Getting Started
These kernel settings are safe to apply on any Ubuntu VPS at VPS.DO running kernel 4.9+ (all current Ubuntu LTS versions). The BBR congestion control algorithm provides the largest real-world improvement for internet-facing services — improved throughput for users on high-latency or lossy connections (mobile, intercontinental). Settings survive reboots when placed in /etc/sysctl.d/.
Conclusion
Linux TCP kernel tuning delivers three main benefits: higher connection capacity (via increased somaxconn and file descriptor limits), better throughput (via BBR congestion control and larger socket buffers), and faster port recycling (via tcp_tw_reuse and reduced tcp_fin_timeout). These changes require no additional software, take effect immediately with sysctl -p, and persist across reboots. For high-traffic VPS deployments, kernel tuning is often the difference between a server that struggles at 1,000 concurrent connections and one that handles 10,000+.