Choosing between nftables, iptables, and UFW in 2026
A practical firewall decision guide for Linux operators: when nftables is the right default, when UFW is still enough, and why Docker keeps iptables syntax relevant.
The short answer is not complicated:
- For a fresh Linux server you control end to end, pick nftables.
- For a tiny Ubuntu box with simple allow/deny needs, UFW is still fine.
- For Docker-heavy or legacy estates, iptables syntax still matters even when nftables is underneath.
The long answer matters because people are not choosing a packet filter. They are choosing an operator interface for the Linux Netfilter stack, plus all the ecosystem assumptions that come with it.
These are not three different kernels
All three names ultimately sit on top of the same kernel packet-filtering machinery: Netfilter.
- iptables is the older userspace interface.
- nftables is the modern replacement and the official successor from the Netfilter project itself, which notes it has been upstream since kernel 3.13 and includes an iptables compatibility layer.
- UFW is not its own firewall engine at all. It is a frontend, mostly aimed at easier host-based administration on Ubuntu-like systems, which Ubuntu still documents as the user-friendly default.
That distinction clears up most confusion immediately. UFW is about convenience. nftables is about the model. iptables is about compatibility, installed habits, and tooling inertia.
Why nftables is the clean default
If you are starting from zero, nftables is simply the better shape of firewall policy.
The two practical reasons are:
- It unifies IPv4 and IPv6 much more cleanly.
- It makes the packet path more explicit.
The nftables families documentation explains the big win: you can use the inet family and keep IPv4 and IPv6 policy in one ruleset instead of mentally maintaining parallel worlds. That alone removes a lot of quiet operator error.
The chain documentation makes the second win clear. nftables expects you to create base chains explicitly with hooks and priorities. That is more work than old cargo-cult iptables -A INPUT ... snippets, but it is also more honest. The packet-processing graph becomes something you can reason about instead of folklore.
That honesty ages well. Once a host grows beyond "allow SSH, maybe allow one UDP service," old append-rule habits stop being charming and start becoming maintenance debt.
For a typical public VPS, a minimal nftables policy is readable enough:
table inet filter {
chain input {
type filter hook input priority 0; policy drop;
iif "lo" accept
ct state established,related accept
tcp dport 22 accept
udp dport 51820 accept
}
}
That is already good enough for the kind of host in /blog/self-hosted-wireguard-2026.
When UFW is still the right answer
It is fashionable to sneer at UFW. That is mostly performative.
UFW is still a good tool when all of these are true:
- the host is simple
- the policy is simple
- the admin values readability over expressiveness
- no container runtime is going to play games with NAT chains
Ubuntu's docs still describe UFW as a friendly IPv4/IPv6 host firewall, and they are right. For a single-purpose bastion or tiny app server, this:
ufw default deny incoming
ufw default allow outgoing
ufw allow 22/tcp
ufw allow 51820/udp
ufw enable
is not morally inferior to a handcrafted nftables ruleset. It is just smaller.
The real limit is not that UFW is "bad." It is that UFW intentionally hides parts of the lower-level model. The UFW framework docs make this explicit: by default, UFW manages the filter table and can be extended manually for nat, raw, or mangle, but that is no longer the simple mental model people chose UFW for in the first place.
So the sweet spot is narrow and perfectly respectable:
small Ubuntu host, small rule set, human-readable policy
Once you are maintaining custom before-rules, exceptions around NAT, and a growing pile of comments explaining why UFW is behaving strangely, you are no longer using UFW for simplicity. You are simulating a lower-level firewall while pretending not to.
When iptables still wins
Saying nftables is the modern default is not the same as saying iptables is dead.
iptables still wins when the surrounding ecosystem thinks in iptables terms:
- old automation
- existing playbooks
- Kubernetes-adjacent hosts
- container runtimes
- security tools that still document chain insertion in iptables language
This is the subtle part many comparisons miss: because the nftables project provides an iptables compatibility layer, a lot of systems today are effectively "iptables syntax targeting nftables underneath." The operator experience is still iptables-shaped even though the kernel path is newer.
That can be a perfectly rational compromise. A working estate with years of scripts is not improved just because someone wants prettier syntax.
This is especially true for teams. A firewall that five operators can read and debug is better than a theoretically superior one that only one person enjoys.
The equivalent baseline policy still looks familiar:
iptables -P INPUT DROP
iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -p tcp --dport 22 -j ACCEPT
iptables -A INPUT -p udp --dport 51820 -j ACCEPT
If your team can read that in five seconds and debug it at 2 a.m., it is not obsolete in the ways that matter operationally.
The Docker caveat that changes the answer
This is the part that decides a lot of real deployments.
Docker's packet-filtering docs say published container ports can bypass UFW because Docker diverts traffic in the nat table before it reaches the chains UFW usually manages. That means "I allowed nothing in UFW" is not the same thing as "nothing is reachable."
Worse, Docker's Ubuntu installation docs currently warn that firewall rules created directly with nft are not supported on a system with Docker installed. Docker expects iptables or ip6tables rules and specifically calls out the DOCKER-USER chain.
That does not mean nftables is fake. It means container reality still drags a lot of hosts back toward iptables-compatible workflows.
So if the host runs Docker and published ports matter, the naive recommendation changes:
- Do not assume UFW gives you the whole picture.
- Do not freestyle direct
nftpolicy against Docker unless you know the exact interaction model you are accepting. - Do reason in terms Docker itself documents, including
iptablesandDOCKER-USER.
This is also why firewall-adjacent tools like CrowdSec still discuss both models; its firewall bouncer docs support iptables and nftables because the real world is mixed.
Decision table
| Situation | Best default | Why |
|---|---|---|
| Fresh public VPS, no containers | nftables | Clean model, unified IPv4/IPv6 policy, easy to grow |
| Tiny Ubuntu bastion or admin box | UFW | Readable, fast to audit, enough for simple host policy |
| Docker host with published ports | iptables mindset | Matches Docker docs and chain expectations |
| Legacy automation estate | iptables | Existing tooling and operator fluency matter more than style points |
| Complex gateway or multi-zone host | nftables | Better expression model once policy stops being tiny |
Migration without making a mess
If you are on UFW and outgrowing it, migrate because the policy shape changed, not because somebody on the Internet called it a toy.
If you are on iptables and everything around you assumes iptables, migrate only when the benefits are concrete:
- combining IPv4 and IPv6 policy
- cleaning up unreadable rule sprawl
- reducing duplicated chain logic
If those benefits are not real for your environment, "leave the working firewall alone" is an adult answer.
My rule in 2026 is simple:
- nftables for greenfield
- UFW for deliberately simple hosts
- iptables where the ecosystem already picked it for you
That is not ideological. It is just the difference between choosing a firewall for a blog post and choosing one for a machine you may have to debug under pressure.