iptables & nftables Cheatsheet

nftables

Use this iptables & nftables reference while you build software engineering projects, review code, or refresh the syntax you reach for most.

Why nftables replaced iptables

Concerniptablesnftables
Commandsiptables, ip6tables, arptables, ebtablesone, nft
IPv4 and IPv6two separate rulesetsone inet family covers both
Multiple criteriaone rule per port, per addressnative sets and maps in a single rule
Updating rulesrule by rule, non-atomicnft -f replaces a whole ruleset atomically
Built-in chainsfixed, always present, always evaluatedyou create only the chains you need
Counterson every rule, alwaysopt in per rule
nft --version
sudo nft list ruleset                  # the whole thing, including iptables-nft rules
sudo nft list tables
sudo nft list table inet filter
sudo nft -f /etc/nftables.conf         # load a file, atomically
sudo nft -c -f /etc/nftables.conf      # check syntax without loading
sudo nft flush ruleset                 # remove everything

Families, tables, and chains

sudo nft add table inet filter
sudo nft add chain inet filter input \
  '{ type filter hook input priority 0 ; policy drop ; }'
sudo nft add rule inet filter input iif lo accept
sudo nft add rule inet filter input ct state established,related accept
sudo nft add rule inet filter input tcp dport 22 accept
FamilyCovers
ipIPv4 only
ip6IPv6 only
inetboth, one ruleset, the usual choice
arpARP, replaces arptables
bridgebridged frames, replaces ebtables
netdevearliest possible hook, per interface, for very cheap drops
HookEquivalent iptables chain
preroutingPREROUTING
inputINPUT
forwardFORWARD
outputOUTPUT
postroutingPOSTROUTING
ingressearlier than prerouting, netdev and inet
Priority nameValueReplaces
raw-300the raw table
mangle-150the mangle table
dstnat-100nat PREROUTING
filter0the filter table
security50the security table
srcnat100nat POSTROUTING

Chain types are filter, nat, and route. A NAT chain must use the nat type and a dstnat or srcnat priority, and unlike iptables there is no shared table you have to share with everyone else's rules.

Translating from iptables

iptables-translate prints the nftables equivalent of a command without running anything, which is the fastest way to learn the syntax.

iptables-translate -A INPUT -p tcp --dport 22 -j ACCEPT
# nft add rule ip filter INPUT tcp dport 22 counter accept

iptables-restore-translate -f /etc/iptables/rules.v4 > /etc/nftables.conf
iptablesnftables
-p tcp --dport 22tcp dport 22
-s 10.0.0.0/8ip saddr 10.0.0.0/8
-d 192.168.1.5ip daddr 192.168.1.5
-i eth0iif eth0 (or iifname "eth0" for a name match)
-o eth0oif eth0
-m conntrack --ctstate NEWct state new
-m multiport --dports 80,443tcp dport { 80, 443 }
-j ACCEPT / DROPaccept / drop
-j REJECT --reject-with tcp-resetreject with tcp reset
-j LOG --log-prefix "x: "log prefix "x: "
-j SNAT --to-source 203.0.113.1snat to 203.0.113.1
-j MASQUERADEmasquerade
-j DNAT --to-destination 10.0.0.5:80dnat to 10.0.0.5:80
-m limit --limit 5/minlimit rate 5/minute
-m comment --comment "x"comment "x"
! -s 10.0.0.0/8ip saddr != 10.0.0.0/8

Note iif matches the interface index and iifname matches the name. An index is faster but breaks if the interface is recreated, so use iifname for anything that can come and go, such as a VPN or container interface.

A complete host ruleset

#!/usr/sbin/nft -f
flush ruleset

table inet filter {
  set admin_hosts {
    type ipv4_addr
    flags interval
    elements = { 10.0.0.0/24, 192.0.2.10 }
  }

  chain input {
    type filter hook input priority filter ; policy drop ;

    iif lo accept
    ct state established,related accept
    ct state invalid drop

    ip protocol icmp icmp type { echo-request, destination-unreachable, \
      time-exceeded } limit rate 5/second accept
    ip6 nexthdr icmpv6 accept

    tcp dport 22 ip saddr @admin_hosts accept comment "ssh, admin nets only"
    tcp dport { 80, 443 } accept comment "public web"

    limit rate 5/minute log prefix "nft-drop: " level info
    reject with icmpx type admin-prohibited
  }

  chain forward { type filter hook forward priority filter ; policy drop ; }
  chain output  { type filter hook output  priority filter ; policy accept ; }
}
sudo nft -c -f /etc/nftables.conf && sudo nft -f /etc/nftables.conf
sudo systemctl enable --now nftables

Do not drop all of ICMPv6. IPv6 depends on neighbour discovery and router advertisements, so a blanket ip6 nexthdr icmpv6 drop breaks address resolution and the host loses connectivity on that protocol entirely.

NAT and forwarding in nftables

table inet nat {
  chain prerouting {
    type nat hook prerouting priority dstnat ; policy accept ;
    iifname "wan0" tcp dport 443 dnat ip to 10.0.0.20:443
    iifname "wan0" tcp dport 8080 redirect to :80
  }
  chain postrouting {
    type nat hook postrouting priority srcnat ; policy accept ;
    oifname "wan0" ip saddr 10.0.0.0/24 masquerade
    oifname "wan0" ip saddr 10.0.1.0/24 snat to 203.0.113.10
  }
}
sudo sysctl -w net.ipv4.ip_forward=1
sudo nft list table inet nat

The same rule as iptables applies: the forward chain sees the post-DNAT destination, so filter on the internal address.

Sets, maps, and counters

Sets are the feature that shortens real rulesets the most. One lookup replaces hundreds of rules.

# a named set, with entry expiry
sudo nft add set inet filter blocklist \
  '{ type ipv4_addr ; flags interval, timeout ; timeout 1h ; }'
sudo nft add element inet filter blocklist '{ 203.0.113.0/24, 198.51.100.7 }'
sudo nft add rule inet filter input ip saddr @blocklist drop
sudo nft list set inet filter blocklist
sudo nft delete element inet filter blocklist '{ 198.51.100.7 }'
# a map: port to backend, one rule instead of one per service
table ip nat {
  map port_to_host {
    type inet_service : ipv4_addr
    elements = { 80 : 10.0.0.21, 443 : 10.0.0.22, 25 : 10.0.0.23 }
  }
  chain prerouting {
    type nat hook prerouting priority dstnat ;
    iifname "wan0" dnat ip to tcp dport map @port_to_host
  }
}
# counters are opt in, and can be named and shared
sudo nft add counter inet filter ssh_hits
sudo nft add rule inet filter input tcp dport 22 counter name ssh_hits accept
sudo nft list counters
sudo nft reset counters
sudo nft list ruleset -a          # show handles, needed to delete a single rule
sudo nft delete rule inet filter input handle 12
sudo nft monitor trace            # live rule tracing, after marking with meta nftrace
FeatureWhat it buys
Named setupdate the list without touching the rules
flags intervalthe set may hold CIDR prefixes and ranges
flags timeoutentries expire on their own, useful for temporary blocks
Mapa lookup that also produces a value, such as a NAT target
Verdict mapjump to different chains based on a match, no rule chain needed
Named countershared across rules, survives rule edits
-a and handlesthe only reliable way to delete one rule