sysctl: Tuning Kernel Parameters on Linux
What You'll Learn
- How to read and change kernel parameters with
sysctl - When to use a runtime change (
sysctl -w) versus persisting via/etc/sysctl.d - How to diagnose why a setting doesn't take effect (precedence, load timing)
Quick Summary
- Read →
sysctl <key>(list all withsysctl -a) - Runtime change →
sudo sysctl -w key=value(lost on reboot) - Persist → write
/etc/sysctl.d/99-custom.conf, thensudo sysctl --system
Assumptions (target environment)
- systemd-based distros such as Ubuntu 22.04 / 24.04 or RHEL 9
- Package:
sysctl(8)shipped withprocps(procps-ng) - Changing parameters generally requires root (
sudo)
What is sysctl?
Conclusion: sysctl reads and writes the running kernel's tunable parameters through
/proc/sys/; key names map directly to that directory tree.
sysctl reads and writes the kernel's tunable parameters. Under the hood, those parameters are files in the virtual filesystem /proc/sys/, and sysctl is a thin wrapper that exposes them in a friendly key = value form.
Dots in a key name map one-to-one to directory separators:
# These refer to the same thing sysctl net.ipv4.ip_forward cat /proc/sys/net/ipv4/ip_forward
net.ipv4.ip_forward = 0 0
The main top-level namespaces are:
| Prefix | Area | Typical parameters |
|---|---|---|
net. |
Networking | net.ipv4.ip_forward / net.core.somaxconn |
vm. |
Virtual memory | vm.swappiness / vm.overcommit_memory |
fs. |
Filesystem | fs.file-max / fs.inotify.max_user_watches |
kernel. |
General kernel | kernel.hostname / kernel.pid_max |
How do I read the current value?
Conclusion: Use
sysctl <key>for one value andsysctl -afor everything; add-nto print the value without the key name.
# A single parameter sysctl vm.swappiness
vm.swappiness = 60
# All parameters (thousands of lines; combine with grep) sysctl -a | grep somaxconn
net.core.somaxconn = 4096
To get the value only, use -n (do not print the key name). This is handy in shell scripts that branch on a value.
sysctl -n vm.swappiness
60
Some parameters return "permission denied" for non-root users, so sysctl -a may be incomplete. Run sudo sysctl -a when you need the full list.
How do I change a value temporarily?
Conclusion:
sudo sysctl -w key=valueapplies instantly but is lost on reboot, which makes it ideal for testing and validation.
-w (write) changes the value in the running kernel. It takes effect immediately but reverts on reboot.
# Enable IP forwarding temporarily sudo sysctl -w net.ipv4.ip_forward=1
net.ipv4.ip_forward = 1
Writing directly to /proc/sys/ does the same thing:
echo 1 | sudo tee /proc/sys/net/ipv4/ip_forward
sysctl -w is a temporary change. "I set it but it was gone after reboot" almost always means the value was never persisted. To make it permanent, follow the next section.
How do I make a change permanent?
Conclusion: Put a drop-in file in
/etc/sysctl.d/and apply it withsudo sysctl --system; drop-ins are preferred over editing/etc/sysctl.confdirectly.
Persistence means writing key = value lines to a config file. On modern systemd distros, creating a drop-in file under /etc/sysctl.d/ is preferred over editing /etc/sysctl.conf directly, because it is easier to manage and diff.
By convention, name the file NN-name.conf (where NN is a two-digit number). The number controls precedence (see below).
# Example: reduce swapping and enable IP forwarding sudo tee /etc/sysctl.d/99-custom.conf <<'EOF' # Custom kernel parameters vm.swappiness = 10 net.ipv4.ip_forward = 1 EOF
Writing the file alone does not apply it. Load it explicitly:
# Load just this file sudo sysctl -p /etc/sysctl.d/99-custom.conf # Or reload every config file from the standard directories sudo sysctl --system
* Applying /etc/sysctl.d/99-custom.conf ... vm.swappiness = 10 net.ipv4.ip_forward = 1
Verify the result. After applying, read the value back with sysctl <key> and confirm it matches what you expect.
sysctl vm.swappiness net.ipv4.ip_forward
At boot, systemd-sysctl.service applies these files automatically, so a persisted value survives reboots.
What is the config file precedence?
Conclusion: Files from several directories are merged in lexicographic filename order; for a duplicated key, the lexicographically last filename wins, and
/etc/overrides same-named files elsewhere.
sysctl --system reads *.conf from these directories:
| Priority | Directory | Purpose |
|---|---|---|
| High | /etc/sysctl.d/ |
Administrator custom settings |
| Medium | /run/sysctl.d/ |
Runtime-generated settings |
| Low | /usr/lib/sysctl.d/ |
Package-shipped defaults |
The rules are two-fold:
- Files are merged in lexicographic filename order (compared across directories). If the same key appears in multiple files, the one in the file whose name sorts last (largest) wins. That is why a high number like
99-custom.confmakes overriding easy. - If the same filename exists in multiple directories, the one in
/etc/overrides those in/run/and/usr/lib/. To disable a packaged file, place an empty file of the same name in/etc/sysctl.d/.
# Trace which file sets which key grep -Rn swappiness /etc/sysctl.conf /etc/sysctl.d/ /usr/lib/sysctl.d/ /run/sysctl.d/ 2>/dev/null
/etc/sysctl.conf is still read, but it is the historical single file. Splitting new settings into drop-ins (/etc/sysctl.d/*.conf) makes it easier to trace where each setting comes from.
Common parameters with examples
Conclusion: Memory, networking, and file-descriptor tunables show up most often in practice; never copy values blindly without understanding them.
| Parameter | Role | Common value |
|---|---|---|
vm.swappiness |
Swap aggressiveness (0-100, lower favors RAM) | 10 on DB servers |
vm.overcommit_memory |
Memory overcommit policy | 1 for Redis, etc. |
net.ipv4.ip_forward |
Packet forwarding (required for routers/containers) | 1 |
net.core.somaxconn |
Max accept-queue length | 4096 for busy web servers |
fs.file-max |
System-wide file handle limit | Raise for high-connection servers |
fs.inotify.max_user_watches |
inotify watch limit | Raise for IDEs/build tools |
Do not paste a whole "tuning example" without understanding each parameter. Before applying to production, validate the behavior in staging with a temporary sysctl -w change, then persist it only if all is well.
Why isn't my change taking effect?
Conclusion: Check four things in order: permissions, forgotten reload, an overriding file, and a missing key.
- Insufficient permission:
sysctl -wneedssudo.Operation not permittedmeans permissions or a container restriction (unprivileged containers block most writes). - Forgot to reload: Writing the file is not enough. Confirm you ran
sudo sysctl --system(or-p). - Overridden by another file: If a value reverts, a lexicographically later file is overriding it. Find the culprit with
grep -R <key> /etc/sysctl.d /usr/lib/sysctl.d. - Wrong or missing key:
sysctl: cannot stat ...means a typo or an unloaded module. Usesysctl -eto ignore unknown-key errors and find the correct name withsysctl -a | grep.
# Check the boot-time apply log (systemd) journalctl -u systemd-sysctl