Home / Alt manpages / nft(8)

  • nft(8)
  • Admin command
  • linux

Build and Check a Small nftables Ruleset Before Loading It

You will create a small nftables ruleset, check its syntax without changing the kernel ruleset, and then load it only when you are ready. The examples use nftables 1.0.9-1ubuntu0.1, which provides nft v1.0.9 on this machine. Allow about 15 minutes for the read-only workflow, or longer if you are applying a remote firewall.

You need a shell and the nftables package. Reading and checking a file is normally unprivileged. Listing or changing the kernel ruleset commonly needs root, so use sudo only for the specific command that needs it. Keep an existing console or out-of-band login available before changing firewall policy.

1. Confirm the installed command

Check the binary and version before relying on an example. This is a read-only command:

$ command -v nft
/usr/sbin/nft
$ nft --version
nftables v1.0.9 (Old Doc Yak #3)

The package version and the command version are related but not identical labels. Record both when troubleshooting a system that has more than one installation or a vendor backport.

Checkpoint

If nft --version fails, stop here. Do not copy a ruleset into a shell and hope another program interprets it.

2. Inspect the current ruleset

Ask nft to print what the kernel currently has. This does not alter the ruleset, although reading it may require elevated privileges:

$ sudo nft list ruleset

Save a recovery copy before making a change. The output of list ruleset is designed to be usable as input to nft -f:

$ sudo nft list ruleset > "$HOME/nftables-before-change.nft"
$ test -s "$HOME/nftables-before-change.nft" && echo "backup written"
backup written

An empty output is still meaningful: it means no nftables objects were listed. It does not prove that another firewall tool or another network namespace is inactive. Keep the backup somewhere you can reach if the new policy blocks normal access.

3. Write a deliberately narrow ruleset file

Put the policy in a file so that it can be reviewed and checked as one unit. Replace SSH_IFACE with the real interface name before using this example. The file below accepts established traffic and SSH on one interface, allows loopback, and drops the rest of the input traffic. The policy applies only to this example table.

#!/usr/sbin/nft -f

table inet example_filter {
    chain input {
        type filter hook input priority filter; policy drop;
        iifname "SSH_IFACE" accept
        iifname "lo" accept
        ct state established,related accept
    }
}

The first line is useful when the file is executed directly, but nft -f is clearer in an operational command. This is a first-load example: if the table already exists, delete that test table or adapt the file before rerunning it. The inet family covers IPv4 and IPv6. A base chain needs its type, hook and priority. The drop policy is a security-sensitive choice: the example does not allow DNS, web traffic, ping or other new inbound connections.

Do not run the file yet. Replace the placeholder and review the interface name:

$ ip -br link
$ sed -i 's/SSH_IFACE/enp1s0/' /path/to/example-filter.nft
$ sed -n '1,80p' /path/to/example-filter.nft

If your interface is not enp1s0, use the name from ip -br link. The sed command changes the file, not the live firewall. If you do not have a console or tested recovery path, do not proceed to a drop policy on a remote host.

4. Check the file without applying it

Use --check with --file. nft parses and validates the commands but does not apply their changes:

$ sudo nft --check --file /path/to/example-filter.nft
$ printf 'check status: %s\n' "$?"
check status: 0

No output and status 0 are the normal success result. A syntax error, unknown interface or unsupported expression produces an error and a non-zero status. Fix the file and rerun the complete check. A successful check is not a reachability test: it cannot prove that SSH will remain available, that the intended interface receives the traffic, or that another service is allowed.

Checkpoint

Keep the terminal showing the successful check and compare the reviewed file with the file you will load. Shell redirection, edits and environment changes can make a checked file different from the one later passed to nft.

5. Load the reviewed ruleset

Loading changes kernel state and normally needs root. This is the point of no return for the example's active policy, so confirm the interface, SSH port and recovery access first:

$ sudo nft --file /path/to/example-filter.nft

Immediately list the named table and verify the rules that were installed:

$ sudo nft list table inet example_filter
table inet example_filter {
    chain input {
        type filter hook input priority filter; policy drop;
        iifname "SSH_IFACE" accept
        iifname "lo" accept
        ct state established,related accept
    }
}

The exact formatting can vary, but the table, base chain, drop policy and three rules should be present. Test an already-open administrative session before closing it. Start a second connection if you can, then test the services you deliberately intended to permit.

6. Undo the example safely

If the example is only a temporary test, remove its table by name. This affects the example table and its contents, not unrelated nftables tables:

$ sudo nft delete table inet example_filter
$ sudo nft list table inet example_filter
Error: Could not process rule: No such file or directory

The error from the final command is expected because the table no longer exists. If the delete command reports an error, list the ruleset and inspect the exact family and table name before trying again.

To restore the state captured earlier, load the backup after checking that it is the file you intended to save:

$ sudo nft --check --file "$HOME/nftables-before-change.nft"
$ sudo nft --file "$HOME/nftables-before-change.nft"
$ sudo nft list ruleset

Restoring a backup can remove rules that were added after the backup was taken. Treat it as a deliberate replacement, not as a harmless undo button. Never use sudo nft flush ruleset as a casual cleanup command: the manual says it removes all tables and rules, leaving no packet filtering in place.

7. Diagnose the common mistakes

  • Permission denied: rerun the read or change command with sudo if your account is authorised. Do not make the ruleset file world-writable to avoid using elevation.
  • Unexpectedly blocked SSH: keep the existing session open, use the second console or out-of-band route, and restore the reviewed backup. Check the interface name, address family and destination port.
  • Check passes but traffic fails: syntax validation does not test routing, listening sockets, conntrack state or upstream filtering. Inspect the effective ruleset and service separately.
  • Nothing appears under the name you expected: tables and chains belong to address-family namespaces. The default family when one is omitted is ip, while this guide explicitly uses inet.

Done means

  • nft --version identified the installed nftables release.
  • The existing ruleset was inspected and backed up before the change.
  • The file passed sudo nft --check --file ... before loading.
  • The loaded table and its policy were verified with nft list table.
  • You know how to delete the example table or restore the reviewed backup.