nftables is the successor to iptables, offering a more flexible and efficient way to manage your firewall rules.

Let’s see it in action. Imagine you want to allow SSH traffic from a specific IP address.

nft add rule ip filter input ip saddr 192.168.1.100 tcp dport 22 accept

This command adds a rule to the input chain in the filter table for IPv4. It specifies that if a packet has a source IP address of 192.168.1.100 and is using TCP on port 22, it should be accepted.

The core of nftables is its tables, chains, and rules. Tables are containers for rulesets, and they can be indexed by address family (e.g., ip for IPv4, ip6 for IPv6, inet for both). Chains are ordered lists of rules within a table, and they dictate the path of packets. Rules are the actual instructions that match packets and specify actions.

Here’s how you’d set up a basic filtering table:

nft add table ip filter
nft add chain ip filter input { type filter hook input priority 0 \; }
nft add chain ip filter forward { type filter hook forward priority 0 \; }
nft add chain ip filter output { type filter hook output priority 0 \; }
nft add rule ip filter input tcp dport 22 accept
nft add rule ip filter input tcp dport 80 accept
nft add rule ip filter input tcp dport 443 accept
nft add rule ip filter input ct state established,related accept
nft add rule ip filter input counter drop

This creates an ip table named filter and adds three common chains: input, forward, and output, hooking them into the appropriate network stack points. It then allows SSH, HTTP, and HTTPS traffic, accepts established and related connections (crucial for return traffic), and finally drops everything else with a counter so you can see what’s being dropped.

The priority value on chains determines the order in which chains are processed. Lower numbers mean earlier execution. For example, priority -10 would run before priority 0.

One of the most powerful features is nftables’ ability to use sets and maps. Sets allow you to group multiple values for a single match, and maps allow you to assign different actions or values based on specific matches.

Consider blocking a range of IPs:

nft add set ip blocked_ips { type ipv4_addr\; }
nft add rule ip filter input ip saddr @blocked_ips drop
nft add element ip blocked_ips { 192.168.1.101, 192.168.1.102 }

Here, we create a set named blocked_ips to hold IPv4 addresses. Then, we add a rule to the input chain that drops any packet whose source IP is found in the blocked_ips set. Finally, we add two specific IPs to that set. This is far more efficient than adding individual ip saddr ... drop rules for each IP.

When you want to inspect your rules, nft list ruleset is your go-to command. For a more verbose output that includes chain statistics and set contents, use nft -a list ruleset.

The inet family is a convenient way to manage both IPv4 and IPv6 rules simultaneously. A rule added to an inet table will apply to both families if the packet matches.

nft add table inet filter
nft add chain inet filter input { type filter hook input priority 0 \; }
nft add rule inet filter input tcp dport 22 accept

This creates a single filter table that handles both IPv4 and IPv6 traffic for the input hook, and the rule allowing SSH will apply to both.

The ct state expression is incredibly useful. ct stands for connection tracking, and this expression allows you to match packets based on their state within the connection tracking system. established,related is a common state to allow, ensuring that return traffic for legitimate outgoing connections is permitted.

When dealing with complex configurations, especially those involving many sets or intricate rule logic, the nft -f <filename> command is invaluable. It allows you to load an entire ruleset from a file, making it easy to manage, version, and deploy your firewall configurations.

The counter statement in a rule is not just for visibility; it’s a fundamental part of packet processing. When a packet matches a rule with counter, the packet is processed, its associated counter is incremented, and then the packet continues to the next rule. If the counter rule is the last one for a specific path and is followed by a drop or reject action, it effectively logs and then discards traffic that would otherwise be silently dropped.

You’ll often see nft add rule ip filter input tcp dport 22 accept comment "Allow SSH" or similar. The comment keyword is purely for human readability within the nft list ruleset output and has no impact on packet processing. It’s a good practice for documenting complex rules.

The most surprising thing is how much performance can be gained by using sets and maps over individual rules. Instead of the firewall iterating through dozens or hundreds of identical rules, it performs a single set lookup.

Understanding the priority values for hooks is key to advanced nftables configuration. Priorities range from -4095 to 4095, and different hooks have different default priority ranges. For example, the filter hook typically uses priorities around 0, while mangle might use lower (higher priority) numbers.

The next concept you’ll likely grapple with is NAT (Network Address Translation) and how to implement it using nftables’ nat table and its associated prerouting and postrouting chains.

Want structured learning?

Take the full Nftables course →