UFW, iptables and nftables
UFW shows one set of rules, iptables -L shows another, and nft list ruleset shows a third, longer list. They are all views of the same firewall in the kernel. Once you know how they stack, the output starts to make sense.
Published . Debian 10 and later and Ubuntu 21.04 and later, including Ubuntu 22.04 and 24.04 LTS, where nftables is the default backend.
- One firewall, several tools
- What your distro uses by default
- See the real ruleset
- Why mixing tools confuses people
- Pick one tool and stick to it
- Moving old iptables rules over
One firewall, several tools
The firewall itself lives in the Linux kernel. Everything else is a tool that writes rules into it. On a current server you meet four names:
- nftables is the current kernel firewall framework. Its command is
nft. - iptables-legacy is the old
iptables, which writes to the older x_tables part of the kernel. - iptables-nft takes the familiar
iptablessyntax and writes the rules into nftables instead. On current Debian and Ubuntu, this is what runs when you typeiptables. - UFW is a front end on top. It doesn't filter anything itself. It generates iptables rules from its files in
/etc/ufw/and loads them withiptables-restore.
So on a current Ubuntu server the chain usually goes: ufw writes iptables rules, iptables-nft turns them into nftables rules, and the kernel runs those.
What your distro uses by default
Debian made nftables the default framework in Debian 10 (Buster). There, iptables uses nftables through the iptables-nft layer. Ubuntu switched with 21.04, and the 22.04 LTS release notes state that nftables is the default firewall backend.
To see which variant your iptables is, ask for its version:
sudo iptables -V
(nf_tables) after the version number means iptables-nft. (legacy) means the old backend. Debian and Ubuntu pick between them with the alternatives system, and switching back to legacy looks like this:
sudo update-alternatives --set iptables /usr/sbin/iptables-legacy
sudo update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy
Don't switch unless a program you rely on needs it. The Ubuntu release notes put it plainly: all applications on the system must agree on one backend.
See the real ruleset
Because UFW, Docker and anything else using iptables-nft all end up in nftables, one command shows all of it:
sudo nft list ruleset
Expect a long list. Rules from iptables-nft appear in tables such as table ip filter and table ip6 filter, named after the address family and the old iptables table. UFW's chains have names starting with ufw-. Docker adds its own chains, such as DOCKER-USER. Any native nftables rules appear in their own tables, often table inet filter.
Compare with the narrower views:
sudo ufw status verboseshows only the rules you added through UFW, not its built-in rules or anyone else's.sudo iptables -Sshows only the rules written in iptables form, not native nftables tables.
If iptables-legacy is installed, its rules are in the old x_tables part of the kernel and won't show in nft list ruleset at all. Check with sudo iptables-legacy -S.
Why mixing tools confuses people
Each tool only shows and manages its own part. When two of them write rules, nobody sees the whole picture, and the results surprise you.
The key rule in nftables: when several base chains hook into the same point, a packet passes through all of them in priority order. An accept in one chain isn't final, and the packet still goes to the next. A drop is final straight away. So a port you opened with ufw allow can still be blocked by a separate nftables table that drops it. To let traffic in, every chain on the path has to agree. To block it, one chain is enough.
Other common traps:
- Legacy and nft at once. The nftables wiki warns against using the nft and legacy tools at the same time. The rules end up in two kernel subsystems and interact in ways that are hard to predict.
- A flush that removes everyone's rules.
nft flush ruleseterases the complete ruleset. If the file loaded at boot bynftables.service(by default/etc/nftables.conf) starts withflush ruleset, loading it removes UFW's and Docker's rules too, until those tools load them again. - Docker. Docker writes its own rules for published ports, and that traffic never reaches UFW's input rules. See Docker bypasses UFW.
Pick one tool and stick to it
The Debian wiki's answer to whether you should mix nftables and iptables rulesets is no, unless you know what you are doing. In practice:
- On a typical Ubuntu server, use UFW for your own rules, and leave Docker's chains to Docker. Follow setting up UFW safely.
- If you want native nftables, write your rules in
/etc/nftables.conf, turn it on withsystemctl enable nftables.service, and don't run UFW alongside it. - Whatever you choose, read
nft list rulesetwhen something doesn't add up, and confirm the result with a scan from outside.
Moving old iptables rules over
If you have old iptables commands from a script or a guide, iptables-translate prints the nftables version of a rule:
iptables-translate -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -j ACCEPT
It prints an nft add rule command for you to check. For a whole saved ruleset, iptables-restore-translate does the same. Read the output before you load it. A firewall change over SSH is the classic way to lock yourself out, so keep a way back in ready.
Stenvakt reads the whole ruleset, not one tool's view of it. Its Review is a read-only check of every server, firewall included, and you see a Preview of any fix before it runs. Fixes run in safe mode: if a change cuts off SSH, the server rolls it back by itself. It's a Mac app and isn't out yet: get early access.
Sources
- Debian Wiki: nftables
- Ubuntu 22.04 LTS release notes
- xtables-nft(8) manual page (man7.org)
- nftables wiki: Moving from iptables to nftables
- nftables wiki: Configuring chains