iptables rules accumulate as an append-only log of past incidents. nftables lets you write the whole policy as one declarative file, load it atomically, and diff it in review.
1. The shape of a ruleset
#!/usr/sbin/nft -f
flush ruleset
table inet filter {
set admin_nets {
type ipv4_addr
flags interval
elements = { 10.0.0.0/24, 192.168.10.0/24 }
}
chain input {
type filter hook input priority filter; policy drop;
ct state established,related accept
ct state invalid drop
iif lo accept
ip protocol icmp accept
ip6 nexthdr icmpv6 accept
tcp dport 22 ip saddr @admin_nets accept
tcp dport { 80, 443 } accept
limit rate 5/minute log prefix "nft-drop: "
}
}
2. Why atomic loading matters
The whole file is applied in one transaction. Either the new policy is live or the old one is untouched — there is no window where half the rules exist, which is the window that iptables scripts historically locked people out in.
3. Sets instead of repetition
Named sets can be updated without reloading the ruleset, which makes them the right place for anything dynamic:
nft add element inet filter admin_nets { 203.0.113.7 }
nft list set inet filter admin_nets
4. Test before you commit
sudo nft -c -f /etc/nftables.conf # check syntax, change nothing
Run that in CI against the file in the repository. A firewall that only gets validated by being applied to production is a firewall that will eventually be validated during an outage.