nftables

Quick Reference

# List all rules
nft list ruleset

# List specific table
nft list table inet filter

# Add a table
nft add table inet filter

# Add a chain
nft add chain inet filter input { type filter hook input priority 0 \; policy accept \; }

# Add a rule
nft add rule inet filter input tcp dport 22 accept

# Delete a rule by handle
nft delete rule inet filter input handle 10

# Save ruleset
nft list ruleset > /etc/nftables.conf

# Load ruleset
nft -f /etc/nftables.conf

# Flush all rules
nft flush ruleset

Understanding nftables

nftables vs iptables

Feature iptables nftables

Tables

Predefined (filter, nat, mangle, raw)

User-defined, flexible

Syntax

Multiple commands (iptables, ip6tables, ebtables, arptables)

Single unified command (nft)

Address families

Separate tools per family

Single tool handles all

Rules storage

Individual rules

Rulesets with transactions

Performance

Linear rule traversal

Optimized with sets and maps

Atomicity

No atomic updates

Full atomic ruleset replacement

Debugging

iptables -v counters

Built-in tracing

Architecture

                         ┌─────────────────────────────────────────┐
                         │              nftables                    │
                         └─────────────────────────────────────────┘
                                           │
           ┌───────────────────────────────┼───────────────────────────────┐
           │                               │                               │
    ┌──────▼──────┐                 ┌──────▼──────┐                 ┌──────▼──────┐
    │  Table: ip  │                 │ Table: inet │                 │ Table: ip6  │
    │  (IPv4)     │                 │ (IPv4+IPv6) │                 │   (IPv6)    │
    └─────────────┘                 └─────────────┘                 └─────────────┘
           │                               │
    ┌──────▼──────┐                 ┌──────▼──────┐
    │   Chains    │                 │   Chains    │
    │  - input    │                 │  - input    │
    │  - forward  │                 │  - forward  │
    │  - output   │                 │  - output   │
    └─────────────┘                 └─────────────┘
           │                               │
    ┌──────▼──────┐                 ┌──────▼──────┐
    │    Rules    │                 │    Rules    │
    │  - match    │                 │    Sets     │
    │  - action   │                 │    Maps     │
    └─────────────┘                 └─────────────┘

Address Families

Family Description

ip

IPv4 packets only

ip6

IPv6 packets only

inet

Both IPv4 and IPv6 (recommended)

arp

ARP packets

bridge

Bridge/layer 2 packets

netdev

Ingress hook on network device (early filtering)

Packet Flow

                                    Network
                                       │
                              ┌────────▼────────┐
                              │  prerouting     │
                              │  (raw, nat)     │
                              └────────┬────────┘
                                       │
                              ┌────────▼────────┐
                              │  Routing        │
                              │  Decision       │
                              └────────┬────────┘
                      ┌────────────────┴────────────────┐
                      │                                 │
             ┌────────▼────────┐               ┌────────▼────────┐
             │     input       │               │    forward      │
             │   (filter)      │               │   (filter)      │
             └────────┬────────┘               └────────┬────────┘
                      │                                 │
             ┌────────▼────────┐                        │
             │  Local Process  │                        │
             └────────┬────────┘                        │
                      │                                 │
             ┌────────▼────────┐                        │
             │     output      │                        │
             │   (filter)      │                        │
             └────────┬────────┘                        │
                      │                                 │
                      └────────────────┬────────────────┘
                                       │
                              ┌────────▼────────┐
                              │  postrouting   │
                              │   (nat)        │
                              └────────┬────────┘
                                       │
                                       ▼
                                    Network

Basic Configuration

Tables

# Create a table
nft add table inet filter
nft add table ip nat
nft add table ip6 myfilter

# List tables
nft list tables

# Delete a table (must be empty)
nft delete table inet filter

# Flush a table (delete all chains and rules)
nft flush table inet filter

Chains

# Base chain (hooked into network stack)
nft add chain inet filter input { type filter hook input priority 0 \; policy accept \; }

# Chain types:
#   filter - for packet filtering
#   nat    - for address translation
#   route  - for rerouting packets

# Hook points (filter):
#   prerouting  - before routing decision
#   input       - for local delivery
#   forward     - for forwarded packets
#   output      - for locally generated
#   postrouting - after routing

# Priority (lower = earlier processing):
#   raw:        -300
#   mangle:     -150
#   dstnat:     -100
#   filter:      0
#   security:   50
#   srcnat:     100

# Example chains
nft add chain inet filter input { type filter hook input priority 0 \; policy drop \; }
nft add chain inet filter forward { type filter hook forward priority 0 \; policy drop \; }
nft add chain inet filter output { type filter hook output priority 0 \; policy accept \; }

# Regular chain (no hook, called from other chains)
nft add chain inet filter tcp_chain

# Delete a chain
nft delete chain inet filter tcp_chain

# Flush a chain (delete all rules)
nft flush chain inet filter input

Rules

# Add rule (appends to chain)
nft add rule inet filter input tcp dport 22 accept

# Insert rule (prepends to chain)
nft insert rule inet filter input tcp dport 80 accept

# Add rule at specific position
nft add rule inet filter input position 5 tcp dport 443 accept

# Add rule with comment
nft add rule inet filter input tcp dport 22 accept comment \"SSH access\"

# List rules with handles
nft -a list chain inet filter input

# Delete rule by handle
nft delete rule inet filter input handle 10

# Replace rule
nft replace rule inet filter input handle 10 tcp dport 2222 accept

Rule Syntax

Matching Expressions

# Interface matching
nft add rule inet filter input iifname "eth0" accept
nft add rule inet filter output oifname "eth1" accept
nft add rule inet filter input iifname != "lo" drop

# Protocol matching
nft add rule inet filter input ip protocol tcp accept
nft add rule inet filter input ip6 nexthdr icmpv6 accept
nft add rule inet filter input meta l4proto { tcp, udp } accept

# Address matching
nft add rule inet filter input ip saddr 192.168.1.0/24 accept
nft add rule inet filter input ip daddr 10.0.0.1 drop
nft add rule inet filter input ip6 saddr 2001:db8::/32 accept

# Port matching
nft add rule inet filter input tcp dport 22 accept
nft add rule inet filter input tcp dport { 80, 443 } accept
nft add rule inet filter input tcp dport 1024-65535 accept
nft add rule inet filter input tcp sport != 80 drop

# Multiple conditions
nft add rule inet filter input ip saddr 192.168.1.0/24 tcp dport 22 accept

# Connection tracking
nft add rule inet filter input ct state established,related accept
nft add rule inet filter input ct state new tcp dport 22 accept
nft add rule inet filter input ct state invalid drop

Actions (Verdicts)

Action Description

accept

Accept the packet

drop

Silently drop the packet

reject

Drop with ICMP error reply

queue

Queue to userspace (NFQUEUE)

continue

Continue to next rule

return

Return from current chain

jump

Jump to another chain

goto

Go to another chain (no return)

log

Log the packet

# Basic actions
nft add rule inet filter input tcp dport 22 accept
nft add rule inet filter input tcp dport 23 drop

# Reject with specific ICMP type
nft add rule inet filter input reject
nft add rule inet filter input reject with icmp type host-unreachable
nft add rule inet filter input reject with icmpv6 type no-route
nft add rule inet filter input reject with tcp reset

# Jump to another chain
nft add rule inet filter input tcp dport { 80, 443 } jump web_traffic

# Logging
nft add rule inet filter input tcp dport 22 log prefix \"SSH: \" accept
nft add rule inet filter input log flags all counter drop

Connection Tracking States

State Description

new

First packet of a connection

established

Part of established connection

related

Related to existing connection (e.g., ICMP error, FTP data)

invalid

Packet doesn’t belong to any known connection

untracked

Packet explicitly untracked

# Stateful firewall rules
nft add rule inet filter input ct state invalid drop
nft add rule inet filter input ct state established,related accept
nft add rule inet filter input ct state new tcp dport 22 accept
nft add rule inet filter input ct state new tcp dport { 80, 443 } accept

Sets and Maps

Named Sets

# Create a set
nft add set inet filter allowed_ips { type ipv4_addr \; }
nft add set inet filter allowed_ports { type inet_service \; }

# Add elements to set
nft add element inet filter allowed_ips { 192.168.1.1, 192.168.1.2 }
nft add element inet filter allowed_ports { 22, 80, 443 }

# Use set in rule
nft add rule inet filter input ip saddr @allowed_ips accept
nft add rule inet filter input tcp dport @allowed_ports accept

# Delete element
nft delete element inet filter allowed_ips { 192.168.1.2 }

# Flush set
nft flush set inet filter allowed_ips

# List set
nft list set inet filter allowed_ips

Set Types

Type Description

ipv4_addr

IPv4 addresses

ipv6_addr

IPv6 addresses

ether_addr

MAC addresses

inet_proto

IP protocols

inet_service

TCP/UDP ports

mark

Packet marks

ifname

Interface names

Set Flags

# Interval set (ranges)
nft add set inet filter port_ranges { type inet_service \; flags interval \; }
nft add element inet filter port_ranges { 1024-65535 }

# Timeout set (elements expire)
nft add set inet filter recent_connections { type ipv4_addr \; timeout 1h \; }

# Dynamic set (can be updated from rules)
nft add set inet filter blocked_ips { type ipv4_addr \; flags dynamic,timeout \; timeout 24h \; }

# Counter set
nft add set inet filter counted_ips { type ipv4_addr \; flags counter \; }

Anonymous Sets

# Inline sets (no explicit creation)
nft add rule inet filter input tcp dport { 22, 80, 443, 8080 } accept
nft add rule inet filter input ip saddr { 192.168.1.0/24, 10.0.0.0/8 } accept

Maps

# Create a map
nft add map inet filter port_mark { type inet_service : mark \; }
nft add element inet filter port_mark { 22 : 1, 80 : 2, 443 : 3 }

# Verdict map
nft add map inet filter port_verdict { type inet_service : verdict \; }
nft add element inet filter port_verdict { 22 : accept, 23 : drop, 80 : accept }

# Use map in rule
nft add rule inet filter input tcp dport vmap @port_verdict
nft add rule inet filter input meta mark set tcp dport map @port_mark

NAT Configuration

Source NAT (Masquerading)

# Create NAT table
nft add table ip nat

# Create postrouting chain
nft add chain ip nat postrouting { type nat hook postrouting priority 100 \; }

# Masquerade (dynamic SNAT)
nft add rule ip nat postrouting oifname "eth0" masquerade

# Static SNAT
nft add rule ip nat postrouting oifname "eth0" snat to 203.0.113.1

# SNAT with port range
nft add rule ip nat postrouting oifname "eth0" snat to 203.0.113.1:1024-65535

Destination NAT (Port Forwarding)

# Create prerouting chain
nft add chain ip nat prerouting { type nat hook prerouting priority -100 \; }

# Port forward
nft add rule ip nat prerouting iifname "eth0" tcp dport 80 dnat to 192.168.1.100

# Port forward with port change
nft add rule ip nat prerouting iifname "eth0" tcp dport 8080 dnat to 192.168.1.100:80

# DNAT range
nft add rule ip nat prerouting tcp dport 3000-3010 dnat to 192.168.1.100

# Redirect (local port forward)
nft add rule ip nat prerouting tcp dport 80 redirect to :8080

Dual-Stack NAT (IPv4 + IPv6)

# Use inet family for both
nft add table inet nat
nft add chain inet nat prerouting { type nat hook prerouting priority -100 \; }
nft add chain inet nat postrouting { type nat hook postrouting priority 100 \; }

# IPv4 masquerade
nft add rule inet nat postrouting ip saddr 192.168.1.0/24 oifname "eth0" masquerade

# IPv6 (usually not NAT, but possible)
nft add rule inet nat postrouting ip6 saddr fd00::/64 oifname "eth0" masquerade

Rate Limiting

Basic Rate Limiting

# Limit connections per second
nft add rule inet filter input tcp dport 22 limit rate 10/second accept

# Limit with burst
nft add rule inet filter input tcp dport 22 limit rate 10/second burst 20 packets accept

# Rate units: second, minute, hour, day
nft add rule inet filter input tcp dport 25 limit rate 100/minute accept

# Byte-based limiting
nft add rule inet filter input limit rate 10 mbytes/second accept

Per-Source Rate Limiting

# Create meter for tracking
nft add rule inet filter input tcp dport 22 \
    meter ssh_ratelimit { ip saddr limit rate 3/minute } accept

# With timeout
nft add rule inet filter input tcp dport 80 \
    meter http_ratelimit { ip saddr timeout 1h limit rate 100/minute } accept

# Per-source connection limit
nft add rule inet filter input tcp dport 22 ct state new \
    meter ssh_connlimit { ip saddr ct count over 3 } drop

Logging and Monitoring

Logging Rules

# Basic logging
nft add rule inet filter input log prefix \"INPUT: \" drop

# Log with flags
nft add rule inet filter input log flags all prefix \"TRACE: \" drop
# Flags: tcp sequence, tcp options, ip options, skuid, ether, all

# Log level
nft add rule inet filter input log level info prefix \"INFO: \"
nft add rule inet filter input log level warn prefix \"WARN: \"
# Levels: emerg, alert, crit, err, warn, notice, info, debug

# Log specific traffic
nft add rule inet filter input tcp dport 22 ct state new log prefix \"SSH: \" accept

Counters

# Named counter
nft add counter inet filter http_counter
nft add rule inet filter input tcp dport 80 counter name http_counter accept

# Inline counter
nft add rule inet filter input tcp dport 443 counter accept

# View counters
nft list chain inet filter input

# Reset counter
nft reset counter inet filter http_counter

Tracing

# Enable tracing for specific traffic
nft add rule inet filter prerouting meta nftrace set 1

# View trace (requires nft monitor)
nft monitor trace

# Enable in raw table for full path
nft add table inet raw
nft add chain inet raw prerouting { type filter hook prerouting priority -300 \; }
nft add rule inet raw prerouting tcp dport 22 meta nftrace set 1

Complete Ruleset Examples

Basic Workstation Firewall

/etc/nftables.conf
#!/usr/sbin/nft -f

flush ruleset

table inet filter {
    chain input {
        type filter hook input priority 0; policy drop;

        # Accept loopback
        iifname "lo" accept

        # Accept established/related connections
        ct state established,related accept

        # Drop invalid
        ct state invalid drop

        # Accept ICMP
        ip protocol icmp accept
        ip6 nexthdr icmpv6 accept

        # Accept SSH from local network
        ip saddr 192.168.1.0/24 tcp dport 22 accept

        # Log dropped packets
        log prefix "DROPPED: " flags all
    }

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

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

Server with Services

/etc/nftables.conf
#!/usr/sbin/nft -f

flush ruleset

# Define variables
define lan_if = "eth0"
define wan_if = "eth1"
define lan_net = 192.168.1.0/24
define admin_ips = { 192.168.1.10, 192.168.1.11 }

table inet filter {
    # Allowed services
    set allowed_tcp_ports {
        type inet_service
        elements = { 22, 80, 443 }
    }

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

        # Loopback
        iifname "lo" accept

        # Established connections
        ct state established,related accept
        ct state invalid drop

        # ICMP/ICMPv6
        ip protocol icmp icmp type { echo-request, echo-reply } accept
        ip6 nexthdr icmpv6 accept

        # SSH from admin IPs only
        ip saddr $admin_ips tcp dport 22 accept

        # Web services from anywhere
        tcp dport { 80, 443 } accept

        # Rate limit new connections
        tcp flags syn limit rate 100/second burst 150 accept

        # Log and drop
        counter log prefix "INPUT DROP: "
    }

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

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

NAT Router/Gateway

/etc/nftables.conf
#!/usr/sbin/nft -f

flush ruleset

define wan_if = "eth0"
define lan_if = "eth1"
define lan_net = 192.168.1.0/24
define web_server = 192.168.1.100
define ssh_server = 192.168.1.50

table inet filter {
    chain input {
        type filter hook input priority 0; policy drop;

        iifname "lo" accept
        ct state established,related accept
        ct state invalid drop

        # Allow ICMP
        ip protocol icmp accept
        ip6 nexthdr icmpv6 accept

        # Allow SSH from LAN
        iifname $lan_if tcp dport 22 accept

        # Allow DHCP from LAN
        iifname $lan_if udp dport { 67, 68 } accept

        # Allow DNS from LAN
        iifname $lan_if udp dport 53 accept
        iifname $lan_if tcp dport 53 accept

        log prefix "INPUT DROP: "
    }

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

        ct state established,related accept
        ct state invalid drop

        # Allow LAN to WAN
        iifname $lan_if oifname $wan_if accept

        # Allow port forwards from WAN
        iifname $wan_if oifname $lan_if ip daddr $web_server tcp dport { 80, 443 } accept
        iifname $wan_if oifname $lan_if ip daddr $ssh_server tcp dport 22 accept

        log prefix "FORWARD DROP: "
    }

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

table ip nat {
    chain prerouting {
        type nat hook prerouting priority -100;

        # Port forwards
        iifname $wan_if tcp dport 80 dnat to $web_server
        iifname $wan_if tcp dport 443 dnat to $web_server
        iifname $wan_if tcp dport 2222 dnat to $ssh_server:22
    }

    chain postrouting {
        type nat hook postrouting priority 100;

        # Masquerade outgoing traffic
        oifname $wan_if masquerade
    }
}

Docker Host Firewall

/etc/nftables.conf
#!/usr/sbin/nft -f

flush ruleset

table inet filter {
    chain input {
        type filter hook input priority 0; policy drop;

        iifname "lo" accept
        ct state established,related accept
        ct state invalid drop

        ip protocol icmp accept
        ip6 nexthdr icmpv6 accept

        # SSH
        tcp dport 22 accept

        # Docker published ports (managed by Docker)
        # Don't add rules here; Docker manages its own

        log prefix "INPUT DROP: "
    }

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

        ct state established,related accept
        ct state invalid drop

        # Docker bridge networks
        iifname "docker*" accept
        oifname "docker*" accept

        # Container to internet
        iifname "docker*" oifname "eth0" accept
        iifname "eth0" oifname "docker*" ct state established,related accept

        log prefix "FORWARD DROP: "
    }

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

Service Management

Systemd Integration

# Enable nftables service
systemctl enable nftables
systemctl start nftables

# Check status
systemctl status nftables

# Reload rules
systemctl reload nftables

# Service uses /etc/nftables.conf by default

Persistence

# Save current ruleset
nft list ruleset > /etc/nftables.conf

# Test before applying
nft -c -f /etc/nftables.conf

# Apply ruleset
nft -f /etc/nftables.conf

# Atomic replace (transaction)
nft -f - << 'EOF'
flush ruleset
table inet filter {
    chain input {
        type filter hook input priority 0; policy accept;
    }
}
EOF

Backup and Restore

# Backup
nft list ruleset > /root/nftables-backup-$(date +%Y%m%d).conf

# Restore
nft flush ruleset
nft -f /root/nftables-backup-20240315.conf

# Or single command
nft -f /root/nftables-backup-20240315.conf  # flush + load if file starts with "flush ruleset"

Migration from iptables

Translation Tool

# Convert iptables rules to nftables
iptables-save > /tmp/iptables.rules
iptables-restore-translate -f /tmp/iptables.rules > /etc/nftables.conf

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

Common Conversions

iptables nftables

iptables -A INPUT

nft add rule inet filter input

iptables -I INPUT

nft insert rule inet filter input

-p tcp --dport 22

tcp dport 22

-s 192.168.1.0/24

ip saddr 192.168.1.0/24

-d 10.0.0.1

ip daddr 10.0.0.1

-i eth0

iifname "eth0"

-o eth1

oifname "eth1"

-m state --state ESTABLISHED

ct state established

-j ACCEPT

accept

-j DROP

drop

-j REJECT

reject

-j LOG --log-prefix "X: "

log prefix "X: "

-j MASQUERADE

masquerade

-j DNAT --to 192.168.1.1:80

dnat to 192.168.1.1:80

iptables Compatibility Layer

# Use iptables-nft (uses nftables backend)
update-alternatives --set iptables /usr/sbin/iptables-nft
update-alternatives --set ip6tables /usr/sbin/ip6tables-nft

# Check which backend is active
iptables -V
# Shows: iptables v1.8.x (nf_tables)

# Rules created by iptables-nft visible in nft
iptables -A INPUT -p tcp --dport 22 -j ACCEPT
nft list ruleset | grep 22

Troubleshooting

Debugging Rules

# Check syntax without applying
nft -c -f /etc/nftables.conf

# Verbose output
nft -a list ruleset    # Show handles

# List specific elements
nft list table inet filter
nft list chain inet filter input
nft list set inet filter allowed_ips

# Check counters
nft list chain inet filter input
# Look for counter values

# Enable tracing
nft add rule inet raw prerouting meta nftrace set 1
nft monitor trace

Common Issues

# Rule order matters
# BAD: drop before allow
nft add rule inet filter input drop
nft add rule inet filter input tcp dport 22 accept  # Never reached!

# GOOD: allow before drop
nft add rule inet filter input tcp dport 22 accept
nft add rule inet filter input drop

# Check chain policy
nft list chain inet filter input | grep policy
# If policy is drop, you need explicit accepts

# Verify conntrack
nft add rule inet filter input ct state established,related accept
# This should be near the top for established connections

# Check for duplicate rules
nft -a list chain inet filter input
# Look for same matches with different handles

# Interface names
# Use quotes and exact names
nft add rule inet filter input iifname "eth0" accept  # Exact match
nft add rule inet filter input iifname "eth*" accept  # Wildcard

Network Issues

# Check if nftables is blocking traffic
# Temporarily set policy to accept
nft add chain inet filter input { policy accept \; }
nft flush chain inet filter input

# Add logging to see what's being dropped
nft insert rule inet filter input log prefix "DEBUG: "

# Check counter on specific rule
nft list chain inet filter input
# Counter shows packets/bytes matched

# Monitor in real-time
watch -n1 "nft list chain inet filter input"

# Check NAT rules
nft list table ip nat
# Verify prerouting and postrouting chains

Quick Command Reference

# === Table Operations ===
nft list tables                          # List all tables
nft add table inet filter                # Create table
nft delete table inet filter             # Delete table
nft flush table inet filter              # Clear table

# === Chain Operations ===
nft list chains                          # List all chains
nft add chain inet filter input { type filter hook input priority 0 \; policy drop \; }
nft delete chain inet filter mychain     # Delete chain
nft flush chain inet filter input        # Clear chain

# === Rule Operations ===
nft add rule inet filter input tcp dport 22 accept      # Add rule
nft insert rule inet filter input tcp dport 80 accept   # Insert at beginning
nft -a list chain inet filter input                     # List with handles
nft delete rule inet filter input handle 10             # Delete by handle

# === Sets ===
nft add set inet filter myips { type ipv4_addr \; }
nft add element inet filter myips { 1.2.3.4, 5.6.7.8 }
nft delete element inet filter myips { 1.2.3.4 }
nft list set inet filter myips
nft flush set inet filter myips

# === Ruleset Operations ===
nft list ruleset                         # Show all rules
nft list ruleset > /etc/nftables.conf    # Save rules
nft -f /etc/nftables.conf                # Load rules
nft flush ruleset                        # Clear everything

# === Monitoring ===
nft monitor                              # Watch for changes
nft monitor trace                        # Packet tracing
nft -a list ruleset                      # Rules with handles

# === Service ===
systemctl enable nftables                # Enable at boot
systemctl reload nftables                # Reload rules
systemctl status nftables                # Check status

See Also