nftables vs iptables
nftables is the current Linux packet filtering framework. iptables is its predecessor, still present but superseded. Understanding both matters because you will encounter iptables in documentation, older systems, and scripts, while nftables is what you should be writing for new setups.
The relationship between iptables and nftables
iptables was the standard Linux firewall tool from the late 1990s through the early 2010s. It uses a kernel framework called Netfilter and a userspace tool for writing rules into it. Over time, the limitations of iptables became apparent: it required separate tools for IPv4 (iptables), IPv6 (ip6tables), ARP (arptables), and bridge (ebtables), its syntax was inconsistent, and rule matching was done sequentially with no optimisation.
nftables was merged into the Linux kernel in version 3.13 (2014) and replaced Netfilter’s old infrastructure with a faster, unified bytecode VM. A single tool (nft) handles all address families. Matching is done via efficient lookup tables and maps.
On modern distributions, iptables is usually iptables-nft — a compatibility wrapper that translates iptables commands into nftables rules internally. The old iptables kernel modules are often not even loaded.
Where each is the default
| Distribution | Default framework |
|---|---|
| Fedora 32+ | nftables |
| Debian 10+ (Buster) | nftables |
| Ubuntu 20.04+ | nftables (ufw uses it) |
| RHEL/CentOS 8+ | nftables (firewalld uses it) |
| Arch Linux | nftables |
| Ubuntu 18.04 | iptables |
| Debian 9 | iptables |
| RHEL/CentOS 7 | iptables |
# Check which is active on your system
sudo nft list ruleset 2>/dev/null | head -5
sudo iptables -L -n 2>/dev/null | head -5
# Check if iptables is the legacy version or the nft shim
iptables --version
# "iptables v1.8.x (nf_tables)" = the nftables shim
# "iptables v1.6.x (legacy)" = the old iptables
iptables: syntax and concepts
Tables and chains
iptables organises rules into tables, each with specific built-in chains:
Table: filter (default, most common)
Chains: INPUT, OUTPUT, FORWARD
Table: nat (Network Address Translation)
Chains: PREROUTING, OUTPUT, POSTROUTING
Table: mangle (packet modification)
Chains: PREROUTING, INPUT, FORWARD, OUTPUT, POSTROUTING
Common iptables commands
# List rules (filter table, default)
sudo iptables -L
sudo iptables -L -n # no name resolution
sudo iptables -L -n -v # verbose (show packet/byte counters)
sudo iptables -L --line-numbers # show rule line numbers
# List a specific chain
sudo iptables -L INPUT -n -v
# List the nat table
sudo iptables -t nat -L -n -v
# Add a rule to the INPUT chain
sudo iptables -A INPUT -p tcp --dport 22 -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 80 -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 443 -j ACCEPT
# Allow established/related connections (essential for outbound to work)
sudo iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
# Allow loopback
sudo iptables -A INPUT -i lo -j ACCEPT
# Set default policy (drop unmatched)
sudo iptables -P INPUT DROP
sudo iptables -P FORWARD DROP
sudo iptables -P OUTPUT ACCEPT
# Insert a rule at a specific position
sudo iptables -I INPUT 1 -p tcp --dport 22 -j ACCEPT
# Delete a rule by number
sudo iptables -D INPUT 3
# Delete a rule by specification
sudo iptables -D INPUT -p tcp --dport 8080 -j ACCEPT
# Flush (delete all rules in a chain)
sudo iptables -F INPUT
sudo iptables -F # flush all chains in filter table
# Zero counters
sudo iptables -Z
# Save and restore
sudo iptables-save > /etc/iptables/rules.v4
sudo ip6tables-save > /etc/iptables/rules.v6
sudo iptables-restore < /etc/iptables/rules.v4
iptables rule examples
# Block a specific IP
sudo iptables -A INPUT -s 203.0.113.5 -j DROP
# Block a subnet
sudo iptables -A INPUT -s 203.0.113.0/24 -j DROP
# Allow ICMP (ping)
sudo iptables -A INPUT -p icmp --icmp-type echo-request -j ACCEPT
# Rate limit connections (simple DDoS mitigation)
sudo iptables -A INPUT -p tcp --dport 80 -m limit --limit 100/minute --limit-burst 200 -j ACCEPT
# Log before dropping
sudo iptables -A INPUT -j LOG --log-prefix "IPT DROP: " --log-level 4
sudo iptables -A INPUT -j DROP
# NAT: masquerade outbound traffic (router/NAT setup)
sudo iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
# Port forwarding
sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8080
nftables: syntax and concepts
Tables, chains, and rules
nftables uses tables, chains, and rules like iptables, but the structure is more explicit:
- Table: a container for chains. Has an address family (
ip,ip6,inet,arp,bridge,netdev) - Chain: an ordered list of rules. Base chains attach to hooks; regular chains are called from base chains
- Rule: a match + action
The inet address family handles both IPv4 and IPv6, so you only need one set of rules.
Common nftables commands
# Show the full ruleset
sudo nft list ruleset
# Show tables
sudo nft list tables
# Show a specific table
sudo nft list table ip filter
sudo nft list table inet firewall
# Show a specific chain
sudo nft list chain inet firewall input
# Add a table
sudo nft add table inet firewall
# Add a base chain (hook = where in network stack, priority = order)
sudo nft add chain inet firewall input '{ type filter hook input priority 0; policy drop; }'
sudo nft add chain inet firewall output '{ type filter hook output priority 0; policy accept; }'
# Add rules
sudo nft add rule inet firewall input iif lo accept
sudo nft add rule inet firewall input ct state established,related accept
sudo nft add rule inet firewall input tcp dport 22 accept
sudo nft add rule inet firewall input tcp dport { 80, 443 } accept
sudo nft add rule inet firewall input drop
# Insert a rule at the beginning
sudo nft insert rule inet firewall input tcp dport 8080 accept
# Delete a specific rule (rules have handles; list with --handle)
sudo nft list chain inet firewall input --handle
sudo nft delete rule inet firewall input handle 5
# Flush a chain (remove all rules)
sudo nft flush chain inet firewall input
# Delete a chain
sudo nft delete chain inet firewall input
# Delete a table (and everything in it)
sudo nft delete table inet firewall
A complete nftables configuration
The recommended approach is to write rules in a configuration file:
# /etc/nftables.conf
table inet firewall {
chain input {
type filter hook input priority 0; policy drop;
# Allow loopback
iif lo accept
# Allow established/related connections
ct state established,related accept
# Drop invalid connections
ct state invalid drop
# Allow ICMP
ip protocol icmp accept
ip6 nexthdr icmpv6 accept
# Allow SSH
tcp dport 22 accept
# Allow HTTP and HTTPS
tcp dport { 80, 443 } accept
# Log and drop everything else
log prefix "nft drop: " level info
drop
}
chain output {
type filter hook output priority 0; policy accept;
}
chain forward {
type filter hook forward priority 0; policy drop;
}
}
# Apply the configuration file
sudo nft -f /etc/nftables.conf
# Test the configuration without applying
sudo nft -c -f /etc/nftables.conf
# Save current ruleset to a file
sudo nft list ruleset > /etc/nftables.conf
# Persist across reboots
sudo systemctl enable nftables
sudo systemctl start nftables
nftables sets and maps
One of nftables’ advantages over iptables is native support for sets (collections of values) and maps (key-value lookups), which are much more efficient than long lists of individual rules.
# Allow multiple ports in one rule (set literal)
sudo nft add rule inet firewall input tcp dport { 22, 80, 443, 8080 } accept
# Named set (reusable)
sudo nft add set inet firewall allowed_ports { type inet_service; }
sudo nft add element inet firewall allowed_ports { 22, 80, 443 }
sudo nft add rule inet firewall input tcp dport @allowed_ports accept
# Blocklist (IP set)
sudo nft add set inet firewall blocklist { type ipv4_addr; }
sudo nft add element inet firewall blocklist { 203.0.113.5, 198.51.100.0/24 }
sudo nft add rule inet firewall input ip saddr @blocklist drop
# Rate limiting
sudo nft add rule inet firewall input tcp dport 22 ct state new limit rate 10/minute accept
Migrating from iptables to nftables
# Convert existing iptables rules to nftables syntax
iptables-save | iptables-restore-translate -f /dev/stdin
# Or convert an iptables-save file
iptables-restore-translate -f /path/to/iptables.rules
# The output can be fed into nft -f to load the converted rules
iptables-save | iptables-restore-translate -f /dev/stdin | sudo nft -f /dev/stdin
Side-by-side comparison
| Task | iptables | nftables |
|---|---|---|
| Allow SSH | iptables -A INPUT -p tcp --dport 22 -j ACCEPT | nft add rule inet filter input tcp dport 22 accept |
| Block an IP | iptables -A INPUT -s 1.2.3.4 -j DROP | nft add rule inet filter input ip saddr 1.2.3.4 drop |
| Allow multiple ports | One rule per port | tcp dport { 80, 443 } accept |
| View rules | iptables -L -n -v | nft list ruleset |
| Set default policy | iptables -P INPUT DROP | policy drop; in chain definition |
| Flush rules | iptables -F | nft flush ruleset |
| Save rules | iptables-save > file | nft list ruleset > file |
| Load rules at boot | iptables-persistent | nftables systemd service |
Frequently Asked Questions
What is the difference between nftables and iptables?
nftables is the successor to iptables, introduced in Linux kernel 3.13 (2014) and now the default on Fedora, Debian 10+, Ubuntu 20.04+, and most other modern distributions. The key differences are: nftables uses a single unified tool (nft) that handles IPv4, IPv6, ARP, and bridge traffic, while iptables required separate tools (iptables, ip6tables, arptables, ebtables). nftables rules are loaded as bytecode into a kernel VM, making them faster and more atomic than iptables chains. nftables also has a cleaner syntax and better set/map support for matching multiple criteria efficiently.
Should I use nftables or iptables for a new Linux server?
Use nftables for new setups. It is the current standard, is faster, and has a cleaner architecture. iptables is still present on most systems (often as iptables-nft, a compatibility shim that translates iptables commands to nftables internally), so existing iptables knowledge and scripts still work. However, writing new rules directly in nftables syntax is the right approach going forward. If you are using a frontend like ufw or firewalld, the underlying tool choice is managed for you — focus on the frontend.
How do I view the current nftables ruleset?
Run sudo nft list ruleset to see all tables, chains, and rules. For a specific table: sudo nft list table ip filter. For a specific chain: sudo nft list chain ip filter input. The nft list ruleset command outputs the entire configuration in a format that can be piped directly back into nft -f to restore the same configuration, making it convenient for backups and review.
What is an nftables table and chain?
In nftables, a table is a container for chains, and a chain is an ordered list of rules. Tables have an address family (ip for IPv4, ip6 for IPv6, inet for both, arp, bridge, netdev). Chains can be base chains (attached to a Netfilter hook and automatically evaluated for packets) or regular chains (only evaluated when explicitly jumped to from another chain). A base chain has a hook (input, output, forward, prerouting, postrouting) and a priority that determines the order relative to other chains on the same hook.
How do I make nftables rules persist across reboots?
On systemd systems, save your ruleset to /etc/nftables.conf and enable the nftables service: sudo nft list ruleset > /etc/nftables.conf followed by sudo systemctl enable nftables. The nftables service reads /etc/nftables.conf at boot. Alternatively, on Debian and Ubuntu you can also use /etc/nftables.conf with the nftables package. For iptables, use iptables-save to write rules and iptables-restore to load them at boot via the iptables-persistent package or a systemd unit.
Can iptables and nftables rules coexist?
It is not recommended to mix them. On modern systems, iptables is implemented as iptables-nft (a translation layer that converts iptables commands to nftables rules), so technically they share the same underlying kernel tables. However, mixing iptables and nft commands to manage the same ruleset can lead to confusing interactions. Choose one frontend and use it consistently. If you use ufw or firewalld, they pick one internally and you do not need to worry about this.