iptables & nftables

iptables is the traditional Linux firewall — it's been filtering packets for 20+ years. You've interacted with it every time Docker creates a container or you configure a firewall rule. nftables is its modern replacement: cleaner syntax, better performance, one tool instead of four. Both work through the same kernel mechanism: Netfilter hooks.

iptables — Tables, Chains, and Rules

How does iptables organize its rules? iptables has 4 tables (filter, nat, mangle, raw), each with built-in chains. A chain is a list of rules. Each rule says: "if packet matches these conditions, take this action." Packets flow through chains in order. First matching rule wins. If no rule matches, the chain's default policy applies (ACCEPT or DROP).
Tables and their chains: filter (default — used for basic firewall rules): INPUT ← packets destined for this host FORWARD ← packets passing through (router mode) OUTPUT ← packets originated from this host nat (Network Address Translation): PREROUTING ← DNAT: change destination before routing POSTROUTING ← SNAT/MASQUERADE: change source after routing OUTPUT ← NAT for locally generated packets mangle (packet modification — TTL, DSCP, marks): PREROUTING, INPUT, FORWARD, OUTPUT, POSTROUTING raw (connection tracking bypass): PREROUTING, OUTPUT

Common iptables Commands

# View rules (with line numbers and packet counts) iptables -L -v -n --line-numbers iptables -t nat -L -v -n # NAT table # Allow incoming SSH iptables -A INPUT -p tcp --dport 22 -j ACCEPT # Block an IP iptables -A INPUT -s 192.168.1.100 -j DROP # Allow established connections (stateful firewall) iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT # Drop everything else (default deny at end of INPUT chain) iptables -A INPUT -j DROP # Delete a rule by line number iptables -D INPUT 3 # Insert at specific position (insert before line 2) iptables -I INPUT 2 -p tcp --dport 80 -j ACCEPT # Set default policy iptables -P INPUT DROP iptables -P FORWARD DROP iptables -P OUTPUT ACCEPT # Save rules (Debian/Ubuntu) iptables-save > /etc/iptables/rules.v4 # Restore: iptables-restore < /etc/iptables/rules.v4

NAT — Network Address Translation

# MASQUERADE: share one public IP (your router does this) # Traffic from 192.168.0.0/24 going out eth0 gets source NAT'd iptables -t nat -A POSTROUTING -s 192.168.0.0/24 -o eth0 -j MASQUERADE echo 1 > /proc/sys/net/ipv4/ip_forward # SNAT: static source NAT (you know the external IP) iptables -t nat -A POSTROUTING -s 10.0.0.0/8 -o eth0 \ -j SNAT --to-source 203.0.113.10 # DNAT: port forwarding (expose internal service externally) # Forward external port 8080 to internal host 10.0.0.5:80 iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 8080 \ -j DNAT --to-destination 10.0.0.5:80 # Docker uses this automatically for -p 8080:80 port mappings

nftables — The Modern Replacement

Why was nftables created if iptables works? iptables has four separate tools (iptables, ip6tables, arptables, ebtables) for IPv4, IPv6, ARP, and bridge filtering. Rules can't be updated atomically — a window exists during rule changes where packets slip through. nftables unifies all four, supports atomic rule updates, has better performance (JIT compilation), and a cleaner syntax. Debian/Ubuntu now default to nftables under the hood even when you run `iptables` commands.
# nftables: create a simple firewall nft add table inet filter nft add chain inet filter input \ { type filter hook input priority 0 \; policy drop \; } nft add chain inet filter output \ { type filter hook output priority 0 \; policy accept \; } # Allow established + loopback nft add rule inet filter input ct state established,related accept nft add rule inet filter input iif lo accept # Allow SSH nft add rule inet filter input tcp dport 22 accept # View rules nft list ruleset # nftables rule file (/etc/nftables.conf): # table inet filter { # chain input { # type filter hook input priority 0; policy drop; # ct state established,related accept # iif "lo" accept # tcp dport 22 accept # } # } systemctl enable --now nftables

iptables vs nftables

iptablesnftables
IPv6 supportSeparate ip6tables commandUnified (inet family)
Atomic updatesNo — rules apply one at a timeYes — transaction-based
PerformanceLinear rule matchingSets + maps for O(1) lookups
SyntaxVerbose, inconsistentClean, consistent
Tools needediptables, ip6tables, arptables, ebtablesJust nft
Distro supportStill default on RHEL 8-, older DebianDefault on Debian 10+, RHEL 9+

Frequently Asked Questions

What will I learn here?

This page covers the core concepts and techniques you need to understand the topic and progress confidently to the next lesson.

How should I use this page?

Start with the overview, then follow the section links to deepen your understanding. Use the table of contents on the right to jump to specific sections.

What should I read next?

Use the navigation below to continue to the next lesson or explore related topics.