~/notes/2026|09|05
nftables: First Contact
I switched to nftables the day I had to read an old iptables script and gave up. nftables reads like what it does: one syntax, no -A vs -I guessing, and a whole firewall that fits on a screen. If you have never looked at it, here is the 10-minute version.
The three things you need
- A table groups rules for one family.
inetcovers IPv4 and IPv6 at once, which alone is a reason to switch. - A chain is a named list of rules hooked into the packet path:
input,forward,output. - Each rule matches and jumps to a verdict:
accept,drop,reject.
A firewall that fits in a file
table inet filter {
chain input {
type filter hook input priority filter; policy drop;
ct state established,related accept
iif lo accept
ct state invalid drop
tcp dport 22 accept # ssh
tcp dport 80,443 accept # web
}
chain forward { type filter hook forward priority filter; policy drop; }
chain output { type filter hook output priority filter; policy accept; }
}Read it like prose: drop everything incoming, allow replies to my own connections, allow loopback, allow SSH and web. The policy drop is the whole philosophy — you list what you want, everything else dies.
Put it in /etc/nftables.conf, run nft -f /etc/nftables.conf, and please do it from a tmux session with a second window open. Remote firewalls punish mistakes instantly.
drop, reject, and why the difference matters
drop is silent: the client waits and times out. reject answers with a TCP RST, so the client fails immediately. For a closed port, reject is polite; for someone port-scanning you, drop is quieter:
nft add rule inet filter input tcp dport 23 reject with tcp resetPort lists without the copy-paste
nft add set inet filter allowed_ports { type inet_service; }
nft add element inet filter allowed_ports { 22, 80, 443, 51820 }
nft add rule inet filter input tcp dport @allowed_ports acceptOne rule, one set, and adding a port later is a single line. My SSH rate limit rides on the same idea:
nft add rule inet filter input tcp dport 22 ct state new limit rate 6/minute acceptSix fresh connections a minute is plenty for a human and useless for a brute-forcer.
When in doubt, nft list ruleset prints your live state as valid nftables syntax — you can paste it straight into the config file. That alone beats iptables-save, which always looked like it was written by a machine that hated you.