Security

Get started with nftables: tables, chains and rules

Learn how nftables organises tables, chains and rules, build an inet filter table with a default-drop input chain that allows SSH and web traffic, and save it to nftables.conf.

IntermediateLinux server5 min read

nftables is the modern Linux packet filter and the framework that UFW, firewalld and today's iptables command all write to. Using it directly gives you one readable config file that covers IPv4 and IPv6 together. This guide explains the building blocks, then builds a small, safe server firewall: drop everything incoming except SSH, HTTP and HTTPS.

Before you start

  • A Linux VPS running Ubuntu 22.04 or 24.04, Debian 12, or AlmaLinux or Rocky Linux 8 or 9.
  • Root or a sudo user.
  • No other firewall manager active. If UFW or firewalld is running, use that tool instead, or disable it first. See UFW vs firewalld vs nftables vs iptables.
  • Your SSH port. It is 22 unless you changed it.
  • Access to the browser console in your VPS provider's control panel.

Install the nft tool if it is missing:

Bash
# Ubuntu and Debian
sudo apt install nftables
# AlmaLinux and Rocky Linux
sudo dnf install nftables

Warning: A default-drop input chain blocks everything you do not allow. Always allow your SSH port in the same ruleset that sets the drop policy. If you get locked out, log in through the browser console in your VPS provider's control panel and run sudo nft flush ruleset to remove all rules.

Tables, chains and rules

  • A table is a container. Its family decides what traffic it sees: ip (IPv4), ip6 (IPv6), or inet (both). Use inet for a server firewall.
  • A chain holds rules. A base chain is attached to a kernel hook such as input, forward or output, and has a policy (accept or drop) for packets that match no rule.
  • A rule has match expressions and a verdict, for example tcp dport 22 accept. Rules are checked top to bottom and the first verdict wins.

See what is loaded right now:

Bash
sudo nft list ruleset

An empty output means no rules at all. If you see tables named firewalld, ufw or docker, another tool is managing the firewall.

Try commands interactively

You can build rules one command at a time. These are live immediately but not saved:

Bash
sudo nft add table inet demo
sudo nft add chain inet demo input '{ type filter hook input priority 0; policy accept; }'
sudo nft add rule inet demo input tcp dport 8080 counter accept
sudo nft -a list table inet demo

The -a flag shows each rule's handle, which you need to delete it:

Bash
sudo nft delete rule inet demo input handle 3
sudo nft delete table inet demo

The policy here is accept, so experimenting cannot lock you out. For a real firewall, a config file is easier to read and review.

Write the ruleset file

Create the file. On Ubuntu and Debian it is /etc/nftables.conf. On AlmaLinux and Rocky Linux, the service loads /etc/sysconfig/nftables.conf, which can include files from /etc/nftables/. Put the same content in that file instead.

#!/usr/sbin/nft -f

flush ruleset

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

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

        meta l4proto { icmp, ipv6-icmp } accept

        tcp dport 22 accept comment "SSH"
        tcp dport { 80, 443 } accept comment "Web"
    }

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

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

What each part does:

  • flush ruleset clears old rules so loading the file twice does not stack duplicates. It also removes rules created by other tools, such as Docker.
  • established,related lets replies to your own connections back in. Without it, even apt update breaks.
  • lo is the loopback interface. Local services talk to each other through it.
  • ICMP and ICMPv6 keep ping, path MTU discovery and IPv6 neighbour discovery working. IPv6 does not work properly without ICMPv6.
  • Change 22 if your SSH runs on another port.
  • forward drops routed traffic. Docker, VPNs and NAT gateways need forwarding rules, so adjust this chain if you run them.

Test and load it

Check the syntax without applying anything:

Bash
sudo nft -c -f /etc/nftables.conf

No output means the file is valid. For extra safety, schedule an automatic flush in case the new rules cut you off, as described in Test firewall changes with an automatic rollback:

Bash
sudo systemd-run --on-active=5m --unit=nft-rollback /usr/sbin/nft flush ruleset

Now load the ruleset:

Bash
sudo nft -f /etc/nftables.conf

Open a new SSH session. If it connects, cancel the rollback:

Bash
sudo systemctl stop nft-rollback.timer

Load it at boot

Enable the nftables service so the file is applied on every boot:

Bash
sudo systemctl enable --now nftables

On AlmaLinux and Rocky Linux, make sure firewalld is disabled first, or the two will conflict.

Check it works

Bash
sudo nft list ruleset
systemctl is-enabled nftables

From another computer, test an allowed and a blocked port:

Bash
nc -vz 203.0.113.10 22
nc -vz -w 3 203.0.113.10 3306

Replace 203.0.113.10 with your server's IP. Port 22 should connect. Port 3306 should time out, even if MySQL is running.

Troubleshooting

  • "Could not process rule: No such file or directory": you referenced a table or chain that does not exist yet. Create it first, or use the file method.
  • Updates and DNS fail after loading: the established,related rule is missing or placed after a drop. Keep it near the top.
  • Rules gone after reboot: the nftables service is not enabled, or you edited the wrong file for your distro.
  • Docker containers lost network: flush ruleset and the forward drop policy removed Docker's rules. Restart Docker after loading, and allow forwarding for its bridges.
  • Locked out: use the browser console, run sudo nft flush ruleset, fix the SSH port in the file and reload.

Next steps

Was this guide helpful?

Still stuck?

Ask MdmHost AI for a quick answer, or open a ticket and our team will take a look. We are here 24/7.