nftables firewall basics for Linux servers
nftables is the modern replacement for iptables. Learn its table/chain model and write a clean, readable firewall ruleset for a VPS from scratch.
nftables is the modern packet-filtering framework in the Linux kernel, the official successor to iptables. It's faster, has cleaner syntax, and handles IPv4 and IPv6 in one place. If UFW feels too abstract and iptables feels dated, nftables is the sweet spot for direct control.
The model: tables, chains, rules
Three concepts:
- A table groups related rules for a protocol family (
inetcovers both IPv4 and IPv6). - A chain hooks into a point in packet processing, most importantly
input(traffic arriving at the server). - A rule matches packets and takes an action like
acceptordrop.
The strategy for a server is default-drop: reject everything on input, then explicitly accept only what you need.
Check what's there
sudo nft list ruleset
On a fresh system this may be empty or minimal.
Build a ruleset step by step
Create a table and a filtered input chain with a default drop policy:
sudo nft add table inet filter
sudo nft 'add chain inet filter input { type filter hook input priority 0 ; policy drop ; }'
Now add the essential accepts. Keep existing/related connections and loopback:
sudo nft add rule inet filter input ct state established,related accept
sudo nft add rule inet filter input iif lo accept
Allow SSH, HTTP and HTTPS:
sudo nft add rule inet filter input tcp dport 22 accept
sudo nft add rule inet filter input tcp dport { 80, 443 } accept
Allow ping (optional but handy for monitoring):
sudo nft add rule inet filter input ip protocol icmp accept
sudo nft add rule inet filter input ip6 nexthdr icmpv6 accept
The cleaner way: a config file
Adding rules one by one is fine for testing, but a config file is easier to read and reload. Write /etc/nftables.conf:
#!/usr/sbin/nft -f
flush ruleset
table inet filter {
chain input {
type filter hook input priority 0; policy drop;
ct state established,related accept
iif "lo" accept
ip protocol icmp accept
ip6 nexthdr icmpv6 accept
tcp dport 22 accept
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;
}
}
Load and enable it so it survives reboots:
sudo nft -f /etc/nftables.conf
sudo systemctl enable --now nftables
Test before you trust it
The established,related rule keeps your current SSH session alive while you apply changes, but always verify a new connection works before you disconnect. Open a second terminal and ssh in again. If it fails, use Nxeon's dashboard console to reach the server out-of-band and fix the file.
Restrict a port to one IP
To expose a database only to a trusted address:
sudo nft add rule inet filter input ip saddr 203.0.113.5 tcp dport 5432 accept
Everything else hitting 5432 is dropped by the default policy.
Rate-limit SSH
Slow down brute-force attempts:
sudo nft add rule inet filter input tcp dport 22 ct state new limit rate 4/minute accept
nftables vs UFW — which to use
UFW is perfect when you want simple allow/deny rules and don't care about the internals. nftables is the right call when you want a single readable ruleset, fine-grained matching, unified IPv4/IPv6, or you're building something more complex like NAT. Both ultimately drive the same kernel engine — pick the abstraction that fits how much control you want.