nftables

nftables vs UFW vs firewalld: which for a Debian server in 2026?

August 14, 2026 · 8 min read
Illustration immersive du sujet : nftables firewall

The nftables firewall framework has become the official successor to iptables, yet many administrators still hesitate between nftables, UFW, and firewalld. Understanding which tool best fits your server architecture—and why the answer matters in 2026—can mean the difference between a resilient production environment and a brittle patchwork of conflicting rule sets. This guide dissects the technical reality behind each solution, examines their performance characteristics, and explains how to deploy them without disrupting existing Docker networks, fail2ban hooks, or kernel-level XDP filters.

Modern Linux firewall tooling has fragmented into three dominant camps: raw nftables (the kernel subsystem itself), UFW (Uncomplicated Firewall, a Python wrapper around iptables and now nftables), and firewalld (a D-Bus daemon with dynamic zone support). Each promises simplicity, yet each operates at a different abstraction layer with distinct trade-offs in performance, compatibility, and operational overhead.



What nftables, UFW, and firewalld Actually Do

At the kernel level, nftables is the single packet-filtering engine that replaced iptables, ip6tables, arptables, and ebtables. It uses a unified nft command-line interface and stores rules in tables, chains, and rules—all exposed through the Netlink API. A table groups chains by address family (inet, ip, ip6, bridge, arp, netdev), and chains hold the actual verdict logic.

UFW is a front-end that generates either iptables-legacy or nftables rules (depending on your distribution version and backend setting). It hides complexity behind high-level commands like ufw allow 22/tcp, which expand into multiple lower-level rules. UFW maintains its own chain structure and integrates with application profiles stored in /etc/ufw/applications.d/.

Firewalld is a daemon that listens on D-Bus and dynamically modifies nftables (or iptables-legacy on older systems). It organizes rules into zones (public, trusted, dmz, etc.), services, and rich rules. Changes take effect immediately without reloading the entire ruleset, and firewalld persists configuration in XML files under /etc/firewalld/.

Performance and Rule Evaluation

Nftables evaluates packets using a bytecode virtual machine inside the kernel. Rules compile into nftables bytecode, which executes faster than the linear iptables traversal because nftables supports:

  • Verdict maps: hash-table lookups for IP sets, port ranges, and protocol-specific decisions.
  • Concatenations: matching multiple criteria (IP + port) in a single map lookup.
  • No redundant counter increments: counters are optional, reducing per-packet overhead.

UFW and firewalld inherit nftables performance when using the nftables backend, but they inject wrapper chains that can add minor overhead. For example, UFW inserts ufw-before-input, ufw-user-input, and ufw-after-input chains, each evaluated sequentially. Firewalld similarly uses filter_IN_public, filter_IN_public_allow, and similar auto-generated chains.

In high-throughput environments (game servers, media streaming, API gateways), the difference between raw nftables and a wrapper becomes measurable. A single monolithic nftables ruleset in an inet pakkt table can process packets in sub-microsecond time, while wrapper chains introduce additional jumps and chain traversals.



Isolation, Conflict, and the Docker Problem

One of the most painful issues in production firewall management is rule collision. Docker manipulates iptables (or nftables via iptables-nft translation) to configure container networking, inserting DOCKER, DOCKER-USER, and DOCKER-ISOLATION chains. Fail2ban adds dynamic ban rules. Custom automation scripts may inject rate-limits or GeoIP blocks. When all these tools fight for control of the same chains, the result is unpredictable rule ordering, silent overwrites, and service outages.

Nftables solves this by allowing multiple independent tables. Each application can maintain its own table without touching others. For example:

table inet pakkt {
  chain input {
    type filter hook input priority filter; policy accept;
    tcp dport 25565 ct state new limit rate 50/second accept
    tcp dport 25565 drop
  }
}

This inet pakkt table coexists peacefully with Docker's inet filter table, fail2ban's rules in a separate table, and any legacy iptables-nft translations. UFW and firewalld, by contrast, typically operate within a single global namespace, requiring manual coordination to avoid conflicts.

Stateful Firewall Features: Connection Tracking and Rate-Limiting

All three solutions leverage the kernel's conntrack module to track TCP/UDP connection state. Nftables exposes this via the ct expression:

ct state established,related accept
ct state invalid drop

UFW abstracts this into a single ufw default deny incoming directive, which implicitly allows established/related. Firewalld offers zone-level trust settings that enforce similar stateful policies. Under the hood, all three invoke the same conntrack logic, so stateful performance is identical.

For rate-limiting, nftables provides two mechanisms:

  • limit: simple token-bucket per rule (limit rate 10/second).
  • meter: dynamic per-source or per-connection limits, e.g., meter http_ratelimit { ip saddr limit rate 5/second } accept.

UFW supports limit only through manual insertion of raw nftables rules. Firewalld exposes rate-limits via rich rules:

firewall-cmd --permanent --zone=public --add-rich-rule='rule family="ipv4" source address="0.0.0.0/0" service name="ssh" limit value="3/m" accept'

However, rich-rule syntax is verbose and error-prone compared to direct nftables expressions.

Complementarity with XDP and Kernel-Level Filtering

Nftables operates at the Netfilter layer (after L2 parsing but before the network stack). For extreme packet rates—multi-million PPS DDoS—Netfilter becomes a bottleneck because every packet must traverse the kernel's network stack. This is where XDP (eXpress Data Path) shines: XDP programs run in the network driver's receive path, before socket buffers are allocated, enabling sub-microsecond per-packet decisions.

PAKKT.io deploys a single XDP program per interface (the PAKKT Engine) that handles up to 256 simultaneous rules via BPF maps. Each rule can specify port range, protocol (TCP/UDP/ICMP/any), action (block / rate_limit / allow_only), global max_pps, max_port_pps, and min/max packet size. Because XDP is stateless, it cannot track TCP connection state or enforce byte-rate limits—those remain the domain of nftables.

The optimal architecture pairs XDP for high-volume filtering (dropping malformed packets, known attack signatures, volumetric floods) with a stateful nftables firewall for connection tracking, per-connection rate-limits, and TCP flag validation. PAKKT provisions an isolated inet pakkt table that never conflicts with Docker, UFW, firewalld, or iptables-persistent, ensuring zero disruption to existing infrastructure.



nftables Firewall: Practical Deployment and Rule Management

Deploying raw nftables requires understanding its syntax and persistence mechanisms. On Debian/Ubuntu, install via:

apt install nftables
systemctl enable nftables.service

The default ruleset is stored in /etc/nftables.conf. A minimal secure baseline looks like:

#!/usr/sbin/nft -f

flush ruleset

table inet filter {
  chain input {
    type filter hook input priority filter; policy drop;
    
    # Accept loopback
    iif "lo" accept
    
    # Accept established/related
    ct state established,related accept
    ct state invalid drop
    
    # ICMP essentials
    ip protocol icmp icmp type { echo-request, destination-unreachable, time-exceeded } limit rate 5/second accept
    ip6 nexthdr icmpv6 icmpv6 type { echo-request, destination-unreachable, time-exceeded, nd-neighbor-solicit, nd-neighbor-advert } limit rate 5/second accept
    
    # SSH with rate-limit
    tcp dport 22 ct state new limit rate 3/minute accept
    
    # Drop everything else (policy drop)
  }
  
  chain forward {
    type filter hook forward priority filter; policy drop;
  }
  
  chain output {
    type filter hook output priority filter; policy accept;
  }
}

Apply with nft -f /etc/nftables.conf. Changes are atomic: the entire ruleset loads or rolls back, preventing partial application.

IP Blacklists and Whitelists

Nftables sets offer efficient bulk IP management. Create a named set:

table inet filter {
  set blacklist {
    type ipv4_addr
    flags interval
  }
  
  chain input {
    type filter hook input priority filter; policy drop;
    ip saddr @blacklist drop
    # ... rest of rules
  }
}

Populate dynamically:

nft add element inet filter blacklist { 192.0.2.50, 198.51.100.0/24 }

PAKKT synchronizes IP blacklists and whitelists across both the XDP BPF map (for pre-stack filtering) and the nftables set (for stateful enforcement), ensuring consistent policy at both layers. The dual-layer approach stops attacks at the earliest possible point while maintaining connection-aware rules for legitimate traffic.

Monitoring and Debugging

List active rules:

nft list ruleset

Monitor real-time packet matching:

nft monitor

Add counters to specific rules:

tcp dport 25565 counter drop

Query counters:

nft list chain inet filter input

For performance metrics, combine nftables counters with PAKKT Integrations, which aggregate per-port and per-rule statistics into a TimescaleDB time-series database, rendering them on a real-time dashboard with GeoIP enrichment (world map, top source IPs, top countries).



UFW and firewalld: When to Use Them

UFW excels in desktop and single-service environments where simplicity trumps performance. Its command-line interface is intuitive:

ufw allow 22/tcp
ufw allow from 203.0.113.0/24 to any port 3306
ufw enable

However, UFW's abstraction layer makes it difficult to implement complex policies like:

  • Per-connection byte-rate limits.
  • Protocol-specific logic (matching TCP flags, ICMP types).
  • Verdict maps for large IP/port combinations.

Firewalld suits Red Hat / CentOS ecosystems and environments requiring dynamic zone changes (laptops switching networks, servers with multiple NICs in different trust zones). Its zone abstraction simplifies role-based policies:

firewall-cmd --zone=public --add-service=http --permanent
firewall-cmd --zone=trusted --add-source=10.0.0.0/8 --permanent
firewall-cmd --reload

But firewalld's XML configuration and D-Bus dependency add operational complexity. Troubleshooting requires understanding both the high-level zone model and the underlying nftables ruleset it generates.

Migration Path to Raw nftables

For production servers handling high packet rates, migrating to raw nftables eliminates wrapper overhead and unifies rule management. Export your current UFW or firewalld rules, translate them to nftables syntax, and test in a staging environment. A typical migration checklist:

  • Audit existing rules: ufw status verbose or firewall-cmd --list-all-zones.
  • Map services to ports (e.g., sshtcp dport 22).
  • Convert IP allow/deny lists to nftables sets.
  • Implement connection tracking explicitly (ct state established,related accept).
  • Test with a non-disruptive policy (policy accept) and counters before switching to policy drop.

Once migrated, disable UFW or firewalld to prevent conflicts. PAKKT's isolated inet pakkt table approach means you can run PAKKT's XDP + nftables stack alongside your custom nftables table without interference, as each table operates independently within the kernel's Netfilter hooks.



Conclusion

The nftables firewall emerges as the clear technical winner in 2026: it delivers superior performance through bytecode execution, eliminates rule conflicts via table isolation, and provides granular control over connection tracking, rate-limiting, and protocol-specific logic. UFW and firewalld remain useful for low-complexity scenarios, but production environments benefit from raw nftables paired with kernel-level XDP filtering. This dual-layer architecture—stateless XDP for volumetric defense, stateful nftables for connection tracking—offers the best balance of speed, flexibility, and operational stability.



FAQ

Can I run nftables, UFW, and firewalld simultaneously on the same server?

Technically yes, because nftables supports multiple independent tables, but it creates severe operational risk. UFW and firewalld both attempt to manage the default filter table, leading to race conditions and unpredictable rule ordering. Best practice is to choose one front-end (UFW or firewalld) or adopt raw nftables exclusively. If you need coexistence—for example, Docker + custom firewall—use isolated tables like inet pakkt for your application rules while leaving Docker's auto-generated table untouched.

Does nftables support byte-rate limiting in addition to packet-rate limiting?

Yes. Nftables limit and meter expressions accept both rate (packets/second) and burst (bytes) parameters. For example, limit rate over 10 mbytes/second burst 5 mbytes drop enforces a byte-rate ceiling. This is a stateful Netfilter feature; XDP programs like PAKKT Engine remain stateless and cannot track byte counts across packets, so byte-rate limits must be implemented in nftables, not XDP.

How do I verify that my nftables rules are actually being evaluated before Docker or fail2ban rules?

Use nft list tables to enumerate all active tables, then nft list table inet <tablename> for each. Rules are evaluated by hook priority: lower (more negative) priorities run first. The default filter priority is 0; Docker and fail2ban typically use priority 0 as well, so rule ordering within the same priority is table creation order. To guarantee precedence, assign your custom table a lower priority, e.g., type filter hook input priority -10. Add counters to suspect chains and observe which increment first under traffic to confirm evaluation order.

Protect your servers

Deploy PAKKT in 30 seconds

Dual-layer kernel protection. XDP + nftables. Driven from a central panel. 7-day free trial.