Test an nftables Ruleset Before You Load It
One bad line in an nftables ruleset can lock you out of a box you are nowhere near, so nft gives you a way to check first and load second. This walkthrough builds a small ruleset, validates it without touching the kernel, then loads it only once you are ready. The examples use nftables 1.0.9-1ubuntu0.1, which provides nft v1.0.9 on this machine. Budget about 15 minutes for the read-only steps, longer if you are applying a remote firewall.
The route
Jump straight to the step you need, or tick off Done means at the end.
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 save sudo for the command that actually needs it. Keep an existing console or out-of-band login open before you touch firewall policy.
1. Confirm the installed command
Check the binary and its version before you trust any example, this one included. It is read-only:
$ 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 labels, not identical ones. Record both when you are troubleshooting a system with more than one install or a vendor backport.
Checkpoint
If nft --version fails, stop here. Do not paste a ruleset into a shell and hope another program understands it.
2. Inspect the current ruleset
Ask nft to print what the kernel already has. This does not change anything, though reading it may need elevated privileges:
$ sudo nft list ruleset
Save a recovery copy before making any change. list ruleset output is designed to be fed straight back in 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 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 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 shebang line is only useful when the file is executed directly;
nft -fis 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
inetfamily covers IPv4 and IPv6. A base chain needs its own type, hook and priority. - The drop policy is security-sensitive. This example does not allow DNS, web traffic, ping or any other new inbound connection.
Do not run the file yet. Replace the placeholder and review the interface name first:
$ 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 ip -br link gave you. The sed command changes the file, not the live firewall. If you do not have a console or a tested recovery path, do not push a drop policy to a remote host.
4. Check the file without applying it
Use --check with --file. nft parses and validates the commands but does not apply the changes:
$ sudo nft --check --file /path/to/example-filter.nft
$ printf 'check status: %s\n' "$?"
check status: 0
No output and status 0 is 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 whole check. A successful check is not a reachability test: it cannot prove SSH will stay available, that the intended interface sees the traffic, or that another service is allowed through.
Checkpoint
Keep the terminal showing the successful check open, and compare the reviewed file against the one you are about to load. Shell redirection, edits and environment changes can make a checked file different from the one nft actually loads.
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, the SSH port and your recovery access first:
$ sudo nft --file /path/to/example-filter.nft
Immediately list the named table and verify what was actually 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
}
}
Exact formatting can vary, but the table, base chain, drop policy and three rules should all be present. Test an already-open administrative session before you close it. Open a second connection if you can, then test the services you deliberately meant 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 nothing else:
$ 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, the table no longer exists. If the delete itself errors, list the ruleset and check the exact family and table name before trying again.
To restore the state you captured earlier, load the backup after confirming it is the file you meant 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 added after the backup was taken, so treat it as a deliberate replacement, not a harmless undo button. Warning: never use sudo nft flush ruleset as a casual cleanup command. The manual is explicit that it removes all tables and rules, leaving no packet filtering in place at all.
7. Diagnose the common mistakes
- Permission denied. Rerun the read or change command with
sudoif your account is authorised. Do not make the ruleset file world-writable just to dodge elevation. - SSH gets blocked unexpectedly. Keep the existing session open, use a 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 still fails. Syntax validation does not test routing, listening sockets, conntrack state or upstream filtering. Inspect the effective ruleset and the service separately.
- Nothing shows up 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 deliberately usesinet.
Done means
- Version confirmed:
nft --versionidentified the installed nftables release. - Backup taken: the existing ruleset was inspected and saved before the change.
- Checked before loaded: the file passed
sudo nft --check --file ...before it went anywhere near the kernel. - Verified after loading: the loaded table and its policy were confirmed with
nft list table. - Recovery path known: you know how to delete the example table or restore the reviewed backup.