Linux sysctl reference for network-facing servers
A practical sysctl baseline for public Linux hosts, VPN gateways, and Docker boxes, with the knobs that matter and the ones that break routing when you cargo-cult them.
Most sysctl hardening advice is written like a compliance spreadsheet: long list, no context, no failure modes, and no distinction between an ordinary public host and a box that intentionally routes traffic for other machines. That is how people lock themselves out of a VPN gateway with rp_filter, or quietly turn on forwarding and forget that the kernel resets IPv4 behavior when ip_forward changes.
This guide is narrower and more useful. It is for public Linux servers: VPSes, bastions, WireGuard endpoints, Docker hosts, and small gateways. The main rule is simple:
Do not apply router advice to hosts, and do not apply host advice to gateways.
That sounds obvious until you look at how these boxes evolve in real life. A VPS starts life as "just a server." Then Docker gets installed. Then a VPN interface appears. Then somebody adds policy routing. By the time anyone revisits the sysctl file, the machine is half host and half gateway by accident. This is exactly how people end up with a configuration that is individually defensible line by line and still wrong as a whole.
Start with the boring baseline
For a single-homed public server that is not routing packets for anyone else, the safe baseline is small:
- Turn redirect acceptance off.
- Turn redirect sending off.
- Keep IP forwarding off.
- Keep reverse-path filtering on.
- Turn martian logging on while you validate.
- Leave SYN cookies enabled, but treat them as a fallback, not capacity planning.
That gets you most of the value without pretending sysctl is a replacement for a firewall, sane SSH policy, or application hardening. If you have not yet read /blog/network-opsec-checklist, do that too. Kernel knobs do not fix DNS leaks or browser behavior.
Here is the host profile I would actually defend in a code review:
# /etc/sysctl.d/90-network-hardening.conf
net.ipv4.ip_forward = 0
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.default.send_redirects = 0
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
net.ipv4.conf.all.log_martians = 1
net.ipv4.conf.default.log_martians = 1
net.ipv4.tcp_syncookies = 1
You can apply that with:
sudo sysctl --system
Then verify the exact values rather than trusting your own memory:
sysctl net.ipv4.ip_forward
sysctl net.ipv4.conf.all.rp_filter
sysctl net.ipv4.conf.all.accept_redirects
journalctl -k -g martian --since -1h
The packet-trust knobs that actually matter
The most useful sysctls on public hosts are the ones that decide which packets deserve trust in the first place.
rp_filter
rp_filter is reverse-path filtering. In strict mode (1), the kernel checks whether the source address of an inbound packet would normally be reachable through the same interface it arrived on. If not, it smells like spoofing and gets dropped.
That is usually what you want on an ordinary VPS. RFC 3704 exists specifically because source-address spoofing is cheap and still common.
But the trap is obvious once you say it out loud: strict reverse-path filtering assumes symmetric routing. If your host uses policy routing, multiple uplinks, TPROXY, or intentionally asymmetric paths, strict mode can turn legitimate traffic into mysterious disappearance.
Use:
rp_filter = 1on normal public hostsrp_filter = 2on gateways or policy-routed systems where asymmetry is intentional
If you are running a box from /blog/self-hosted-wireguard-2026 as a real gateway instead of a single-user tunnel endpoint, this distinction matters.
accept_redirects
accept_redirects controls whether the kernel accepts ICMP redirect advice. The kernel docs note that hosts default to accepting redirects while routers do not. That is exactly why public servers should set it explicitly instead of inheriting a distro default and hoping.
For internet-facing hosts, the answer is almost always:
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
If some upstream network design depends on ICMP redirects to function, the real fix is usually "stop designing networks like that," not "teach every public server to trust them."
send_redirects
send_redirects is the flip side: should your machine tell other systems about better routes? On a true router, the default can be sensible. On an ordinary public server, it is just extra noise and sometimes extra confusion.
Disable it unless the machine is intentionally acting as a router:
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.default.send_redirects = 0
log_martians
log_martians tells the kernel to log impossible or suspicious source addresses. This is not a magical security control. It is a rollout aid.
Turn it on while you are hardening. If you suddenly start seeing martian logs after changing route policy, forwarding, or rp_filter, that is a clue you broke path assumptions, not evidence the Internet has become haunted.
src_valid_mark
This one gets ignored until someone builds a policy-routed box and traffic starts failing in weird ways. src_valid_mark matters when reverse-path lookups should honor fwmark-based routing decisions. If your route logic depends on marks, leaving this wrong can make anti-spoofing checks misclassify valid traffic.
For most plain public hosts, you can ignore it. For marked-routing systems, you probably cannot.
That is the recurring pattern with sysctl hardening: the obscure-looking knobs are often obscure only because they solve real routing edge cases instead of generic security theater. The moment the host starts making nontrivial routing decisions, you need to stop copying "safe server settings" from random checklists and start describing actual packet flow.
Flood-related knobs: useful, but keep your ego in check
tcp_syncookies
tcp_syncookies is one of the most over-mystified sysctls on the Internet. The kernel docs are refreshingly blunt: syncookies are a fallback facility. They are not a substitute for proper queue sizing, decent front-end capacity, or not exposing junk services in the first place.
The docs also warn that syncookies can disable some TCP extensions and hurt legitimate traffic when they trigger under normal load. That is why the right stance is:
- leave them enabled
- do not brag about them
- do not use them as your scaling story
tcp_synack_retries
tcp_synack_retries defaults to 5, which the kernel docs translate to roughly 31 seconds to the last retransmission and 63 seconds to timeout. You can tune this, but on most small public servers it is not where the real win lives.
If you are tempted to spend your afternoon shaving a retry counter from 5 to 4, but you still have password SSH auth enabled, your priorities are upside down. Fix /blog/ssh-hardening-vpn-bastion first.
The gateway exception: WireGuard boxes, Docker hosts, and routers
This is where almost every hardening article becomes useless.
If the host is acting as a gateway:
ip_forward = 1is correctsend_redirects = 0is still usually correctrp_filter = 2may be correct if routing is asymmetric- your firewall policy matters more than your "hardening list"
And crucially, the kernel documents that changing ip_forward is special because it resets IPv4 parameters to host or router defaults. That means "I toggled forwarding once during setup" is not operationally equivalent to "my intended sysctl policy is still what I think it is."
For gateways, keep a separate delta file:
# /etc/sysctl.d/91-gateway-delta.conf
net.ipv4.ip_forward = 1
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.default.send_redirects = 0
net.ipv4.conf.all.rp_filter = 2
net.ipv4.conf.default.rp_filter = 2
That separation matters because you want reviewers to see, immediately, that this host routes traffic on purpose.
Docker makes the same point from another angle. Docker's nftables docs say the nftables backend does not enable IP forwarding for you. Meanwhile Docker's packet-filtering docs say the iptables backend may enable forwarding automatically. If you treat runtime state as truth, you will eventually lie to yourself.
That is why I strongly prefer separate files for host baseline and gateway delta. The split forces the question reviewers should ask anyway: is this machine supposed to route traffic, or did it merely drift into routing traffic?
Validate before you celebrate
Kernel hardening is where people break pathing and then confidently declare success because the server still answers ping.
Do a short verification loop every time:
sudo sysctl --system
ip route get 1.1.1.1
ip rule
journalctl -k -g martian --since -10m
If the box is a gateway, test from a client behind it, not just from the gateway itself.
If SSH is your only way back in, keep one session open while you test another. If a sysctl change interacts badly with policy routing, you want a live shell already attached, not a life lesson.
The pattern I care about is not "maximum number of knobs changed." It is this:
- Separate host policy from gateway policy.
- Use strict anti-spoofing unless routing reality says otherwise.
- Turn redirects off deliberately.
- Treat SYN cookies as emergency behavior, not architecture.
- Re-check runtime values after anything that toggles forwarding.
That is enough to make your public Linux host noticeably harder to confuse, spoof, or misconfigure, which is a much better outcome than owning a 90-line sysctl file nobody on your team can explain.