Linux kernel tuning is the deepest layer of server optimization — below your application code, below your database queries, below your web server configuration. The kernel controls how your VPS handles TCP connections, manages memory, allocates file descriptors, and schedules I/O. Default kernel parameters are conservative, designed to work safely across every possible workload. By tuning them for your specific workload — web server, database, API gateway, container host — you can dramatically improve throughput, reduce latency, and handle more concurrent connections on the same hardware.
This guide covers practical sysctl tuning for Ubuntu VPS environments, focusing on the parameters that actually matter for virtualized servers. We'll skip the parameters that only apply to bare metal (I/O schedulers, NUMA, CPU governors) and focus on what you can control: TCP/IP stack behavior, memory management, and connection handling limits.
MassiveGRID Ubuntu VPS includes: Ubuntu 24.04 LTS pre-installed · Proxmox HA cluster with automatic failover · Ceph 3x replicated NVMe storage · Independent CPU/RAM/storage scaling · 12 Tbps DDoS protection · 4 global datacenter locations · 100% uptime SLA · 24/7 human support rated 9.5/10
Deploy a self-managed VPS — from $1.99/mo
Need dedicated resources? — from $19.80/mo
Want fully managed hosting? — we handle everything
Where Kernel Tuning Fits in the Optimization Stack
Before touching kernel parameters, make sure you've optimized the layers above first. The optimization stack, from highest impact to lowest:
- Application code — inefficient queries, N+1 problems, unoptimized algorithms. This is where 90% of performance gains live.
- Database configuration — connection pooling, query caching, index optimization, buffer pool sizing.
- Web server / reverse proxy — worker processes, connection limits, keepalive settings, static file handling.
- Kernel parameters — TCP stack tuning, memory management, file descriptor limits.
Kernel tuning amplifies the performance of the layers above. If your application code is slow, no amount of TCP tuning will save it. But if your application is well-optimized and you're pushing the limits of concurrent connections or throughput, kernel tuning removes the bottleneck at the OS level.
Prerequisites
You need an Ubuntu 24.04 VPS with root or sudo access. On a MassiveGRID Cloud VPS, the host handles hardware-level optimizations. Your kernel tuning focuses on TCP/IP stack, memory management, and connection handling — the guest OS parameters that directly affect your application's performance.
Before tuning, establish performance baselines. Load test and benchmark your Ubuntu VPS to measure current throughput, latency, and connection limits. Without a baseline, you can't measure whether your changes improved anything.
# Record current sysctl values before changing anything
sudo sysctl -a > ~/sysctl-defaults-$(date +%Y%m%d).txt
# Check current kernel version
uname -r
# Check available memory and current usage
free -h
# Check current file descriptor limits
ulimit -n
cat /proc/sys/fs/file-max
Understanding sysctl
The sysctl command reads and writes kernel parameters at runtime. Parameters are organized in a tree structure under /proc/sys/ — for example, net.core.somaxconn corresponds to the file /proc/sys/net/core/somaxconn.
# Read a single parameter
sysctl net.core.somaxconn
# Set a parameter (takes effect immediately, lost on reboot)
sudo sysctl -w net.core.somaxconn=65535
# Apply all settings from a file
sudo sysctl -p /etc/sysctl.d/99-custom.conf
# Apply all settings from all sysctl.d files
sudo sysctl --system
To make changes persistent across reboots, create a configuration file in /etc/sysctl.d/. Files are loaded in alphabetical order, so use a high number prefix to ensure your settings override defaults:
# Create a custom tuning profile
sudo nano /etc/sysctl.d/99-performance.conf
# After editing, apply:
sudo sysctl -p /etc/sysctl.d/99-performance.conf
TCP/IP Stack Tuning
TCP stack parameters have the highest impact on VPS performance because network I/O is typically the primary bottleneck. These settings control how the kernel handles incoming connections, manages TCP state, and buffers network data.
Connection Queue and Backlog
# Maximum number of connections queued for acceptance
# Default: 4096 — increase for high-traffic web servers
net.core.somaxconn = 65535
# Maximum SYN backlog (pending connections waiting for ACK)
# Default: 1024 — increase to handle SYN floods and traffic bursts
net.ipv4.tcp_max_syn_backlog = 65535
# Queue length for incoming packets before kernel processing
# Default: 1000 — increase if you see packet drops at high traffic
net.core.netdev_max_backlog = 16384
When somaxconn is too low, the kernel drops new connections silently during traffic spikes. Your web server's listen backlog is capped at this value — even if Nginx sets listen 80 backlog=65535, the kernel enforces its own limit.
TCP Timeouts and Connection Recycling
# Time to hold socket in FIN-WAIT-2 state
# Default: 60 — reduce to free sockets faster
net.ipv4.tcp_fin_timeout = 15
# Allow reuse of TIME-WAIT sockets for new connections
# Default: 2 (enabled for loopback only since kernel 4.12)
# Set to 1 for outbound connections, 2 for both
net.ipv4.tcp_tw_reuse = 1
# Keep-alive interval (seconds between keepalive probes)
# Default: 7200 (2 hours) — reduce for faster dead connection detection
net.ipv4.tcp_keepalive_time = 300
net.ipv4.tcp_keepalive_intvl = 30
net.ipv4.tcp_keepalive_probes = 5
On a busy web server handling thousands of short-lived connections, sockets in TIME_WAIT state can exhaust the connection table. Reducing tcp_fin_timeout and enabling tcp_tw_reuse frees these sockets faster.
TCP Buffer Sizes and Window Scaling
# Maximum receive/send buffer sizes (bytes) for all connections
# Default: 212992 — increase for high-bandwidth transfers
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
# TCP auto-tuning buffer sizes: min, default, max (bytes)
# Kernel auto-tunes between min and max based on connection needs
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 65536 16777216
# Enable window scaling (allows TCP windows > 64KB)
# Default: 1 — should already be enabled, verify it
net.ipv4.tcp_window_scaling = 1
Buffer sizes matter most for connections with high bandwidth-delay products (long-distance or high-throughput transfers). The kernel auto-tunes within the min/max range, so setting a high max doesn't waste memory — it just raises the ceiling.
TCP Congestion and Performance
# Use BBR congestion control (better than cubic for most workloads)
net.core.default_qdisc = fq
net.ipv4.tcp_congestion_control = bbr
# Enable TCP Fast Open for both client and server
# Reduces latency by 1 RTT on repeat connections
net.ipv4.tcp_fastopen = 3
# Enable MTU probing to optimize packet sizes
net.ipv4.tcp_mtu_probing = 1
BBR (Bottleneck Bandwidth and RTT) is Google's congestion control algorithm that performs significantly better than the default cubic on lossy networks and high-latency connections. It maximizes throughput while maintaining low latency.
Memory Management Tuning
Memory parameters control how the kernel uses RAM for caching, handles swap, and manages dirty pages waiting to be written to disk.
Swap Behavior
# How aggressively the kernel swaps memory pages to disk
# Default: 60 — reduce for server workloads
# 10 = swap only when memory pressure is high
# 0 = swap only to avoid OOM (not recommended)
vm.swappiness = 10
For detailed guidance on swap configuration, see Ubuntu VPS swap memory management. On a VPS with adequate RAM, low swappiness keeps your application data in memory where access is fast.
Dirty Page Ratios
# Percentage of total memory that can hold dirty pages
# before processes are forced to write them (blocking)
# Default: 20 — reduce for more consistent I/O
vm.dirty_ratio = 10
# Percentage of total memory at which background writeback starts
# Default: 10 — reduce for earlier, gentler flushing
vm.dirty_background_ratio = 5
# Maximum time dirty pages stay in memory (centiseconds)
# Default: 3000 (30s) — reduce for lower data loss risk
vm.dirty_expire_centisecs = 1500
# How often the kernel checks for dirty pages (centiseconds)
# Default: 500 (5s)
vm.dirty_writeback_centisecs = 500
Lower dirty ratios mean the kernel flushes data to disk in smaller, more frequent batches rather than large bursts. This produces more consistent I/O latency — important for database workloads where a sudden flush can cause a latency spike.
Overcommit and Cache Pressure
# Memory overcommit behavior
# 0 = heuristic overcommit (default, kernel guesses)
# 1 = always overcommit (useful for Redis, which forks for persistence)
# 2 = never overcommit (strict, can cause allocation failures)
vm.overcommit_memory = 0
# How aggressively the kernel reclaims memory from directory/inode caches
# Default: 100 — reduce if you want to keep filesystem metadata cached
# 50 = reclaim cache half as aggressively
vm.vfs_cache_pressure = 50
If you're running Redis, set vm.overcommit_memory = 1. Redis uses fork() for background persistence (RDB snapshots and AOF rewrites), which theoretically doubles memory usage briefly. Without overcommit, the fork can fail on a server with high memory usage. See installing Redis on Ubuntu VPS for details.
Connection Handling Tuning
These parameters control system-wide limits on file descriptors, network connections, and port ranges. They're often the first bottleneck hit on busy servers.
# System-wide maximum file descriptors
# Default: varies (~100000) — increase for servers with many connections
fs.file-max = 2097152
# Range of local ports for outbound connections
# Default: 32768-60999 — expand for servers making many outbound connections
# (reverse proxies, API gateways, microservice clients)
net.ipv4.ip_local_port_range = 1024 65535
# Maximum number of connection tracking entries (for servers with iptables/nftables)
# Default: 65536 — increase if you see "nf_conntrack: table full" in dmesg
net.netfilter.nf_conntrack_max = 262144
Besides sysctl, you also need to increase per-process file descriptor limits via systemd or /etc/security/limits.conf:
# /etc/security/limits.conf
* soft nofile 65535
* hard nofile 65535
root soft nofile 65535
root hard nofile 65535
For systemd-managed services, override the limit in the service unit:
# sudo systemctl edit nginx.service
[Service]
LimitNOFILE=65535
What NOT to Tune on a VPS
Some kernel tuning guides written for bare metal servers include parameters that are irrelevant or even harmful on a VPS:
- I/O schedulers (
elevator=noop) — VPS disk I/O goes through the hypervisor's storage layer. The guest's I/O scheduler has minimal impact because the host manages actual disk scheduling. Changing it adds overhead without benefit. - NUMA tuning (
vm.zone_reclaim_mode) — VPS instances don't have NUMA topology. The hypervisor presents a flat memory model to the guest. - CPU governors (
cpufreq) — CPU frequency scaling is controlled by the host. The guest cannot change CPU clock speeds. - Huge pages (
vm.nr_hugepages) — while technically possible in a VPS, huge pages require coordinated allocation with the hypervisor. Unless your provider explicitly supports it, enabling huge pages can cause memory allocation failures. - Transparent Huge Pages (THP) — on some database workloads (MongoDB, Redis), THP causes latency spikes. Disabling it is valid, but the performance impact on a VPS may differ from bare metal benchmarks.
- Network interface offloading (
ethtool -K) — the virtual NIC's offloading capabilities are determined by the hypervisor, and changing them rarely helps.
Creating a Tuning Profile
Organize your sysctl settings into a single file for easy management and reproducibility:
sudo tee /etc/sysctl.d/99-vps-performance.conf > /dev/null << 'EOF'
# ==============================================
# VPS Performance Tuning Profile
# Ubuntu 24.04 LTS
# ==============================================
# --- TCP/IP Stack ---
net.core.somaxconn = 65535
net.ipv4.tcp_max_syn_backlog = 65535
net.core.netdev_max_backlog = 16384
net.ipv4.tcp_fin_timeout = 15
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_keepalive_time = 300
net.ipv4.tcp_keepalive_intvl = 30
net.ipv4.tcp_keepalive_probes = 5
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 65536 16777216
net.ipv4.tcp_window_scaling = 1
net.core.default_qdisc = fq
net.ipv4.tcp_congestion_control = bbr
net.ipv4.tcp_fastopen = 3
net.ipv4.tcp_mtu_probing = 1
# --- Memory Management ---
vm.swappiness = 10
vm.dirty_ratio = 10
vm.dirty_background_ratio = 5
vm.dirty_expire_centisecs = 1500
vm.vfs_cache_pressure = 50
# --- Connection Handling ---
fs.file-max = 2097152
net.ipv4.ip_local_port_range = 1024 65535
# --- Security Hardening (also improves stability) ---
net.ipv4.tcp_syncookies = 1
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
net.ipv4.icmp_echo_ignore_broadcasts = 1
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
net.ipv6.conf.all.accept_redirects = 0
net.ipv6.conf.default.accept_redirects = 0
EOF
# Apply the profile
sudo sysctl -p /etc/sysctl.d/99-vps-performance.conf
# Verify key settings
sysctl net.core.somaxconn net.ipv4.tcp_congestion_control vm.swappiness
Benchmarking Before and After
Measure the impact of your changes by running the same benchmarks before and after tuning. Use load testing tools to generate realistic traffic patterns.
# Quick connection throughput test with wrk
# Run this BEFORE tuning, record results, then repeat AFTER
wrk -t4 -c400 -d30s http://localhost/
# Test maximum concurrent connections
wrk -t2 -c1000 -d60s http://localhost/
# Check for SYN queue overflows (should be 0 after tuning)
netstat -s | grep -i "syn"
cat /proc/net/netstat | grep -i "listenoverflows\|listendrops"
# Monitor connection states
ss -s
# Watch for dropped packets
cat /proc/net/softnet_stat
After tuning, benchmark on a Dedicated VPS for consistent baselines. Shared resources can introduce variability that makes it difficult to isolate the effect of kernel parameter changes.
Focus on these metrics before and after:
- Requests per second — did throughput increase?
- P99 latency — did tail latency decrease?
- Error rate under load — did connection resets decrease?
- Maximum concurrent connections — can the server handle more simultaneous clients?
- SYN queue drops — are connection attempts being rejected?
Workload-Specific Profiles
Different workloads benefit from different tuning priorities. Here are adjustments to layer on top of the base profile:
Web Server Profile (Nginx, Apache)
# /etc/sysctl.d/99-webserver.conf
# High connection count, short-lived connections
net.core.somaxconn = 65535
net.ipv4.tcp_max_syn_backlog = 65535
net.ipv4.tcp_fin_timeout = 10
net.ipv4.tcp_tw_reuse = 1
# Aggressive keepalive detection
net.ipv4.tcp_keepalive_time = 120
net.ipv4.tcp_keepalive_intvl = 15
net.ipv4.tcp_keepalive_probes = 5
Database Profile (PostgreSQL, MySQL)
# /etc/sysctl.d/99-database.conf
# Lower dirty ratios for consistent write latency
vm.dirty_ratio = 5
vm.dirty_background_ratio = 2
# Keep filesystem metadata cached
vm.vfs_cache_pressure = 30
# Larger shared memory for database buffers
kernel.shmmax = 4294967296
kernel.shmall = 1048576
# Less aggressive swapping — keep data in memory
vm.swappiness = 5
API / Microservice Profile
# /etc/sysctl.d/99-api.conf
# Many outbound connections to other services
net.ipv4.ip_local_port_range = 1024 65535
net.ipv4.tcp_tw_reuse = 1
# Fast connection establishment
net.ipv4.tcp_fastopen = 3
# Higher backlog for bursty traffic
net.core.somaxconn = 65535
net.core.netdev_max_backlog = 32768
Docker Host Profile
# /etc/sysctl.d/99-docker.conf
# IP forwarding (required for Docker networking)
net.ipv4.ip_forward = 1
# Higher conntrack limit for many containers
net.netfilter.nf_conntrack_max = 524288
# More file descriptors for container processes
fs.file-max = 2097152
# More inotify watchers for container file systems
fs.inotify.max_user_watches = 524288
fs.inotify.max_user_instances = 1024
Prefer Expert Tuning?
Kernel tuning requires understanding which parameters matter for your specific workload, testing changes carefully, and monitoring for regressions. The wrong setting can cause memory pressure, connection drops, or subtle performance degradation that's hard to diagnose.
MassiveGRID managed servers include workload-specific kernel optimization as part of the service. The operations team profiles your workload, applies appropriate tuning, benchmarks the results, and monitors for issues — so your kernel parameters evolve with your application rather than being set once and forgotten.