How to Configure a Firewall with nftables
A complete, working nftables ruleset for a typical server — default-deny inbound, stateful connection tracking, and a handful of explicit allowed services.
nftables is the modern successor to iptables on Linux, unifying what used to be separate iptables/ip6tables/arptables/ebtables tools into a single framework with its own more readable rule syntax. This sets up a complete, working server firewall from scratch.
Step 1: confirm nftables is available and check current state
nft list ruleset
On most current distributions, nftables is already the default backend, sometimes with an empty ruleset ready for you to populate.
Step 2: create the base ruleset structure
Rather than adding rules one at a time interactively, write a complete ruleset file — this is both more maintainable and avoids ever having an inconsistent, partially-applied firewall state:
# /etc/nftables.conf
#!/usr/sbin/nft -f
flush ruleset
table inet filter {
chain input {
type filter hook input priority 0; policy drop;
# Allow loopback
iif lo accept
# Allow established/related connections
ct state established,related accept
# Drop invalid packets outright
ct state invalid drop
# Allow ICMP (ping) — useful for diagnostics
ip protocol icmp accept
ip6 nexthdr icmpv6 accept
# Allow SSH
tcp dport 22 accept
# Allow HTTP/HTTPS
tcp dport { 80, 443 } accept
}
chain forward {
type filter hook forward priority 0; policy drop;
}
chain output {
type filter hook output priority 0; policy accept;
}
}
Step 3: understand the structure before applying it
policy drop on the input chain means anything not explicitly allowed is rejected by default — the safe, standard posture for a server. The ct state established,related accept rule is what makes this usable at all: it lets return traffic for connections you initiated (or that match an already-allowed inbound connection) through, without needing a matching rule for every possible response packet.
Step 4: apply and enable
nft -f /etc/nftables.conf
systemctl enable --now nftables
Step 5: verify the ruleset loaded as expected
nft list ruleset
Compare the output against your file to confirm nothing was silently rejected due to a syntax issue — nft -f will report line-specific errors if the file has a problem, but always worth a final visual confirmation.
Step 6: test each rule deliberately, from another machine
# from a separate machine
ssh youruser@yourserver # should work — port 22 explicitly allowed
curl http://yourserver # should work — port 80 allowed
nc -zv yourserver 3306 # should fail/timeout — not in the allow list
Testing from a separate machine matters — testing from the server itself against its own loopback address doesn’t exercise the actual input chain rules the same way genuine external traffic does.
Step 7: add a new allowed service later
To add a new service (say, a custom application on port 8080), edit the file and reapply rather than adding an ad hoc rule interactively — keeping the file as the single source of truth avoids configuration drift between what’s actually loaded and what’s documented:
tcp dport 8080 accept
nft -f /etc/nftables.conf
A safety note before you disconnect
If you’re configuring this over an SSH session, keep that session open while testing from a second connection attempt — a mistake in the ruleset (a missing SSH allow rule, a policy typo) can lock you out immediately, and having your original session still connected is often the only way back in without console/out-of-band access.
Why nftables over legacy iptables
Beyond the unified syntax, nftables rulesets are applied atomically (the whole file loads as one transaction, avoiding the brief inconsistent states possible when adding iptables rules one command at a time) and the rule syntax itself — named chains, structured { } blocks, readable set syntax like { 80, 443 } — is considerably easier to review and maintain as a checked-in configuration file than an equivalent sequence of iptables -A commands.