Migrating from iptables to the modern firewall framework
iptables has shaped Linux firewalls for two decades, but it hits clear limits with large rule sets and complex logic. nftables replaces iptables, ip6tables, arptables, and ebtables with a single, consistent framework featuring native sets, atomic updates, and noticeably better performance at scale.
Table of Contents
- 1. Why iptables hits its limits with large rule sets
- 2. The architecture of nftables: tables, chains, and the kernel interpreter
- 3. Syntax differences between iptables and nftables in direct comparison
- 4. Sets and maps: managing large rule volumes efficiently
- 5. Migration strategy: moving from an existing iptables rule set to nftables
- 6. The iptables-nft compatibility layer for gradual transitions
- 7. Practical example: a complete server firewall with rate limiting
- 8. Monitoring and debugging nftables rule sets in production
- 9. Best practices and common pitfalls during migration
- 10. Summary
- 11. FAQ
1. Why iptables hits its limits with large rule sets
iptables is rule based and evaluates every rule in a chain sequentially from top to bottom. With a few dozen rules that hardly matters, but once a server manages hundreds or thousands of rules, for example for IP based access control or per customer port allowances, evaluation time grows linearly with the number of rules. In the worst case, every packet has to traverse the entire chain before a verdict is reached.
Another structural problem is the lack of atomicity in classic iptables invocations: anyone changing several rules one after another with individual iptables commands risks a short window with an inconsistent rule set, in which packets are either allowed too generously or dropped incorrectly. On top of that, iptables and ip6tables maintain separate rule sets for IPv4 and IPv6, which means double maintenance and encourages inconsistencies between the two protocol versions.
2. The architecture of nftables: tables, chains, and the kernel interpreter
nftables was designed as the successor to iptables, ip6tables, arptables, and ebtables, bundling all four frameworks into one shared kernel infrastructure. Instead of issuing a separate kernel module call for every rule, nftables compiles rule sets into compact bytecode that a virtual machine inside the kernel executes, an approach comparable to the BPF model used by other modern Linux subsystems.
The basic structure consists of tables, which set an address family such as ip, ip6, or the combined inet, chains, which attach to netfilter hooks like input, output, or forward, and rules within those chains. Unlike iptables, the inet address family lets you maintain IPv4 and IPv6 rules together in the same chain, which makes duplicate rule sets unnecessary and structurally enforces consistency between the two protocol versions.
# Create a new table for combined IPv4/IPv6 filtering
nft add table inet filter
# Attach an input chain with default policy drop to the netfilter hook
nft add chain inet filter input { type filter hook input priority 0 \; policy drop \; }
# Show the currently active rule set
nft list ruleset
3. Syntax differences between iptables and nftables in direct comparison
The most noticeable change is the declarative, custom grammar of nftables compared to the many individual flags of iptables. Where iptables requires a separate flag for every condition, such as -p, --dport, or -s, nftables expresses conditions as readable statements that combine protocol, field, and value on a single line, making rules considerably easier for humans to follow.
An SSH rule written in iptables as iptables -A INPUT -p tcp --dport 22 -j ACCEPT reads as nft add rule inet filter input tcp dport 22 accept in nftables. For migrating individual rules, the nftables package ships iptables-translate and ip6tables-translate, which translate an existing iptables command directly into the matching nft syntax, cutting down manual translation effort considerably.
# Translate a single iptables rule into nftables syntax
iptables-translate -A INPUT -p tcp --dport 22 -j ACCEPT
# Resulting output, usable directly as an nft command
# nft add rule ip filter INPUT tcp dport 22 counter accept
4. Sets and maps: managing large rule volumes efficiently
Sets are one of the biggest structural improvements over iptables. Instead of creating a separate rule for every allowed IP address, a set groups any number of values into a single data structure that the kernel manages internally as a hash table, or, for intervals like IP ranges, as a balanced tree. Checking a set with a thousand entries costs barely more processing time than checking a set with ten entries, whereas the same logic in iptables would mean a thousand individual, sequentially evaluated rules.
Maps go a step further and link a key to a value, for example a destination port number to a verdict action such as accept or drop. That lets you express complex decision tables, which in iptables would have required several separate chains with jump rules, as a single compact nftables map, one that can even be updated at runtime without reloading the entire rule set.
# Create and populate a set of allowed admin IP addresses
nft add set inet filter admin_ips { type ipv4_addr \; flags interval \; }
nft add element inet filter admin_ips { 203.0.113.10, 203.0.113.0/28 }
# Rule referencing the set for SSH access
nft add rule inet filter input tcp dport 22 ip saddr @admin_ips accept
# Map from destination port to verdict for several services at once
nft add map inet filter port_verdict { type inet_service : verdict \; }
nft add element inet filter port_verdict { 80 : accept, 443 : accept, 3306 : drop }
nft add rule inet filter input tcp dport vmap @port_verdict
5. Migration strategy: moving from an existing iptables rule set to nftables
A controlled migration works best in two stages. First, the current rule set is backed up with iptables-save and then fully converted into nftables syntax with iptables-restore-translate, which, unlike single rule translation with iptables-translate, converts the entire rule set including chains and policies in one pass. The resulting nft script should then be reviewed manually to fix up rules that automatic translation does not fully cover, such as certain match modules or complex target actions.
In practice, a parallel run works well: the translated nft rule set is first loaded on a test system or in a separate network namespace and validated against real test cases, before it replaces the existing iptables rule set on production servers. It matters that all rules are loaded from a single file with nft -f before the production cutover, since that lets nftables activate the whole rule set atomically, instead of rule by rule with potential intermediate states as in iptables.
# Back up the existing iptables rule set
iptables-save > /root/iptables-backup.rules
# Translate the full rule set into nftables syntax
iptables-restore-translate -f /root/iptables-backup.rules -o /root/nftables-migrated.nft
# Load and inspect the translated rule set atomically
nft -f /root/nftables-migrated.nft
nft list ruleset
6. The iptables-nft compatibility layer for gradual transitions
For environments where existing automation, configuration management, or monitoring scripts are firmly built around the iptables command, the iptables-nft compatibility layer offers a gentle transition. These are alternative iptables, ip6tables, ebtables, and arptables binaries that accept the same familiar command line syntax, but implement their rules internally through the nftables kernel infrastructure instead of the classic x_tables kernel module.
On most modern distributions such as Debian, Ubuntu, and RHEL, this layer is already active by default, which can be verified with iptables --version, since the output then shows nf_tables instead of legacy. The big advantage is that rules created via iptables-nft are visible through nft list ruleset and can be freely combined with natively written nft rules in the same table, allowing a smooth, low risk transition from pure iptables scripting to native nftables syntax without interrupting operation.
# Check which backend the iptables command currently uses
iptables --version
# iptables v1.8.9 (nf_tables)
# Explicitly switch between legacy and nft backend, Debian/Ubuntu
update-alternatives --set iptables /usr/sbin/iptables-nft
update-alternatives --set ip6tables /usr/sbin/ip6tables-nft
7. Practical example: a complete server firewall with rate limiting
A realistic firewall setup for a Magento hosting server combines default rules, an admin IP set, and rate limiting against SSH brute force attempts in a single, readable configuration file. Rule order still matters, since nftables chains are still evaluated sequentially, just far more efficiently once sets and maps replace dozens of individual rules.
The native limit rate expression is particularly valuable, letting you cap connection rates directly within a rule instead of reaching for a separate hashlimit module with a complex parameter list as in iptables. That makes it straightforward to build a robust, readable firewall configuration that opens HTTP, HTTPS, and SSH deliberately and drops everything else by default.
table inet filter {
set admin_ips {
type ipv4_addr
flags interval
elements = { 203.0.113.10, 198.51.100.0/28 }
}
chain input {
type filter hook input priority 0; policy drop;
ct state established,related accept
iifname lo accept
tcp dport { 80, 443 } accept
tcp dport 22 ip saddr @admin_ips accept
tcp dport 22 ct state new limit rate 5/minute accept
tcp dport 22 ct state new counter drop
}
}
8. Monitoring and debugging nftables rule sets in production
For troubleshooting in production, nft list ruleset -a not only shows the active configuration but also exposes internal handle IDs, letting you delete or replace individual rules without reloading the entire rule set. Counters within a rule, as used on the SSH drop rule in the example above, continuously track packet and byte counts and can be inspected with nft list counters to spot suspicious patterns early.
For deeper analysis, nftables also offers native logging expressions that, unlike iptables, are integrated directly into the rule syntax and can be tagged with a specific prefix to unambiguously identify dropped packets in the kernel log. Combined with journalctl -k or a central log aggregator, this makes it possible to trace which rule fired for which packet, which speeds up debugging considerably, especially for more complex set and map based rule sets.
# List the rule set with handle IDs for targeted edits
nft list ruleset -a
# Additionally log dropped SSH packets
nft add rule inet filter input tcp dport 22 ct state new \
log prefix \"ssh-drop: \" drop
# Search the kernel log for the logged entries
journalctl -k -g ssh-drop
9. Best practices and common pitfalls during migration
The most common mistake during migration is still maintaining separate tables for IPv4 and IPv6, even though the inet address family exists to solve exactly that problem. Anyone writing new rule sets from scratch should consistently use inet tables and only fall back to ip or ip6 where protocol specific logic is genuinely needed, for example ICMP specific rules that use different message types between ICMPv4 and ICMPv6.
Another pitfall involves persistent configuration: unlike iptables, where distributions often ship a dedicated persistence package such as iptables-persistent, nftables typically loads its rule set directly from a file such as /etc/nftables.conf via the systemd unit nftables.service. Changes made only interactively with nft commands are lost on the next reboot unless they are also written into that configuration file, which is why production rule sets should always be loaded from a version controlled file.
| Criterion | iptables | nftables |
|---|---|---|
| Rule evaluation | Sequential per rule, linear with rule count | Bytecode in a kernel VM, efficient even with many rules |
| IPv4/IPv6 | Separate tools iptables and ip6tables | Shared inet address family in one table |
| Large rule volumes | One rule per IP or port required | Native sets and maps with near constant lookup time |
| Atomic updates | Only as a whole via iptables-restore | Default behavior on every nft -f load |
| Migration path | No migration path needed | iptables-nft compatibility layer for a gradual switch |
Mironsoft
Server administration, Docker hosts, and performance tuning
Linux servers nobody on the team really understands anymore?
We handle setup, hardening, and performance tuning of Linux servers and Docker hosts for Magento deployments, documented and traceable instead of grown and unclear.
Server Audit
Review the existing server configuration for security gaps and performance bottlenecks.
Docker Host Setup
Set up and secure production-ready Docker environments for Magento cleanly.
Monitoring & Tuning
Measure resource usage and tune systemd, kernel, and services with purpose.
10. Summary
nftables Migration
Core benefit
Native sets and maps replace thousands of individual rules at constant performance
Migration tool
iptables-restore-translate converts the whole rule set in a single pass
Transition path
iptables-nft compatibility layer allows a gradual switch without downtime
Biggest pitfall
Separate ip and ip6 tables instead of the unified inet address family