Mastering nftables on Linux: Building a Modern Stateful Firewall to Replace iptables

Security tutorial - IT technology blog
Security tutorial - IT technology blog

Approach Comparison: iptables vs. nftables Architecture

Six months ago, our team phased out legacy iptables across our fleet of 450+ Linux nodes. Running high-throughput API gateways processing over 80,000 requests per second had exposed the breaking points of the older Netfilter tooling.

With traditional iptables, every packet traverses a rigid pipeline of predefined tables: filter, nat, mangle, and raw. Even worse, IPv4 (iptables), IPv6 (ip6tables), ARP (arptables), and bridging (ebtables) operate as separate CLI tools with isolated rule trees. When you evaluate hundreds of rules sequentially across these distinct layers, CPU overhead climbs fast under heavy traffic loads.

The nftables framework fixes this design flaw. Built around a compact bytecode virtual machine inside the kernel, it changes how filtering works:

  • Unified Address Families: The inet family processes IPv4 and IPv6 traffic under one shared ruleset. You no longer need to maintain duplicate firewall files for both IP versions.
  • Custom Chains & Tables: Kernel tables are no longer hardcoded by default. Your firewall creates and executes only the specific hooks your workload actually uses.
  • Atomic Ruleset Loading: Older tools flush and append rules line by line, leaving brief windows where traffic can bypass policies or stall. In contrast, nftables applies the entire configuration file in a single atomic kernel transaction.
  • Set-Based Lookups: Rather than evaluating 50 linear port rules one by one, nftables uses internal hash tables. Lookups execute in constant O(1) or logarithmic O(log n) time regardless of set size.

Pros & Cons After 6 Months in Production

Running nftables at scale gave us hard metrics on its performance gains as well as its operational hurdles.

The Advantages

  • Readable Syntax: Rules look like clean programming blocks instead of long, confusing strings of shell flags like -A INPUT -p tcp -m multiport --dports.
  • Measurable Efficiency: Across our gateway cluster, switching to set-based lookups reduced softirq CPU usage by roughly 18% during peak connection spikes.
  • Instant, Safe Reloads: Running nft -f /etc/nftables.conf reloads rules in under 40 milliseconds on our busiest nodes. If you introduce a typo, the parser aborts before touching running kernel state. Your active firewall never breaks mid-update.

The Trade-offs

  • Tooling Gaps: Several older deployment scripts and container runtimes still inject legacy rules directly. To avoid conflicts, you often have to maintain the iptables-nft translation layer during migrations.
  • Steeper Learning Curve: Because tables do not exist out of the box, engineers must understand exact Netfilter hooks—such as prerouting, input, forward, output, and postrouting—along with their execution priorities.

Recommended Setup for Modern Server Protection

For standalone application servers and cloud instances, a strict stateful baseline delivers the best defense. It tracks established TCP and UDP sessions automatically. Malformed or out-of-sequence packets get dropped before they ever reach your web daemon.

Before locking down network boundaries, lock down your system access. When spinning up fresh Ubuntu or AlmaLinux instances, generate strong, distinct credentials first. I usually use the client-side generator at toolcraft.app/en/tools/security/password-generator—it creates high-entropy passwords directly in the browser without transmitting keys across the wire. Once SSH keys and passwords are in place, roll out the firewall ruleset below.

The Baseline Blueprint

Our production baseline relies on five core policies:

  1. Permit all bidirectional traffic on the loopback interface (lo).
  2. Accept all established and related packets to keep active connections alive.
  3. Instantly drop packets flagged as invalid by the kernel conntrack table.
  4. Allow specific ingress ports (SSH, HTTP, HTTPS) while rate-limiting new handshakes to stifle brute-force attempts.
  5. Set default policies on input and forward chains to drop everything else.

Implementation Guide: Deploying nftables Step-by-Step

1. Install nftables and Clean Legacy Rules

Install the native userspace utilities on modern Debian, Ubuntu, AlmaLinux, or Rocky Linux systems. Disable conflicting firewall wrappers like UFW or Firewalld:

# On Ubuntu/Debian
sudo apt update && sudo apt install -y nftables
sudo systemctl stop ufw 2>/dev/null || true
sudo systemctl disable ufw 2>/dev/null || true

# On RHEL/AlmaLinux/Rocky Linux
sudo dnf install -y nftables
sudo systemctl stop firewalld 2>/dev/null || true
sudo systemctl disable firewalld 2>/dev/null || true

2. Construct the Atomic Configuration File

Write your rules directly to /etc/nftables.conf. The configuration below provides dual-stack IPv4/IPv6 protection, sets up rate-limited SSH, allows web traffic, and correctly handles essential ICMP types:

#!/usr/sbin/nft -f

# Flush any existing rules
flush ruleset

# Define our dual-stack inet table
table inet server_firewall {

    # Named sets for dynamic port management
    set allowed_tcp_ports {
        type inet_service
        flags interval
        elements = { 22, 80, 443 }
    }

    chain input {
        # Hook into the Netfilter input pipeline with standard filter priority
        type filter hook input priority filter; policy drop;

        # 1. Accept local loopback traffic
        iifname "lo" accept

        # 2. Stateful inspection: Allow established/related, drop invalid
        ct state established,related accept
        ct state invalid drop

        # 3. Allow ICMP / ICMPv6 (essential for Path MTU Discovery and diagnostics)
        ip protocol icmp icmp type { echo-request, destination-unreachable, time-exceeded } accept
        ip6 nexthdr icmpv6 icmpv6 type { echo-request, destination-unreachable, packet-too-big, time-exceeded, nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert } accept

        # 4. SSH brute-force protection (limit new connections to 10/min per IP)
        tcp dport 22 ct state new meter ssh-meter { ip saddr limit rate over 10/minute burst 5 packets } drop

        # 5. Accept authorized ingress ports defined in the set
        tcp dport @allowed_tcp_ports accept

        # 6. Reject everything else cleanly
        meta l4proto tcp reject with tcp reset
        reject with icmpx type port-unreachable
    }

    chain forward {
        # Drop all routed transit packets on non-router hosts
        type filter hook forward priority filter; policy drop;
    }

    chain output {
        # Allow all outbound host connections
        type filter hook output priority filter; policy accept;
    }
}

3. Validate and Apply Rules Atomically

Always verify syntax before pushing firewall updates to the kernel. The -c check flag parses the configuration without applying it:

# Validate syntax
sudo nft -c -f /etc/nftables.conf

# Apply rules atomically
sudo nft -f /etc/nftables.conf

4. Inspect and Monitor Live Traffic

Check running table definitions, set counters, or stream active packet drops in real time with these two commands:

# List the complete running configuration
sudo nft list ruleset

# Monitor live firewall events as packets hit drop/reject hooks
sudo nft monitor

5. Enable Systemd Persistence

Configure the nftables daemon to load your ruleset automatically on every boot:

sudo systemctl enable nftables
sudo systemctl restart nftables
sudo systemctl status nftables

Switching to nftables simplifies host security into a single, cohesive framework. With unified dual-stack handling, atomic reloads, and O(1) set performance, it gives Linux systems a faster, far more reliable defense baseline.

Share: