Most Linux distros now ship nftables as the default firewall backend, with iptables kept around as a compatibility wrapper (iptables-nft). On Ubuntu 22 you’re already running nftables under the hood whether you know it or not — the iptables command is just translating to nft rules behind the scenes. So why bother migrating? Three reasons: the compat layer is incomplete, the syntax is genuinely better once you get used to it, and your rules will perform better when they don’t go through a translation step.
This is the migration script I used on my Oracle box, plus the three rules iptables-translate silently mishandles that almost cost me access.
The 30-line conversion
The basic translation is built into nftables itself. iptables-save | iptables-restore-translate emits an nft ruleset:
#!/bin/bash
set -euo pipefail
BACKUP=/root/firewall-backup-$(date +%F).tar.gz
iptables-save > /root/iptables.rules.before
ip6tables-save > /root/ip6tables.rules.before
nft list ruleset > /root/nftables.before
iptables-save | iptables-restore-translate -f - > /tmp/v4.nft
ip6tables-save | ip6tables-restore-translate -f - > /tmp/v6.nft
cat > /etc/nftables.conf <<EOF
#!/usr/sbin/nft -f
flush ruleset
include "/tmp/v4.nft"
include "/tmp/v6.nft"
EOF
nft -c -f /etc/nftables.conf
systemctl disable --now netfilter-persistent || true
iptables -F && iptables -X
ip6tables -F && ip6tables -X
systemctl enable --now nftables
nft list ruleset > /root/nftables.after
tar czf $BACKUP /root/iptables.rules.before /root/ip6tables.rules.before \
/root/nftables.before /root/nftables.after
echo "Backup at $BACKUP"
That’s the happy path. nft -c -f dry-checks the config before loading; if it fails, the script aborts with the existing rules still active.
Run it from a screen / tmux session
The flush-then-load step is briefly atomic but if anything goes sideways your firewall is open or closed for a few seconds. Run from tmux with a separate “panic console” SSH session that you keep open. If your main shell drops, the panic console can re-add a permissive INPUT rule.
Three things the translator silently misses
- 1. Custom chain names with hyphens. If your iptables ruleset has
-A f2b-sshdor-A CROWDSEC-BLOCKLISTS, the translator emits hyphenated chain names. nftables 1.0+ accepts them only if quoted, andiptables-restore-translatedoesn’t always add the quotes. Result: nft refuses to load with a cryptic parse error pointing at the wrong line. Fix is to grep your translated output and wrap any hyphenated names:chain "f2b-sshd". - 2.
-m comment --commentannotations. Comments in iptables rules become matches on a string match in the translated nft output. They work, but they’re 5-10× more expensive than the nativecommentattribute in nftables. If you have a lot of commented rules, do a second pass convertingmatch comment ...tocomment "..."at end-of-rule. - 3.
--setfrom ipset. If your iptables rules reference ipset sets (-m set --match-set blocklist src), the translator sometimes emits@blocklistreferences but doesn’t translate the sets themselves. You have to manually convert eachipset saveoutput into an nftablessetdefinition with a matching name. Miss this and your DROP rules are referencing empty sets — the firewall is effectively wide open for those rules.
The third one is the dangerous one. The other two break loud. The ipset miss makes you think the firewall loaded fine, while silently neutering your blocklist enforcement.
Verifying the migration worked
# 1. The number of rules matches roughly
iptables -S | wc -l
nft list ruleset | grep -c 'iif\|ip saddr'
# 2. Every set referenced in nft has elements
nft list sets
# 3. A test packet path matches expectation
nft monitor trace
# in another terminal: curl from a blocklisted IP
nft monitor trace is the single best debugging tool the migration gives you. It shows packets traversing the ruleset in real time, with the chain names they hit and the verdicts. iptables had nothing equivalent — the closest was iptables -t raw -A PREROUTING -j TRACE, which dumped to dmesg and was painful.
Should you migrate at all?
If your iptables command is already iptables-nft under the hood (check with iptables --version — it’ll say (nf_tables)), and your ruleset is small, the migration buys you better debugging tools and slightly faster rule evaluation. It costs you a couple of hours of careful work and the small risk of a brief outage during the cutover.
If you’re still on legacy iptables (Ubuntu 18 / CentOS 7 era), the migration to nftables is overdue and worth doing soon — those distros are EOL or about to be, and the next OS upgrade will force the issue under worse conditions.
Either way: take the backup, run from tmux with a panic console, and check those three silent misses before declaring victory.
Cover photo: Brett Sayles on Pexels.
