nftables — The Modern Linux Firewall with nft

Practical guide to nftables — the modern iptables successor. Tables, chains, rules, sets and NAT with the nft syntax, plus hardened firewall setups.

nftables is the unified firewall of the Linux kernel, replacing the legacy tools iptables, ip6tables, arptables, and ebtables under a single command: nft. Instead of four separate toolsets, you manage IPv4, IPv6, ARP, and bridge traffic with one consistent syntax of tables, chains, rules, sets, and maps. Rule changes are applied atomically, and named sets keep large address lists fast and maintainable. This guide takes you from listing existing rules through match expressions and NAT to a complete, hardened firewall setup.

Listing Rules

nft list ruleset — List all rules across all tables and families.

sudo nft list ruleset

nft list tables — List all tables.

sudo nft list tables

nft list table FAMILY TABLE — List all chains and rules in a specific table.

sudo nft list table inet filter

nft list chain FAMILY TABLE CHAIN — List rules in a specific chain.

sudo nft list chain inet filter input

nft -a list ruleset — List all rules with handle numbers (needed for deleting).

sudo nft -a list ruleset

nft -s list ruleset — List rules without string translation (numeric protocols/ports).

sudo nft -s list ruleset

nft -j list ruleset — List rules in JSON format.

sudo nft -j list ruleset

Address Families

ip — IPv4 only (equivalent to iptables).

ip6 — IPv6 only (equivalent to ip6tables).

inet — Both IPv4 and IPv6 in one table. Recommended for most use cases.

arp — ARP protocol (equivalent to arptables).

bridge — Bridge/layer 2 filtering (equivalent to ebtables).

netdev — Ingress filtering at the device level (before routing).

Table Management

nft add table FAMILY NAME — Create a new table.

sudo nft add table inet filter

nft delete table FAMILY NAME — Delete a table and all its chains/rules.

sudo nft delete table inet filter

nft flush table FAMILY NAME — Remove all rules from a table (keep chains).

sudo nft flush table inet filter

nft flush ruleset — Remove all tables, chains, and rules. Caution: with remote access and a default policy drop, this locks you out — allow SSH/established first and test locally only.

sudo nft flush ruleset

Chain Management

nft add chain FAMILY TABLE CHAIN '{ type TYPE hook HOOK priority PRIO; policy POLICY; }' — Create a base chain with hook, priority, and policy. Be careful with policy drop before adding accept rules (e.g. for SSH/established) – see the hardened setup below.

sudo nft add chain inet filter input '{ type filter hook input priority 0; policy drop; }'

nft add chain FAMILY TABLE CHAIN — Create a regular (non-base) chain for organizing rules.

sudo nft add chain inet filter tcp_chain

nft delete chain FAMILY TABLE CHAIN — Delete a chain (must be empty).

sudo nft delete chain inet filter tcp_chain

nft flush chain FAMILY TABLE CHAIN — Remove all rules from a chain.

sudo nft flush chain inet filter input

Adding Rules

nft add rule FAMILY TABLE CHAIN STATEMENT — Append a rule to the end of a chain.

sudo nft add rule inet filter input tcp dport 80 accept

nft insert rule FAMILY TABLE CHAIN STATEMENT — Insert a rule at the beginning of a chain.

sudo nft insert rule inet filter input tcp dport 443 accept

nft add rule FAMILY TABLE CHAIN position HANDLE STATEMENT — Add a rule after a specific handle.

sudo nft add rule inet filter input position 8 tcp dport 8080 accept

nft insert rule FAMILY TABLE CHAIN position HANDLE STATEMENT — Insert a rule before a specific handle.

sudo nft insert rule inet filter input position 8 tcp dport 22 accept

Deleting Rules

nft delete rule FAMILY TABLE CHAIN handle HANDLE — Delete a rule by handle number (use -a to find handles).

sudo nft delete rule inet filter input handle 12

nft flush chain FAMILY TABLE CHAIN — Delete all rules in a chain.

sudo nft flush chain inet filter input

Match Expressions

tcp dport PORT — Match TCP destination port.

sudo nft add rule inet filter input tcp dport 22 accept

udp dport PORT — Match UDP destination port.

sudo nft add rule inet filter input udp dport 53 accept

tcp dport { P1, P2, P3 } — Match multiple ports using anonymous sets.

sudo nft add rule inet filter input tcp dport { 80, 443, 8080 } accept

tcp dport P1-P2 — Match a port range.

sudo nft add rule inet filter input tcp dport 8000-9000 accept

ip saddr ADDRESS — Match source IPv4 address.

sudo nft add rule inet filter input ip saddr 192.168.1.0/24 accept

ip daddr ADDRESS — Match destination IPv4 address.

sudo nft add rule inet filter output ip daddr 10.0.0.0/8 drop

ip6 saddr ADDRESS — Match source IPv6 address.

sudo nft add rule inet filter input ip6 saddr fd00::/8 accept

ip saddr { A1, A2, A3 } — Match multiple source addresses.

sudo nft add rule inet filter input ip saddr { 10.0.0.1, 10.0.0.2 } accept

iifname INTERFACE — Match incoming interface name.

sudo nft add rule inet filter input iifname "eth0" accept

oifname INTERFACE — Match outgoing interface name.

sudo nft add rule inet filter output oifname "lo" accept

meta l4proto icmp — Match ICMP protocol.

sudo nft add rule inet filter input meta l4proto icmp accept

ct state { established, related } — Match connection tracking state.

sudo nft add rule inet filter input ct state { established, related } accept

ct state invalid — Match invalid connection state.

sudo nft add rule inet filter input ct state invalid drop

ct state new — Match new connections.

sudo nft add rule inet filter input ct state new tcp dport 22 accept

Actions (Verdicts)

accept — Allow the packet.

drop — Silently discard the packet.

reject — Reject the packet with an ICMP error.

reject with TYPE — Reject with a specific message type.

sudo nft add rule inet filter input tcp dport 23 reject with tcp reset

log prefix "TEXT" — Log the packet with a prefix.

sudo nft add rule inet filter input tcp dport 22 log prefix "SSH: " accept

counter — Count packets and bytes matching this rule.

sudo nft add rule inet filter input tcp dport 80 counter accept

jump CHAIN — Jump to another chain for further processing.

sudo nft add rule inet filter input tcp dport { 80, 443 } jump web_chain

return — Return from the current chain.

limit rate N/TIME — Rate limit matches (e.g., 5/minute, 1/second).

sudo nft add rule inet filter input icmp type echo-request limit rate 1/second accept

NAT

nft add chain ip nat postrouting '{ type nat hook postrouting priority 100; }' — Create a NAT postrouting chain.

sudo nft add chain ip nat postrouting '{ type nat hook postrouting priority 100; }'

masquerade — Masquerade outgoing traffic (dynamic SNAT).

sudo nft add rule ip nat postrouting oifname "eth0" masquerade

dnat to ADDRESS:PORT — Destination NAT (port forwarding).

sudo nft add rule ip nat prerouting tcp dport 8080 dnat to 192.168.1.100:80

snat to ADDRESS — Source NAT with a static IP.

sudo nft add rule ip nat postrouting ip saddr 10.0.0.0/8 snat to 203.0.113.1

redirect to :PORT — Redirect to a local port.

sudo nft add rule ip nat prerouting tcp dport 80 redirect to :8080

Named Sets

nft add set FAMILY TABLE NAME '{ type TYPE; }' — Create a named set.

sudo nft add set inet filter blocked_ips '{ type ipv4_addr; }'

nft add element FAMILY TABLE SET '{ ELEM1, ELEM2 }' — Add elements to a set.

sudo nft add element inet filter blocked_ips '{ 10.0.0.1, 10.0.0.2 }'

nft delete element FAMILY TABLE SET '{ ELEM }' — Remove an element from a set.

sudo nft delete element inet filter blocked_ips '{ 10.0.0.1 }'

nft list set FAMILY TABLE SET — Show contents of a named set.

sudo nft list set inet filter blocked_ips

ip saddr @SET_NAME drop — Use a named set in a rule.

sudo nft add rule inet filter input ip saddr @blocked_ips drop

Save & Restore

nft list ruleset > FILE — Save the current ruleset to a file. Without /etc/nftables.conf plus the enabled nftables service, rules are not persistent and are lost on reboot.

sudo nft list ruleset > /etc/nftables.conf

nft -f FILE — Load rules from a file.

sudo nft -f /etc/nftables.conf

nft -c -f FILE — Check/validate a ruleset file without applying it.

sudo nft -c -f /etc/nftables.conf

Common Firewall Setup

nft add table inet filter — Step 1: Create a filter table for IPv4 and IPv6.

sudo nft add table inet filter

nft add chain inet filter input '{ type filter hook input priority 0; policy drop; }' — Step 2: Create input chain with drop policy. Caution: from now on everything without a matching rule is dropped — add the SSH/established rules (steps 4 and 6) first, or you will lock yourself out over remote access.

sudo nft add chain inet filter input '{ type filter hook input priority 0; policy drop; }'

nft add rule inet filter input iifname "lo" accept — Step 3: Allow loopback traffic.

sudo nft add rule inet filter input iifname "lo" accept

nft add rule inet filter input ct state { established, related } accept — Step 4: Allow established connections.

sudo nft add rule inet filter input ct state { established, related } accept

nft add rule inet filter input ct state invalid drop — Step 5: Drop invalid packets.

sudo nft add rule inet filter input ct state invalid drop

nft add rule inet filter input tcp dport { 22, 80, 443 } accept — Step 6: Allow SSH, HTTP, HTTPS.

sudo nft add rule inet filter input tcp dport { 22, 80, 443 } accept

nft add rule inet filter input icmp type echo-request accept — Step 7: Allow ping.

sudo nft add rule inet filter input icmp type echo-request accept

Conclusion

With nftables you manage IPv4, IPv6, and bridge filtering in a single, consistent framework instead of four separate legacy tools. Draft new rulesets in an /etc/nftables.conf, validate them with nft -c -f, and enable the nftables service so they survive a reboot. And always test a default policy drop locally first: on a remote server, a forgotten SSH accept quickly costs you access.

Further Reading

  • age – modern file encryption on the command line
  • clamav – open-source virus scanner for Linux servers
  • fail2ban – bans attacker IPs based on log patterns