nftables

Modern packet filter — tables, chains, sets, and rule management replacing iptables.

Ruleset Inspection

Show the entire ruleset — the truth about what nftables is doing
sudo nft list ruleset
List all tables
sudo nft list tables
List chains in a specific table
sudo nft list table inet filter
Show a specific chain with handle numbers — needed for deletion
sudo nft -a list chain inet filter input
Export ruleset as JSON — useful for programmatic manipulation
sudo nft -j list ruleset

Table and Chain Management

Create a table — inet family handles both IPv4 and IPv6
sudo nft add table inet filter
Create an input chain with default drop policy
sudo nft add chain inet filter input '{ type filter hook input priority 0; policy drop; }'
Create a forward chain
sudo nft add chain inet filter forward '{ type filter hook forward priority 0; policy drop; }'
Create an output chain — typically accept
sudo nft add chain inet filter output '{ type filter hook output priority 0; policy accept; }'
Delete a table — removes all chains and rules inside it
sudo nft delete table inet filter
Flush all rules from a chain — keeps the chain, removes rules
sudo nft flush chain inet filter input
Flush the entire ruleset — clean slate
sudo nft flush ruleset

Rule Management

Add a rule — allow SSH from trusted subnet
sudo nft add rule inet filter input tcp dport 22 ip saddr 10.50.0.0/16 accept
Insert a rule at the beginning of a chain — processed first
sudo nft insert rule inet filter input ct state established,related accept
Delete a rule by handle — get the handle from nft -a list
sudo nft delete rule inet filter input handle 15
Add a rule with a comment — self-documenting firewall
sudo nft add rule inet filter input tcp dport 443 accept comment \"allow HTTPS\"

Complete Stateful Firewall

Production-ready nftables firewall — the baseline
sudo nft flush ruleset

sudo nft -f - <<'EOF'
table inet filter {
    chain input {
        type filter hook input priority 0; policy drop;

        # Loopback
        iif "lo" accept

        # Connection tracking
        ct state established,related accept
        ct state invalid drop

        # ICMP and ICMPv6
        ip protocol icmp accept
        ip6 nexthdr icmpv6 accept

        # SSH from trusted
        tcp dport 22 ip saddr 10.50.0.0/16 accept
    }

    chain forward {
        type filter hook forward priority 0; policy drop;
    }

    chain output {
        type filter hook output priority 0; policy accept;
    }
}
EOF

Sets

Create a named set — group ports or addresses for cleaner rules
sudo nft add set inet filter allowed_ports '{ type inet_service; }'
Add elements to the set
sudo nft add element inet filter allowed_ports '{ 22, 80, 443 }'
Use the set in a rule — matches any element
sudo nft add rule inet filter input tcp dport @allowed_ports accept
Anonymous set — inline, no need to declare separately
sudo nft add rule inet filter input tcp dport { 22, 80, 443 } accept
IP address set — whitelist trusted sources
sudo nft add set inet filter trusted_hosts '{ type ipv4_addr; }'
sudo nft add element inet filter trusted_hosts '{ 10.50.1.20, 10.50.1.50, 10.50.1.60 }'
sudo nft add rule inet filter input ip saddr @trusted_hosts accept
Interval set — match entire subnets
sudo nft add set inet filter internal_nets '{ type ipv4_addr; flags interval; }'
sudo nft add element inet filter internal_nets '{ 10.50.0.0/16, 172.16.0.0/12 }'

Maps

Create a verdict map — different action per port
sudo nft add map inet filter port_policy '{ type inet_service : verdict; }'
sudo nft add element inet filter port_policy '{ 22 : accept, 80 : accept, 23 : drop }'
sudo nft add rule inet filter input tcp dport vmap @port_policy
Inline verdict map — compact syntax for simple cases
sudo nft add rule inet filter input tcp dport vmap { 22 : accept, 80 : accept, 443 : accept }

NAT

Create a NAT table and chains
sudo nft add table ip nat
sudo nft add chain ip nat prerouting '{ type nat hook prerouting priority -100; }'
sudo nft add chain ip nat postrouting '{ type nat hook postrouting priority 100; }'
Masquerade — dynamic SNAT for outbound (router pattern)
sudo nft add rule ip nat postrouting oifname "eth0" masquerade
SNAT — static source NAT
sudo nft add rule ip nat postrouting oifname "eth0" snat to 192.168.1.100
DNAT — port forwarding inbound traffic
sudo nft add rule ip nat prerouting tcp dport 8443 dnat to 10.50.1.20:443
Redirect — local port redirect (transparent proxy pattern)
sudo nft add rule ip nat prerouting tcp dport 80 redirect to :3128

Rate Limiting

Rate limit SSH connections — 3 per minute per source
sudo nft add rule inet filter input tcp dport 22 ct state new limit rate 3/minute accept
Rate limit with burst — allow short bursts before throttling
sudo nft add rule inet filter input tcp dport 22 ct state new limit rate 3/minute burst 5 packets accept
Per-source rate limiting with meters — more granular than global limit
sudo nft add rule inet filter input tcp dport 22 ct state new meter ssh-rate '{ ip saddr limit rate 3/minute }' accept

Logging

Log dropped packets with a prefix — appears in journalctl
sudo nft add rule inet filter input log prefix \"nft-dropped: \" counter drop
Log with rate limiting — prevent log floods
sudo nft add rule inet filter input limit rate 5/minute log prefix \"nft-dropped: \" counter drop
Log specific traffic without dropping — audit rule
sudo nft insert rule inet filter input tcp dport 22 log prefix \"nft-ssh: \" counter
Watch nftables log entries
journalctl -k -f | grep "nft-"

Counters

Add a named counter — track packets/bytes for specific rules
sudo nft add counter inet filter ssh_counter
sudo nft add rule inet filter input tcp dport 22 counter name ssh_counter accept
Show counter values
sudo nft list counter inet filter ssh_counter
Reset counters on all rules
sudo nft reset counters

Persistence

Save ruleset to config file — loaded at boot by nftables.service
sudo nft list ruleset > /etc/nftables.conf
Load from config file
sudo nft -f /etc/nftables.conf
Enable nftables service for boot persistence
sudo systemctl enable nftables
Validate config file syntax before applying
sudo nft -c -f /etc/nftables.conf

Migrating from iptables

Translate iptables rules to nftables — built-in migration tool
iptables-save | iptables-restore-translate -f /dev/stdin
Translate a single iptables rule
iptables-translate -A INPUT -p tcp --dport 22 -j ACCEPT
Key differences from iptables
iptables -A INPUT           → nft add rule inet filter input
iptables -I INPUT 1         → nft insert rule inet filter input
iptables -D INPUT 3         → nft delete rule inet filter input handle N
iptables -F                 → nft flush ruleset
iptables -L -n -v           → nft list ruleset
iptables -t nat             → separate table ip nat (or inet with newer kernels)
-m conntrack --ctstate      → ct state
-m multiport --dports       → tcp dport { 22, 80, 443 }
-j LOG --log-prefix         → log prefix "..."

See Also

  • iptables — legacy packet filter

  • Firewall — firewalld and general firewall management