nftables vs iptables: The Linux Firewall, Then and Now
nftables is the successor to iptables in the Linux kernel: one tool for IPv4, IPv6 and more, sets and maps, atomic rule updates, and a cleaner syntax. How the two relate (including iptables-nft), a complete server ruleset, translating rules, and the Docker, UFW and firewalld interactions that trip people up.
For two decades, Linux firewalls meant iptables. Its successor, nftables, has been in the kernel since 3.13 (2014) and is now the default backend on Debian, Ubuntu, RHEL and Fedora. Even when you type iptables today, you're very likely talking to nftables underneath.
How they relate
Both are front ends to netfilter, the kernel's packet-filtering framework.
- Legacy iptables used separate tools and kernel tables per protocol:
iptables,ip6tables,arptables,ebtables. - nftables replaces all of them with one kernel subsystem and one tool,
nft, plus a small virtual machine in the kernel that evaluates rules. iptables-nftis a compatibility layer: the familiariptablescommand syntax, but rules are stored in nftables. Check which you have:
iptables -V
# iptables v1.8.x (nf_tables) ← compatibility layer over nftables
# iptables v1.8.x (legacy) ← old backend
Mixing legacy iptables and nftables rules on one host is a recipe for confusion — both get evaluated, and neither tool shows the other's rules.
What nftables improves
| iptables | nftables | |
|---|---|---|
| Protocols | One tool per family | inet family covers IPv4 + IPv6 in one table |
| Tables/chains | Fixed built-ins | You create only what you need |
| Updates | Rule by rule, non-atomic | Whole ruleset replaced atomically |
| Matching many addresses | One rule per address (or ipset) | Native sets and maps |
| Multiple actions per rule | No | Yes (e.g. count + log + drop) |
| Performance with large rule lists | Linear | Sets are hash/tree lookups |
A complete server ruleset
This example is for a standalone server whose firewall you manage entirely with nftables. flush ruleset removes every nftables table, including rules installed by Docker or firewalld. Don't apply it as-is on those hosts. Replace the documentation-only admin IPs with your own before loading it, and keep console or rescue access available while testing.
#!/usr/sbin/nft -f
flush ruleset
table inet filter {
set admin_ips {
type ipv4_addr
elements = { 203.0.113.7, 198.51.100.20 }
}
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_ips accept
tcp dport { 80, 443 } accept
limit rate 5/minute log prefix "nft-drop: "
}
chain forward {
type filter hook forward priority filter; policy drop;
}
chain output {
type filter hook output priority filter; policy accept;
}
}
Load it atomically and check:
sudo nft -c -f /etc/nftables.conf # syntax check only
sudo nft -f /etc/nftables.conf
sudo nft list ruleset
sudo systemctl enable nftables # load at boot
Always keep an existing SSH session open while changing firewall rules on a remote server, so a mistake doesn't lock you out. (SSH Hardening, CIDR Notation Explained)
Sets can be updated live without reloading the ruleset — handy for blocklists:
sudo nft add element inet filter admin_ips '{ 192.0.2.44 }'
Translating iptables rules
iptables-translate -A INPUT -p tcp --dport 443 -j ACCEPT
# nft 'add rule ip filter INPUT tcp dport 443 counter accept'
iptables-save > rules.v4
iptables-restore-translate -f rules.v4 > rules.nft
Review the output — translations are literal, and you'll usually want to restructure into the inet family and sets.
The interactions that trip people up
- Docker programs its own NAT and forwarding rules. Published ports (
-p 5432:5432) can bypass host INPUT rules because traffic is DNAT'd and goes through FORWARD. Bind published ports to127.0.0.1when only local access is needed. With Docker's iptables backend, custom filtering belongs inDOCKER-USER. With its native nftables backend, there is noDOCKER-USERchain: use a separate table with a forward base chain and appropriate priority, following Docker's nftables documentation. Don't edit Docker-owned tables. (Docker Compose Networking, Container Networking Internals) - UFW is a front end that writes iptables rules (through
iptables-nfton modern systems). Pick one way of managing the firewall per host. (UFW Firewall Basics) - firewalld uses nftables as its backend on current distributions; don't hand-edit rules underneath it.
- fail2ban can use either; configure the
nftablesactions on nftables hosts. (fail2ban) - Rule priority: multiple base chains on the same hook all run, ordered by priority; an
acceptin one chain doesn't stop another chain on the same hook from dropping the packet.
Should you migrate?
On a new server, write nftables directly — it's cleaner and atomic. On an existing iptables-nft setup that works, there's no urgency; you're already on nftables under the hood. If anything still uses legacy iptables, plan to move off it.
The summary
- nftables replaces iptables/ip6tables/ebtables with one tool, one syntax and atomic updates.
- Modern
iptablescommands usually write nftables rules viaiptables-nft. - Use
inettables and sets, check withnft -c, and watch Docker's port publishing.
EasySpawn gives each server its own isolated VM with the network firewall managed for you, so only your app's web ports face the internet. See how it works or join the waitlist.
Related: Linux Namespaces Explained · What Is eBPF? · Secure a New VPS · Agent Egress Control With a Proxy
Keep reading
How to Run AI-Generated Code Safely
AI-generated code is usually well-intentioned and occasionally destructive, and the packages it installs are a supply-chain risk of their own. A practical, layered approach — what the code can see, reach, consume, and outlive — with a hardened Docker command you can use today.
Firecracker vs gVisor vs Containers: Choosing Isolation for Untrusted Code
Containers share a kernel; gVisor intercepts it; Firecracker gives each workload its own. How the three isolation models actually work, what each costs in performance and compatibility, and how to match the boundary to the threat — for AI agents, multi-tenant platforms, and code execution.