I have finally moved my router's firewall off iptables and onto nftables, and the thing that pushed me over the edge was not performance or some clever new feature. It was the sheer relief of having one ruleset instead of three.
The old setup was an iptables file, a separate ip6tables file that drifted out of sync with it constantly, and a pile of ipset lists bolted on the side. Every change meant editing in two or three places and hoping I had not let v4 and v6 diverge. nftables collapses all of that. One config, one inet table that handles both address families at once, sets as a first-class part of the language rather than a separate tool.
A trimmed slice of it looks like this:
table inet filter {
set blocklist {
type ipv4_addr
elements = { 192.0.2.0, 198.51.100.7 }
}
chain input {
type filter hook input priority 0; policy drop;
ct state established,related accept
iif "lo" accept
ip saddr @blocklist drop
tcp dport 22 accept
}
}
That one inet table is doing what used to take two binaries and a bolt-on. The blocklist is just a set, defined inline, no separate ipset dance.
I will not pretend the translation was instant. The syntax is genuinely different, not iptables with a fresh coat of paint, and I leaned on iptables-translate to get my bearings before rewriting the lot by hand. But the rules now read like something I could hand to someone else and have them understand. That alone was worth the evening.