SYSCTL.D(5)
๐ NAME ๐
sysctl.d โ okay so this is section 5 of the manual, bestie, and it's literally the whole vibe of "how do I configure kernel parameters at boot" โจ
โจ SYNOPSIS โจ
/etc/sysctl.d/*.conf
/run/sysctl.d/*.conf
/usr/local/lib/sysctl.d/*.conf
/usr/lib/sysctl.d/*.conf
key.name.under.proc.sys = some value
key/name/under/proc/sys = some value
key/middle.part.with.dots/foo = 123
key.middle/part/with/dots.foo = 123
-key.that.will.not.fail = value
key.pattern.*.with.glob = whatever
-key.pattern.excluded.with.glob
key.pattern.overridden.with.glob = custom
๐ธ DESCRIPTION ๐ธ
so here's the tea: at boot, systemd-sysctl.service(8) reads the configuration files from all those directories listed above and uses them to configure sysctl(8) kernel parameters. that's the entire plot, no subplot, no twist ๐ซถ
๐ CONFIGURATION FORMAT ๐
okay so a config file is just a list of variable assignments, one per line, separated by newlines. that's it. empty lines get ignored, and so do lines whose first non-whitespace character is # or ; โ those are your comments, queen ๐
now here's the part everyone gets confused about, so pay attention bestie: you can use either / or . as a separator inside a sysctl variable name. not both-at-once-arbitrarily, there's a rule:
- if the first separator is a slash, then all the remaining slashes and dots are left intact ๐ฆ
- if the first separator is a dot, then dots and slashes get interchanged ๐
so like, kernel.domainname=foo and kernel/domainname=foo are equivalent, and both of them cause foo to be written to /proc/sys/kernel/domainname. periodt.
and either net.ipv4.conf.enp3s0/200.forwarding or net/ipv4/conf/enp3s0.200/forwarding can be used to refer to /proc/sys/net/ipv4/conf/enp3s0.200/forwarding. iconic, honestly.
you can also use a glob glob(7) pattern to write the same value to all matching keys ๐. but listen โ keys for which an explicit pattern exists get excluded from any glob matching. and on top of that, you can explicitly exclude a key from being set by any matching glob patterns by writing the key name prefixed with a - character and not followed by =. check the SYNOPSIS up there, it's all laid out ๐
also! any access permission errors, and any attempts to write variables that aren't present on the local system, get logged at debug level and do not cause the service to fail. no drama. but other types of errors when setting variables get logged with higher priority and cause the service to return failure at the end โ after it's processed the other variables, so at least it finishes the fit first ๐ญ
as an exception: if a variable assignment is prefixed with a single - character, then failure to set that variable for any reason gets logged at debug level and will not cause the service to fail. bestie's got grace ๐ซถ
the settings configured with sysctl.d files get applied early on boot. and the network interface-specific options get applied individually for each network interface as it shows up in the system. (more specifically: net.ipv4.conf.*, net.ipv6.conf.*, net.ipv4.neigh.* and net.ipv6.neigh.*.) ๐
now, the sad part ๐ญ โ many sysctl parameters only become available when certain kernel modules are loaded. and modules are usually loaded on demand, like when certain hardware gets plugged in or a network gets brought up. which means systemd-sysctl.service(8) โ which runs during early boot โ will not configure those parameters if they become available after it has run. so unfair.
to set such parameters, the recommendation is: add a udev(7) rule to set those parameters when they become available. alternatively, a slightly simpler and less efficient option is to add the module to modules-load.d(5), which causes it to be loaded statically before sysctl settings are applied (see the example below, it's a good one ๐ธ).
๐ CONFIGURATION DIRECTORIES AND PRECEDENCE ๐
config files get read from directories in /etc/, /run/, /usr/local/lib/, and /usr/lib/ โ in order of precedence, exactly like the SYNOPSIS section listed them above. files must have the .conf extension, no exceptions, that's the dress code.
the override hierarchy, in full:
- files in
/etc/override files with the same name in/run/,/usr/local/lib/, and/usr/lib/. - files in
/run/override files with the same name under/usr/.
all configuration files get sorted by their filename in lexicographic order, regardless of which of the directories they live in. and if multiple files specify the same option, the entry in the file with the lexicographically latest name takes precedence. so basically: a config in a certain file can either be replaced completely (by dropping a file with the same name in a directory with higher priority), or you can just change individual settings (by specifying additional settings in a file with a different name that's ordered later). your choice, bestie, both are valid ๐
packages should install their configuration files in /usr/lib/ (that's distribution packages) or /usr/local/lib/ (that's local installs) [1]. and files in /etc/ are reserved for the local administrator, who may use this logic to override the configuration files installed by vendor packages. that's your turf, own it ๐
it is recommended to prefix all filenames with a two-digit number and a dash to simplify the ordering. and it's recommended to use the range 10-40 for configuration files in /usr/ and the range 60-90 for configuration files in /etc/ and /run/ โ that way local and transient configuration files will always take priority over the configuration files shipped by the OS vendor. yes this is a whole system. yes it works. no we're not arguing about it ๐
if the administrator wants to disable a configuration file supplied by the vendor, the recommended way is to place a symlink to /dev/null in the configuration directory in /etc/, with the same filename as the vendor configuration file. galaxy brain move tbh ๐. but โ if the vendor configuration file is included in the initrd image, the image has to be regenerated. don't forget that part, it will absolutely bite you ๐
๐ EXAMPLES ๐
๐ Example 1. Set kernel YP domain name
/etc/sysctl.d/domain-name.conf:
kernel.domainname=example.com
๐ Example 2. Apply settings available only when a certain module is loaded (method one)
/etc/udev/rules.d/99-bridge.rules:
ACTION=="add", SUBSYSTEM=="module", KERNEL=="br_netfilter", \
RUN+="/usr/lib/systemd/systemd-sysctl --prefix=/net/bridge"
/etc/sysctl.d/bridge.conf:
net.bridge.bridge-nf-call-ip6tables = 0
net.bridge.bridge-nf-call-iptables = 0
net.bridge.bridge-nf-call-arptables = 0
this method applies settings when the module is loaded. please note that, unless the br_netfilter module is loaded, bridged packets will not be filtered by Netfilter (starting with kernel 3.18), so simply not loading the module is sufficient to avoid filtering. lowkey elegant ๐ฆ
๐ Example 3. Apply settings available only when a certain module is loaded (method two)
/etc/modules-load.d/bridge.conf:
br_netfilter
/etc/sysctl.d/bridge.conf:
net.bridge.bridge-nf-call-ip6tables = 0
net.bridge.bridge-nf-call-iptables = 0
net.bridge.bridge-nf-call-arptables = 0
this method forces the module to be always loaded. please note that, unless the br_netfilter module is loaded, bridged packets will not be filtered by Netfilter (starting with kernel 3.18), so simply not loading the module is sufficient to avoid filtering. same disclaimer, different strategy, that's the duality of girl ๐
๐ Example 4. Set network routing properties for all interfaces
/etc/sysctl.d/20-rp_filter.conf:
net.ipv4.conf.default.rp_filter = 2
net.ipv4.conf.*.rp_filter = 2
-net.ipv4.conf.all.rp_filter
net.ipv4.conf.hub0.rp_filter = 1
the rp_filter key will be set to "2" for all interfaces, except "hub0".
here's why, bestie, follow along: we set net.ipv4.conf.default.rp_filter first, so any interfaces which are added later will get this value (this also covers any interfaces detected while we're running). the glob matches any interfaces which were detected earlier. the glob will also match net.ipv4.conf.all.rp_filter, which we do not want to set at all, so it is explicitly excluded. and "hub0" is excluded from the glob because it has an explicit setting. see? every line has a job. no line is unemployed. ๐
๐ SEE ALSO ๐
systemd(1), systemd-sysctl.service(8), systemd-delta(1), sysctl(8), sysctl.conf(5), modprobe(8)
๐งจ NOTES ๐งจ
- ๐ฃ๐ฅ๐งจ๐ฅ๐ฅ๐ฃ please note that those configuration files must be available at all times. if
/usr/local/is a separate partition, it may not be available during early boot, and must not be used for configuration. like, seriously, do not.
okay that's the whole page, bestie, you ate it ๐
this was sysctl.d from systemd 262.1, page SYSCTL.D(5), and yes I will be here whenever you need to reread it ๐โจ๐ฆ๐
๐ฆ where this page comes from
- arch rolling: systemd 262.1-1 ยท LGPL-2.1-or-later AND CC0-1.0 AND GPL-2.0-or-later AND MIT-0
- ๐ girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms