$linuxjunkies
>

How to Tune Kernel Parameters with sysctl

Learn how to tune Linux kernel parameters at runtime and permanently using sysctl and /etc/sysctl.d, with practical network and VM tunables explained.

AdvancedUbuntuDebianFedoraArch9 min readUpdated June 7, 2026

Before you start

  • Root or sudo access on the target system
  • Basic familiarity with Linux file editing and the terminal
  • Kernel 4.9 or later for BBR congestion control support
  • Understanding of the workload profile you are tuning for

The Linux kernel exposes hundreds of runtime parameters through the /proc/sys virtual filesystem. sysctl is the standard interface for reading and writing those parameters without rebooting. Knowing which knobs matter—and how to make changes survive a reboot—is essential for squeezing performance out of servers, tuning network stacks, and hardening systems. This guide covers the mechanics, the persistence layer at /etc/sysctl.d/, and a curated set of network and VM tunables worth understanding in production.

How sysctl Works

Every file under /proc/sys maps to a kernel parameter. The dotted names sysctl uses simply replace directory separators with dots: net.ipv4.tcp_rmem maps to /proc/sys/net/ipv4/tcp_rmem. You can read and write those files directly with cat and echo, but sysctl is safer and handles persistence hooks properly.

Read a Single Parameter

sysctl net.ipv4.tcp_rmem

Read All Current Parameters

sysctl -a

Output will be several hundred lines and varies by kernel version and loaded modules.

Write a Parameter at Runtime

sysctl -w net.core.somaxconn=4096

This takes effect immediately but is lost on reboot. Runtime writes are useful for testing before committing to a config file.

Making Changes Persistent with /etc/sysctl.d/

Drop .conf files into /etc/sysctl.d/ rather than editing /etc/sysctl.conf directly. Files are read in lexicographic order, so prefix your files with a number (e.g., 90-network-tuning.conf) to control precedence. Distro packages typically ship their own files in this directory, and yours should come after them so they win.

Create a Tuning File

sudo tee /etc/sysctl.d/90-tuning.conf <<'EOF'
# Network performance
net.core.somaxconn = 4096
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_congestion_control = bbr
net.core.default_qdisc = fq
net.ipv4.tcp_fastopen = 3
net.ipv4.tcp_slow_start_after_idle = 0

# VM / memory
vm.swappiness = 10
vm.dirty_ratio = 15
vm.dirty_background_ratio = 5
vm.overcommit_memory = 1

# Security / hardening
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
net.ipv4.icmp_echo_ignore_broadcasts = 1
net.ipv4.tcp_syncookies = 1
EOF

Apply Without Rebooting

sudo sysctl --system

--system reads all files from /etc/sysctl.d/, /run/sysctl.d/, /usr/lib/sysctl.d/, and /etc/sysctl.conf in the correct order. It is the modern replacement for sysctl -p.

Key Network Tunables Explained

Socket Backlog: net.core.somaxconn

Controls the maximum length of the accept queue for listening sockets. The kernel default is 4096 on recent kernels, but older distros ship 128 or 512. Under heavy inbound connection load (reverse proxies, web servers) this queue filling up causes silent connection drops. Set to 4096 or higher and ensure your application calls listen() with a matching backlog.

TCP Buffer Sizes: tcp_rmem / tcp_wmem

These three-value triplets define minimum, default, and maximum socket buffer sizes in bytes for receive and send paths respectively. The maximum should match net.core.rmem_max / net.core.wmem_max—the kernel silently clamps to those ceilings. For high-bandwidth paths (10 Gbps+) or high-latency links (satellite, intercontinental), larger buffers allow the TCP window to fill the bandwidth-delay product.

Congestion Control: tcp_congestion_control + default_qdisc

BBR (Bottleneck Bandwidth and Round-trip propagation time) is Google's congestion control algorithm, available since kernel 4.9 and enabled by loading the tcp_bbr module. Pair it with the fq (Fair Queuing) qdisc for best results. Check that the module is loaded before setting this parameter:

modprobe tcp_bbr
echo tcp_bbr | sudo tee /etc/modules-load.d/tcp_bbr.conf

TCP Fast Open: tcp_fastopen

Value 3 enables TFO for both clients and servers. TFO allows data to be sent in the SYN packet for repeat connections, cutting one round-trip from connection establishment. Requires application-level support (nginx, for example, supports it). Value 1 is client-only, 2 is server-only.

tcp_slow_start_after_idle

When set to 0, the kernel does not reset the TCP congestion window after a connection goes idle. This is a meaningful improvement for persistent connections that burst traffic periodically (keep-alive HTTP, replication streams).

Key VM Tunables Explained

vm.swappiness

A value from 0 to 200 (kernel ≥ 5.8) or 0 to 100 on older kernels. It biases the reclaim balance between anonymous memory (swap candidate) and page cache. 10 strongly prefers evicting page cache over swapping out application memory. Use 1 on database hosts where unexpected swap latency is catastrophic. Do not set it to 0 on kernels before 3.5—that prevented swapping entirely and could cause OOM kills.

vm.dirty_ratio / vm.dirty_background_ratio

dirty_background_ratio is the percentage of total memory at which the kernel starts writing dirty pages in the background. dirty_ratio is the hard cap at which processes block on writes. Lowering both reduces the worst-case write-back burst at the cost of more frequent I/O—good for spinning disks and latency-sensitive workloads. For write-heavy workloads on NVMe, you can raise these slightly.

vm.overcommit_memory

Mode 1 always allows overcommit, which is what Redis, some JVMs, and fork-heavy workloads expect. Mode 0 (default) uses heuristic checking. Mode 2 refuses any overcommit—rarely useful outside strict environments.

Distro-Specific Notes

Debian / Ubuntu

# Apply immediately and verify
sudo sysctl --system
sysctl net.core.somaxconn

Fedora / RHEL / Rocky

RHEL 9 and Rocky 9 ship with a /usr/lib/sysctl.d/50-default.conf that sets several security defaults. Your files in /etc/sysctl.d/ with a higher numeric prefix will override them correctly.

sudo sysctl --system
# Confirm precedence
sysctl -a 2>/dev/null | grep rp_filter

Arch Linux

Arch ships almost no default sysctl overrides, so you start with bare kernel defaults. The systemd-sysctl.service unit applies your files at boot—no extra steps needed.

sudo systemctl status systemd-sysctl.service

Verification

After applying, confirm each critical value loaded correctly:

sysctl net.core.somaxconn net.ipv4.tcp_congestion_control vm.swappiness

Expected output (will vary if you used different values):

net.core.somaxconn = 4096
net.ipv4.tcp_congestion_control = bbr
vm.swappiness = 10

To confirm changes survive reboot, restart and re-run the same command. You can also use sysctl --system --dry-run (kernel ≥ 5.2) to preview what would be applied without writing anything.

Troubleshooting

Parameter is read-only or not found

Some parameters only exist when a specific kernel module is loaded. If net.ipv4.tcp_congestion_control = bbr fails, tcp_bbr is not loaded. Run modprobe tcp_bbr and retry. Parameters under /proc/sys/net/bridge/ require br_netfilter.

Value reverts after applying --system

A later file in the load order is overriding yours. Run sysctl --system 2>&1 | grep <parameter> to see which file wins. Rename your file to a higher number (e.g., 99-) to take precedence, but be aware that some security frameworks (SELinux policy, systemd-networkd) may re-apply their own values after boot via separate mechanisms.

BBR not available on older kernels

BBR requires kernel 4.9+. Ubuntu 18.04 LTS (kernel 4.15) and later, Fedora 26+, and Arch all have it. RHEL 7 uses a 3.10 kernel and does not support BBR without a custom kernel. Check with uname -r and sysctl net.ipv4.tcp_available_congestion_control.

Changes not applied at boot (non-systemd edge case)

On all modern distros with systemd, systemd-sysctl.service handles this. Verify it is enabled:

systemctl is-enabled systemd-sysctl.service

It should print static—this unit is a dependency of sysinit.target and cannot be disabled through normal means.

tested on:Ubuntu 24.04Fedora 40Rocky 9.3Arch 2024.05

Frequently asked questions

What is the difference between /etc/sysctl.conf and /etc/sysctl.d/?
/etc/sysctl.conf is the legacy single config file. /etc/sysctl.d/ allows modular, ordered configuration drop-ins. Distro packages and tools use sysctl.d so their values can be overridden cleanly without editing the base file. Prefer sysctl.d for all new configuration.
Will these changes survive a kernel upgrade?
Yes. Sysctl parameters are kernel-version-agnostic plain text files. The only risk is a new kernel version removing or renaming a parameter, which would cause systemd-sysctl to log a warning but not fail boot.
Is it safe to set vm.overcommit_memory=1 in production?
It depends on the workload. Redis explicitly requires it and will warn on startup if it is not set. For general-purpose servers, mode 0 (heuristic) is safer as it still allows most overcommit while rejecting obviously impossible allocations.
How do I check which congestion control algorithms are available on my kernel?
Run 'sysctl net.ipv4.tcp_available_congestion_control'. The output lists all loaded algorithms. If bbr is missing, load the module with 'modprobe tcp_bbr' and check again.
Can I tune sysctl parameters inside a container or VM?
Inside unprivileged containers (Docker default, Kubernetes pods) most net.* and vm.* parameters are read-only because they are namespaced to the host kernel. Privileged containers or those with the SYS_ADMIN capability can write some of them. In a full VM you have complete control as if it were bare metal.

Related guides