Home/Engineer/OS / Systems/Linux Kernel Parameter Tunin…
OS / SystemsAdvancedUbuntuDebianCentOSRHELsysctlOSlinux

Linux Kernel Parameter Tuning — sysctl · Network Stack · Memory Optimization

An in-depth guide to controlling the TCP stack, file descriptors, and memory management with sysctl, and applying optimal parameters for production workloads (high-traffic web servers, databases, and container hosts).

Contents
  1. Basic sysctl Usage
  2. 1. Network Stack Tuning
  3. 2. File Descriptor and inotify Limits
  4. 3. Memory Management Tuning
  5. 4. cgroups v2 — Resource Isolation
  6. 5. NUMA Architecture Optimization
  7. 6. Recommended Profiles by Workload

Basic sysctl Usage

sysctl is an interface for reading and writing kernel parameters while the kernel is running.

Bash
# Query the current value
sysctl net.core.somaxconn
sysctl -a | grep tcp_tw          # Pattern search

# Apply immediately (resets on reboot)
sudo sysctl -w net.core.somaxconn=65535

# Persist across reboots (/etc/sysctl.d/99-tuning.conf)
echo "net.core.somaxconn = 65535" | sudo tee -a /etc/sysctl.d/99-tuning.conf
sudo sysctl -p /etc/sysctl.d/99-tuning.conf

# Reload all settings
sudo sysctl --system

Parameters can also be accessed directly via the /proc/sys/ path. net.core.somaxconn = /proc/sys/net/core/somaxconn


1. Network Stack Tuning

TCP Connection Handling Limits

INI
# /etc/sysctl.d/99-network.conf

# SYN backlog queue size (prevents SYN drops under high concurrent connections)
net.core.somaxconn = 65535
net.ipv4.tcp_max_syn_backlog = 65535

# Network device input queue size
net.core.netdev_max_backlog = 65535

# Expand the local port range (when there are many outbound connections)
net.ipv4.ip_local_port_range = 1024 65535

TIME_WAIT Socket Reuse

Under a large volume of short-lived connections (HTTP/1.1 polling, microservice internal communication), TIME_WAIT sockets can exhaust available ports.

INI
# Allow reuse of sockets in TIME_WAIT (client side)
net.ipv4.tcp_tw_reuse = 1

# Shorten the FIN_WAIT2 timeout (default 60s → 15s)
net.ipv4.tcp_fin_timeout = 15

# Maximum number of TIME_WAIT buckets (saves memory)
net.ipv4.tcp_max_tw_buckets = 1440000
Bash
# Check the current socket state distribution
ss -s

# Monitor the TIME_WAIT count in real time
watch -n1 "ss -s | grep TIME-WAIT"

TCP Buffer Sizes — Throughput Optimization

In high-bandwidth (1 Gbps+) environments, small buffers constrain TCP window scaling and reduce throughput.

INI
# TCP socket read/write buffers (min / default / max, bytes)
net.core.rmem_default = 262144
net.core.rmem_max = 134217728     # 128MB
net.core.wmem_default = 262144
net.core.wmem_max = 134217728

# TCP auto-tuning range (min / pressure / max)
net.ipv4.tcp_rmem = 4096 87380 134217728
net.ipv4.tcp_wmem = 4096 65536 134217728

# Enable automatic buffer tuning
net.ipv4.tcp_moderate_rcvbuf = 1

# BBR congestion control algorithm (Linux 4.9+; outperforms CUBIC in high-RTT environments)
net.core.default_qdisc = fq
net.ipv4.tcp_congestion_control = bbr
Bash
# Check whether BBR is supported
sysctl net.ipv4.tcp_available_congestion_control

# Current congestion control algorithm
sysctl net.ipv4.tcp_congestion_control

# Measure TCP throughput (iperf3)
iperf3 -c TARGET_HOST -t 30 -P 4

Keepalive — Clearing Zombie Connections

INI
# Idle time before starting keepalive probes (default 7200s → 60s)
net.ipv4.tcp_keepalive_time = 60

# Interval between probe retries
net.ipv4.tcp_keepalive_intvl = 10

# Maximum number of probes (connection is dropped if exceeded)
net.ipv4.tcp_keepalive_probes = 6

2. File Descriptor and inotify Limits

Causes of and remedies for the "Too many open files" error on high-traffic servers.

INI
# /etc/sysctl.d/99-fs.conf

# System-wide maximum number of file handles
fs.file-max = 2097152

# inotify watcher limits (Kafka, Elasticsearch, Docker environments)
fs.inotify.max_user_watches = 524288
fs.inotify.max_user_instances = 512

# AIO limit (database I/O)
fs.aio-max-nr = 1048576
Bash
# /etc/security/limits.d/99-nofile.conf (per-process limits)
*    soft nofile 1048576
*    hard nofile 1048576
root soft nofile 1048576
root hard nofile 1048576
Bash
# Currently used file handles / maximum
cat /proc/sys/fs/file-nr      # allocated / unused / maximum

# Number of open files for a specific process
ls /proc/1234/fd | wc -l

# Top 10 processes by file-handle usage system-wide
lsof 2>/dev/null | awk '{print $1}' | sort | uniq -c | sort -rn | head 10

3. Memory Management Tuning

vm.swappiness — Swap Tendency

INI
# /etc/sysctl.d/99-memory.conf

# Swap aggressiveness (0 = minimize swap, 100 = use aggressively)
# Database servers: 1–10 recommended (protects the memory cache)
# General-purpose servers: 10–30
vm.swappiness = 10

# Dirty page ratio limits (prevents I/O bursts)
vm.dirty_ratio = 10           # Force flush when dirty pages exceed 10% of total memory
vm.dirty_background_ratio = 5 # Start background flush when dirty pages exceed 5%

# Maximum time dirty pages are kept (100 cs = 1 second)
vm.dirty_expire_centisecs = 3000   # 30 seconds
vm.dirty_writeback_centisecs = 500 # Wake the flush thread every 5 seconds
Bash
# Current dirty page status
cat /proc/meminfo | grep -i dirty

# Force a dirty-page flush (caution: causes an I/O spike)
sync; echo 3 | sudo tee /proc/sys/vm/drop_caches

Controlling the OOM Killer

Bash
# Check OOM score (higher = killed first; -1000 = never kill)
cat /proc/1234/oom_score
cat /proc/1234/oom_score_adj

# Protect a critical process from OOM (PostgreSQL example)
echo -500 | sudo tee /proc/$(pgrep -f "postgres: checkpointer")/oom_score_adj

# Set OOM policy at the systemd service unit level
# /etc/systemd/system/postgresql.service.d/override.conf
[Service]
OOMScoreAdjust=-500
INI
# Kill a process without panicking on OOM (keeping the default is recommended)
vm.panic_on_oom = 0
vm.oom_kill_allocating_task = 0

HugePages — Optimizing Large-Memory Databases

Processes that use large amounts of memory—such as PostgreSQL, Oracle, and Redis—can reduce TLB misses with HugePages.

Bash
# Current HugePage usage
grep -i huge /proc/meminfo

# Calculate the required number of HugePages (based on PostgreSQL shared_buffers)
# shared_buffers = 8GB, HugePage = 2MB
# Required count = 8 * 1024 / 2 = 4096

# Set the number of HugePages
echo "vm.nr_hugepages = 4096" | sudo tee -a /etc/sysctl.d/99-hugepages.conf
sudo sysctl -p /etc/sysctl.d/99-hugepages.conf

# Enable HugePages in PostgreSQL
# postgresql.conf: huge_pages = on

4. cgroups v2 — Resource Isolation

cgroups v2 lets you precisely limit CPU, memory, and I/O per process or container.

Bash
# Check whether cgroups v2 is mounted
mount | grep cgroup2
# or
stat -fc %T /sys/fs/cgroup/

# Inspect the current cgroup hierarchy
systemctl status --no-pager | head -5
cat /proc/1234/cgroup

Limiting Service Resources with systemd

Bash
# /etc/systemd/system/myapp.service.d/limits.conf
[Service]
# CPU: equivalent of 2 cores max (200%)
CPUQuota=200%

# Memory: 4 GB max (OOM-killed if exceeded)
MemoryMax=4G
MemorySwapMax=0       # Disable swap

# I/O: limit read/write to 100 MB/s each
IOReadBandwidthMax=/dev/sda 100M
IOWriteBandwidthMax=/dev/sda 100M

# Limit the number of processes
TasksMax=512
Bash
sudo systemctl daemon-reload
sudo systemctl restart myapp

# Check live resource usage
systemctl status myapp | grep -E "Memory|CPU|Tasks"

# Inspect cgroup stats directly
cat /sys/fs/cgroup/system.slice/myapp.service/memory.current
cat /sys/fs/cgroup/system.slice/myapp.service/cpu.stat

Manually Creating a cgroup and Assigning Processes

Bash
# Create a cgroup
sudo mkdir /sys/fs/cgroup/batch-jobs

# Set CPU weight (default 100; lower = lower priority)
echo 20 | sudo tee /sys/fs/cgroup/batch-jobs/cpu.weight

# Set a memory limit
echo $((2 * 1024 * 1024 * 1024)) | sudo tee /sys/fs/cgroup/batch-jobs/memory.max

# Place a process
echo $BASHPID | sudo tee /sys/fs/cgroup/batch-jobs/cgroup.procs

# Run a batch job (inside this cgroup)
./heavy-batch-job.sh

5. NUMA Architecture Optimization

On multi-socket servers, ignoring NUMA topology causes cross-node memory access and a sharp increase in latency.

Bash
# Inspect NUMA topology
numactl --hardware
numastat

# Pin a process to a specific NUMA node
numactl --cpunodebind=0 --membind=0 ./my-program

# Pin PostgreSQL to NUMA node 0
numactl --cpunodebind=0 --membind=0 -u postgres pg_ctl start

# Disable NUMA automatic balancing (recommended for DB servers)
echo 0 | sudo tee /proc/sys/kernel/numa_balancing

High-Traffic Web Server (Nginx / HAProxy)

INI
net.core.somaxconn = 65535
net.ipv4.tcp_max_syn_backlog = 65535
net.core.netdev_max_backlog = 65535
net.ipv4.ip_local_port_range = 1024 65535
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 15
net.core.rmem_max = 134217728
net.core.wmem_max = 134217728
net.ipv4.tcp_congestion_control = bbr
net.core.default_qdisc = fq
fs.file-max = 2097152

Database Server (PostgreSQL / MySQL)

INI
vm.swappiness = 1
vm.dirty_ratio = 5
vm.dirty_background_ratio = 2
vm.nr_hugepages = 4096          # Adjust to match shared_buffers size
kernel.numa_balancing = 0
net.ipv4.tcp_keepalive_time = 60
net.ipv4.tcp_keepalive_intvl = 10
net.ipv4.tcp_keepalive_probes = 6
fs.aio-max-nr = 1048576

Container Host (Docker / Kubernetes)

INI
fs.inotify.max_user_watches = 524288
fs.inotify.max_user_instances = 512
vm.max_map_count = 262144        # Required for Elasticsearch
net.bridge.bridge-nf-call-iptables = 1   # Kubernetes CNI
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
kernel.pid_max = 4194304
Bash
# Verify settings — dump current values after applying
for param in net.core.somaxconn vm.swappiness fs.file-max vm.nr_hugepages; do
  echo "$param = $(sysctl -n $param)"
done
#sysctl#OS#linux
Editorial note

AI tools may assist drafting. Check the evidence in each guide for whether commands were run and which environment was used. Confirm the current docs for your version before applying instructions, and email us about errors. email us

Topic-related official docs (auto-linked · not per-claim sources)Linux man pages ↗

Questions & answers

Ask a question about this guide. We'll review and reply.