How to Use the iptables and nftables Commands in Linux to Configure Firewall Rules and Packet Filtering

The Linux kernel’s Netfilter framework forms the underlying packet processing and stateful inspection architecture for virtually all Linux network security appliances, container virtual switches, and standalone server firewalls. For over two decades, systems engineers interfaced with Netfilter through the iptables command suite. However, contemporary enterprise distributions (including Debian, Ubuntu 22.04/24.04 LTS, and RHEL 9) have transitioned standard packet filtering infrastructure to nftables (invoked via the nft utility).

While iptables relied on separate binaries for IPv4 (iptables), IPv6 (ip6tables), and ARP (arptables) with fixed table chains, nftables consolidates multi-protocol packet inspection into a single runtime environment with configurable tables, atomic rule updates, and significantly higher packet evaluation throughput. This technical guide covers packet filtering fundamentals, compares syntax differences between legacy iptables and modern nftables, and provides production-grade firewall configuration templates.

Architectural Comparison: iptables vs. nftables

Understanding how the two command suites interface with the Linux kernel Netfilter hooks clarifies why modern Linux distributions default to nftables:

Feature / Attribute iptables nftables (nft)
Protocol Handling Fragmented across separate binaries (iptables, ip6tables, arptables, ebtables). Unified single binary handling IPv4, IPv6, ARP, and bridge frames under the inet family.
Table Structure Fixed, hardcoded kernel tables (filter, nat, mangle, raw). Completely customizable tables and chains created by the administrator.
Rule Updating Rule replacements flush and reload entire monolithic tables. Atomic rule addition, modification, and deletion without table re-initialization.
Performance Linear rule scanning causing latency degradation with large rule counts. Internal bytecode compilation using fast dictionary sets and interval lookups.

Essential Packet Filtering with Legacy iptables

Even in modern systems, backward-compatible translation layers (iptables-nft) keep legacy syntax relevant for legacy shell scripts and container daemons like Docker.

Inspecting Active Firewall Rules

To list all active rules across the standard filter table with packet and byte counters:

sudo iptables -L -n -v --line-numbers

Establishing Default Policies (Deny-by-Default)

A resilient firewall drops unsolicited ingress packets while allowing outbound client connections:

# Set default chain policies
sudo iptables -P INPUT DROP
sudo iptables -P FORWARD DROP
sudo iptables -P OUTPUT ACCEPT

# Allow established and related return traffic
sudo iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT

# Allow local loopback interface traffic
sudo iptables -A INPUT -i lo -j ACCEPT

# Allow inbound SSH on port 22
sudo iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -j ACCEPT

Persisting iptables Across Reboots

Rules created via iptables reside purely in volatile kernel memory. On Debian/Ubuntu systems, install and invoke the persistence package:

sudo apt install iptables-persistent
sudo netfilter-persistent save

Modern Packet Filtering with nftables (nft)

The nft command provides a cleaner syntax that eliminates redundant protocol flags and allows multi-port ranges within a single statement.

Listing the Complete nftables Ruleset

To view all active tables, chains, and rules currently evaluated by the kernel:

sudo nft list ruleset

Creating a Production Firewall with nftables

Create a secure baseline configuration that blocks incoming probes, authorizes loopback communications, and allows inbound web and SSH traffic:

# 1. Create a unified IPv4/IPv6 table
sudo nft add table inet filter

# 2. Add standard base chains linked to Netfilter kernel hooks
sudo nft add chain inet filter input { type filter hook input priority 0 \; policy drop \; }
sudo nft add chain inet filter forward { type filter hook forward priority 0 \; policy drop \; }
sudo nft add chain inet filter output { type filter hook output priority 0 \; policy accept \; }

# 3. Allow loopback traffic
sudo nft add rule inet filter input iifname "lo" accept

# 4. Accept established and related sessions
sudo nft add rule inet filter input ct state established,related accept

# 5. Drop invalid packets
sudo nft add rule inet filter input ct state invalid drop

# 6. Permit inbound SSH (22), HTTP (80), and HTTPS (443) using a set
sudo nft add rule inet filter input tcp dport { 22, 80, 443 } ct state new accept

# 7. Permit ICMP ping with rate limiting
sudo nft add rule inet filter input ip protocol icmp icmp type echo-request limit rate 5/second accept

Translating Legacy iptables Rules to nftables

Administrators migrating legacy bash scripts can convert complex rule definitions using the native iptables-translate utility:

# Translate an iptables command to nft syntax
iptables-translate -A INPUT -p tcp --dport 8080 -j ACCEPT

The console prints the equivalent native nft statement:

nft add rule ip filter INPUT tcp dport 8080 counter accept

Persisting and Loading Configuration Files

In modern distributions, nftables loads its ruleset on boot from /etc/nftables.conf:

  1. Export the active, working ruleset to the configuration file:
    sudo nft list ruleset | sudo tee /etc/nftables.conf
  2. Enable the systemd service to ensure rules are injected before network interfaces initialize during system startup:
    sudo systemctl enable --now nftables
  3. To test syntax validity when authoring rules manually inside /etc/nftables.conf before loading:
    sudo nft -c -f /etc/nftables.conf

Get the best tech tips delivered straight to your inbox.

Join thousands of readers mastering Apple, Google, Microsoft, and Linux.