Migrating from iptables to nftables: A Practical Firewall Guide
iptables is on its way out and nftables is the replacement. Here's how I migrate real firewalls without locking myself out or dropping traffic.
- #linux
- #nftables
- #iptables
- #firewall
- #networking
- #security
I have written more iptables rules than I care to count, and I will not miss the syntax. It worked, but it was a pile of separate tools (iptables, ip6tables, arptables, ebtables), a confusing chain/table model, and rule files that nobody could read six months later. nftables replaces all of it with one tool, one syntax, and a rule format that actually looks like the policy you meant to write.
Most distros already route the legacy iptables command through the nft backend, so you may be using nftables without knowing it. But to get the real benefits — sets, maps, one ruleset for v4 and v6 — you want native nft rules. Here’s how I migrate without cutting my own SSH session.
The model in one paragraph
nftables has tables (containers, scoped to a family like inet, ip, ip6), which hold chains (attached to hooks like input, forward, output), which hold rules. The big win is the inet family: one table handles both IPv4 and IPv6, so you stop maintaining two parallel rulesets. Chains have a policy (the default action) and a priority (ordering relative to other chains on the same hook).
Translate your existing rules first
Don’t hand-rewrite. There’s a converter:
# Dump current iptables rules to a file
sudo iptables-save > /tmp/rules.v4
# Translate to nft syntax
iptables-restore-translate -f /tmp/rules.v4 > /tmp/ruleset.nft
Read the output — it’s a faithful translation, but it’s mechanical, not idiomatic. You’ll usually clean it up into something tighter. Still, it’s the fastest way to see your real policy in the new syntax.
A clean starting ruleset
Here’s a minimal, readable /etc/nftables.conf for a typical server — SSH, HTTP/HTTPS, ICMP, and established traffic:
#!/usr/sbin/nft -f
flush ruleset
table inet filter {
chain input {
type filter hook input priority 0; policy drop;
ct state established,related accept
iif "lo" accept
ct state invalid drop
ip protocol icmp accept
ip6 nexthdr ipv6-icmp accept
tcp dport 22 accept
tcp dport { 80, 443 } accept
}
chain forward {
type filter hook forward priority 0; policy drop;
}
chain output {
type filter hook output priority 0; policy accept;
}
}
Note tcp dport { 80, 443 } — an anonymous set in one rule instead of two. That readability compounds fast on a real firewall.
Don’t lock yourself out
This is the rule that has saved me more than once. Before you apply a policy drop ruleset over SSH, set a dead-man’s switch:
# Apply the new ruleset, but schedule a rollback in 2 minutes
sudo sh -c 'nft -f /etc/nftables.conf; sleep 120 && nft flush ruleset' &
If your SSH session survives, you cancel the rollback (kill the background job) and persist for real. If you locked yourself out, the flush fires in two minutes and you get back in. I have never regretted doing this; I have absolutely regretted skipping it.
Then make it permanent:
sudo systemctl enable --now nftables
sudo nft list ruleset > /etc/nftables.conf # snapshot the live state
Sets and maps: the actual reason to switch
Anonymous sets are nice, but named sets are where nftables earns its keep. A blocklist that updates without rewriting rules:
table inet filter {
set blocklist {
type ipv4_addr
flags timeout
}
chain input {
type filter hook input priority 0; policy drop;
ip saddr @blocklist drop
ct state established,related accept
tcp dport 22 accept
}
}
Now adding a bad actor is a data operation, not a rule edit:
sudo nft add element inet filter blocklist '{ 203.0.113.7 timeout 1h }'
The timeout flag auto-expires the entry. You can wire this to fail2ban-style automation and never touch the chain again. Maps go further — map a destination port to a verdict, or do destination NAT with a single map lookup instead of a rule per port.
Verifying and debugging
sudo nft list ruleset # the whole policy, human-readable
sudo nft list table inet filter # one table
sudo nft -a list ruleset # with handles, needed for deleting rules
To watch a rule actually match, add a counter:
tcp dport 22 counter accept
Then nft list ruleset shows packet and byte counts per rule — far easier than tcpdump for “is this rule even being hit?”
Where AI saves time
The translation from “what I want” to nft syntax is the slow part, especially for NAT and the set/map features. I describe the intent — “rate-limit new SSH connections to 10 per minute per source IP” — and ask a model for the nft rule, then read it carefully before applying. The syntax is regular enough that models do well here, but a firewall is exactly the place you verify every line. Keep a few Linux firewall prompts handy for the common patterns.
Migration checklist
- Translate existing rules with
iptables-restore-translateto see your real policy. - Rewrite into idiomatic
inetrules with named sets where it helps. - Apply with a dead-man’s-switch rollback so you can’t lock yourself out.
- Add counters to rules you’re unsure about and watch them match.
systemctl enable nftablesand snapshot the live ruleset to disk.
The payoff is one tool, one ruleset for both IP versions, and dynamic sets that turn firewall changes into data updates. After the first clean migration you won’t want to go back.
Firewall changes can sever remote access. Always apply drop policies behind a timed rollback and verify your management path before persisting.
Download the Free 500-Prompt DevOps AI Toolkit
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.