Beginners Guide to nftables Traffic Filtering

Traffic filtering with nftables

Many Linux administrators became familiar with iptables and ip6tables. Less familiar are tools like arptables and ebtables. Meet the successor of them all: nftables, a packet filtering framework, with the goal to replace all the previous ones. After reading this guide you will be able to configure your own firewall configuration. Step by step we will show how nftables work. Although no knowledge of iptables is needed, we will share some differences with iptables where applicable. This way we can avoid inefficient rules.

Introduction: Netfilter

Before we start with this guide info nftables, it is good to know about netfilter. Both iptables and nftables use the netfilter components in the Linux kernel. This explains also the first two letters from this new traffic filtering solution.

One of the flaws in iptables is the slightly cryptic way of expressing which information flows are allowed. For that reason, the nftables syntax is shorter and easier to understand. Instead of saying “-p tcp”, it simplifies it to just “tcp”. This syntax is also very similar to what tcpdump is using. This is no surprise, as the project was inspired by the elegant syntax of tcpdump. If you know pf from operating systems FreeBSD or OpenBSD, you may also like the new syntax.

Suggested read: differences between iptables and nftables explained


For this beginners guide, we assume that you have a recent Linux kernel. Support for nftables should also be compiled into the kernel, together with the related nftables modules. To be sure, check if you have the nf_tables kernel module available. and if the nft binary is installed.

Kernel Modules

To determine if you have the nf_tables kernel module, use the modinfo command.

modinfo nf_tables

The output will look something like this:

Screenshot of modinfo utility showing the nf_tables kernel module

Check if nf_tables module is available on your system.

Use lsmod to show any active nftables kernel module.

lsmod | grep nf_tables

This should at least reveal the nf_tables modules: nf_tables_inet, nf_tables_ip, nf_tables_ip6. On newer versions, this might be nf_tables_ipv4 and nf_tables_ipv6.

Output showing that nftables kernel modules are active

Disable iptables

It is possible to mix iptables and nftables. However, this increases complexity and also the chance to introduce errors. So keep it simple and flush out all iptables rules, and make sure it is not loaded.

iptables -F

Do the same for IPv6:

ip6tables -F

Ensure that during system reboots the iptables configuration or modules are no longer loaded.

Kernel and client

We already have seen the active kernel modules in the sections before. Here is a big difference with iptables. The intelligence of the rulesets has been moved to the client utility nft. This utility parses the rules and compiles them into a language that the kernel module understands. This way the kernel receives an optimized set of instructions. Another benefit is that you can manipulate the rules, instead of having to flush the configuration with every new change.

Configuration of nftables

Before showing examples, it is good to know some basic rules regarding the configuration syntax.

The hash sign (#) is used for comments, similar to your shell. To combine several commands, use the semicolon (;) sign. To split an instruction into several lines, use a backslash (\) at the end of the line. Then continue with your nft command on the next one.

So in short:

  • # = comment
  • ; = more commands or parameters to follow
  • \ = break a rule into multiple lines

Tip: when a statement includes a semicolon, you can tell your shell to ignore it, by adding a backslash.


Repetition is bad. To simplify things, nftables supports variables. Instead of repeating an interface multiple times, you define it at the beginning of your configuration file. After that, you will be using the variable.

Example: defining interfaces
define ext_if = eth0
define int_if = eth1
define all_if = { $ext_if, $int_if }


Within the configuration of nftables, a table is at the top of the ruleset. It consists of chains, which are containers for rules. Overview: Tables –> Chains –> Rules.

The maximum length of a table name is 27 characters. At this moment you can create a table (add), delete it (delete), display it (list) or empty it (flush).

Address Families

All objects within nftables have a so called namespace, which includes the address family. This address family specifies what kind of hooks will be applied for further analysis of the information stream. For example this can be ip for IPv4 traffic, or ip6 for IPv6 traffic. As nftables is aware of the ongoing usage of IPv6, it simplifies usage for both protocol families. It does so by combining them both within the inet address family.

For filtering arp traffic, we previously used arptables. With nftables that kind of network traffic belongs to the arp address family.

If you have configured a bridged interface, you may want to use bridge (previously ebtables). Then there is netdev, which is used for ingress filtering, or traffic coming into the system. It allows for early filtering traffic, before it reaches other filters (below layer 3 on OSI model).


  • arp
  • bridge
  • inet (= ip + ip6)
  • ip
  • ip6
  • netdev

As you can see, the names are as short as possible. You can combine rulesets for IPv4 and IPv6 traffic with the inet address family. Netdev is one of the latest additions and allows filtering before it is


After creating a table, the next step is to create chains. Chains are containers holding rules, and are of a defined type. Chains can be 1 of the two types: base or non-base. Being a base type chain, it has a related hook in the kernel. With a hook, the related chain can “see” the traffic, otherwise it can’t.

nft add chain ip traffic-filter output { type filter hook output priority 0 \; policy accept\; }

Chain types: base, non-base

Hook: input, output


The basic building blocks of rule in nftables consists of the following components:

  • expression(s)
  • operator
  • action

The expressions within a rule are evaluated from left to right. When the first expression matches, it continues with the other parts. If the expression does not result in a positive outcome, the next rule in line will be evaluated.


nft add rule Firewall Incoming ip daddr drop

For example, tcp dport 22 accept

Advanced Configuration 


A set is a collection of data elements. This could be for example filled with IPv4 addresses, or port numbers. Maximum length of a set name is 15 characters. If you exceed this is, an error will follow:

Could not process rule: Numerical result out of range
Anonymous Sets

dport { 22, 23, 80, 443 }

Usage: Directly used in rules

Named Sets

nft add set inet blacklist blacklist4-perm { type ipv4_addr \; }
nft add element inet blacklist blacklist4-perm {, }
nft add rule inet blacklist input ip saddr @blacklist4-perm drop

Usage: Filled with data, then referenced in a rule


A map is used to do a mapping. You use one field, to look up the value of another, and act on that.

Examples needed


Another type is the dictionary (or verdict maps). They use the structure of a set and are a powerful component within nftables, as they can include the verdict.

nft add rule ip Firewall Forward ip daddr vmap {\ : jump chain-dmz, \ : jump chain-ssn1, \ : jump chain:ssn2, \ : jump chain-desktops \

Traffic Hooks

Each type of traffic has one or more possible traffic hooks. They can be used to make more specific filters.


iifname = Incoming interface
oifname = Outgoing interface


Basic syntax
<protocol> <dport/sport> <port> <action>

dport/sport: destination port or source port. For example SSH running on our system, would indicate port 22 as destination for incoming traffic. So in this case: tcp dport 22

Outgoing traffic to another server, would be outgoing traffic to the SSH daemon on the target, which would be dport as well.

oifname lo accept

icmp type {echo-reply} drop
icmp accept
udp sport bootpc dport bootps accept
ip daddr tcp dport {http, postgresql, ipp} accept
udp dport dns accept
tcp dport {dns, http, ntp, https, 9418} accept

Creating Tables, Chains and Rules

Next step is creating tables.

nft add table inet incoming-traffic

Note: if you don’t specify inet, the ip address family will be used by default

Within a table, we then create a chain. A chain is a container of one or more rules and used for the organization of the rule. In other words, it is a ruleset.

Now we have created our table, we add an input chain.

nft list table inet incoming-traffic
table inet incoming-traffic {
        chain input {

nft add chain inet incoming-traffic management

nft add rule inet incoming-traffic management tcp dport 22

nft add chain inet incoming-traffic web-traffic

nft add rule inet incoming-traffic web-traffic tcp dport 80 counter

nft add rule inet incoming-traffic web-traffic tcp dport 443 counter

Best Practices for nftables

Use clear names

Like in the world of software development, you have to use self-declaring names for your tables. Some examples use the name “filter”, which is confusing on what it is doing specifically.

Frequently Asked Questions

How can I see all tables for IPv4 and IPv6?

Use the inet address family when using both IPv4 (ip) and IPv6 (ip6).

nft list tables inet

Why do I get an error when trying to show an existing table?

You might have forgotten to specify the address family. Use nft list tables first.

How can I see the rule numbers within each table and/or chain?

nft list table inet filter-traffic -a

How can I export my rules and backup them?

Use nft export xml or nft export json

Common Mistakes

Like most firewall types, it is easy to make mistakes. We have collected a few common mistakes, so you can avoid them early on.

Loading rules without flushing

When loading rules from a file, flush them first. Or better, make a backup first by exporting them. Then flush the rules and import them.

Screenshot of double firewall rules in nftables

Double firewall rules in nftables

Splitting IPv4 and IPv6

nftables has a great facility to combine traffic for IPv4 (ip) and IPv6 (ip6), named “inet”. This way you can enable incoming traffic for your web server on port 80, for both protocol families.

Making rules too complicated

Like in life, more rules result in more complexity. Keep things as simple as possible. Most likely you are not the only one who has to understand your firewall rules, so building rules needs some attention. Another important step is documenting specific rules which are not obvious.

Forgetting the protocol family

When requesting the list of active tables, the result set might seem to be empty. By default, the nft utility will use the ip protocol family. For example, when using the inet family, this will result in no entries listed.


Invalid table

<cmdline>:1:1-12: Error: Could not process rule: Table 'x' does not exist

If you try to list a non-existing table, you will receive this error. Show the tables with list tables.

nft list tables

Other useful resources

Here are some other resources to use

  • nftables wiki
  • Quick reference guide
One more thing...

Keep learning

So you are interested in Linux security? Join the Linux Security Expert training program, a practical and lab-based training ground. For those who want to become (or stay) a Linux security expert.

See training package

Lynis Enterprise screenshot to help with system hardeningSecurity scanning with Lynis and Lynis Enterprise

Run automated security scans and increase your defenses. Lynis is an open source security tool to perform in-depth audits. It helps with system hardening, vulnerability discovery, and compliance.



  • GreenGreen

    Best guide I’ve found in over an hour of searching. Thank you for this great guide and the time you put into it.

  • ZorgZorg

    Good article. Would it be possible to base rules on originating binaries. Similar to Subgraph’s application firewall –

  • M.A. PerryM.A. Perry

    Thanks for writing a very interestingand useful article. I do have two questions though.

    You say that the iptables should be disabled.
    Is it also pertinent to ultimately purge ip_tables and also iptables-persistent?
    And should the directory /etc/iptables/ and its contents (rules.v4 and rules.v6) be deleted?

  • Andy Van PeltAndy Van Pelt

    Some typos and mis-leading things:

    “The maximum length of a table is 27 characters.” I believe you meant the maximum length of a table *name*.

    The sentence after Traffic Hooks is “Each type of traffic has one or more possible traffic hooks. They can be used to make more specific filters, or”. It looks to me like the sentence ends in mid-thought.

    The example for creating a chain has (I believe) the command line shown with a “#” as the first character. I stared at that several times until I understood (I think) that it’s *not* a comment, but is instead the command line. You should make it clearer if so.

    The Loading Rules Without Flushing example is confusing. The listed code doesn’t seem to have much to do with the topic. Maybe it’s really bloody obvious what the connection is, but anybody (like me) who isn’t already fluent with iftables probably won’t get it either.

  • anacondaanaconda

    More tutorials needed.
    How to define CIDR Ip range and allow only inet traffic only from those ?


Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.