Setting Up nftables on a Linux VPS: A Practical Firewall Guide

nftables is the modern replacement for iptables on Linux, and since Debian 10, RHEL 8, and their derivatives, it is the default firewall framework shipped with the kernel. If your VPS still runs an iptables ruleset from 2015, you are maintaining two parallel rule sets — legacy iptables and the nftables backend that now powers it — without getting any of nftables’ benefits. This guide walks through a practical nftables setup for a Linux VPS: the syntax you need, a working ruleset you can adapt, and the mistakes that lock people out of their own servers.

nftables vs. iptables: What Actually Changed

The headline difference is one tool instead of four: where iptables needed separate binaries for IPv4, IPv6, ARP, and bridging, nftables handles all of them through the single nft command using an inet family table that covers both IP versions. Rules are evaluated more efficiently, and the whole ruleset is replaced atomically — there is no window where a partially applied chain leaves you exposed.

Aspectiptablesnftables
Binariesiptables, ip6tables, arptables, ebtablesSingle nft binary
IPv4 + IPv6Separate rulesets, easy to forget oneOne inet table covers both
Rule updatesPer-rule, with partial-apply riskAtomic ruleset replacement
Sets and mapsRequire separate ipset toolBuilt-in anonymous and named sets
Persistenceiptables-save/restore scriptsnft -f /etc/nftables.conf

A Minimal Working Ruleset

Install nftables and enable it on boot:

sudo apt update && sudo apt install -y nftables   # Debian/Ubuntu
sudo systemctl enable --now nftables

Then write your ruleset to /etc/nftables.conf. A sane default for a web server with SSH management looks like this:

#!/usr/sbin/nft -f
flush ruleset

table inet filter {
    chain input {
        type filter hook input priority filter; policy drop
        ct state established,related accept
        iif "lo" accept
        ip protocol icmp icmp type echo-request accept
        tcp dport 22 accept
        tcp dport 80 accept
        tcp dport 443 accept
        counter drop
    }
    chain forward {
        type filter hook forward priority filter; policy drop
    }
    chain output {
        type filter hook output priority filter; policy accept
    }
}

Apply it with sudo nft -f /etc/nftables.conf and verify with sudo nft list ruleset. The ct state established,related accept line is what lets return traffic in — omit it and your server can reach out but never receive replies, which looks exactly like a broken network.

Common Mistakes (and How to Avoid Them)

  • Locking yourself out of SSH. If you are editing remotely, apply rules from a tmux session and keep a second root shell open. Better: test with nft -c -f /etc/nftables.conf (check-only) before applying, and make sure your SSH port rule comes before any drop policy.
  • Forgetting the loopback rule. Without iif "lo" accept, local services talking to each other via 127.0.0.1 get blocked and your database or cache mysteriously “stops working” after a reboot.
  • Using iptables syntax inside nftables. -A INPUT -p tcp --dport 22 -j ACCEPT is not valid nft syntax. The nft equivalents are tcp dport 22 accept — no dashes, no -j.
  • Not persisting the ruleset. Rules applied with nft at the shell vanish on reboot unless you save them with nft list ruleset > /etc/nftables.conf (or edit the file directly and enable the service).
  • Blocking ICMP entirely. Dropping all ICMP breaks path MTU discovery and makes IPv6 connections hang. Allow echo-request and let the rest through.

Going Further: Sets and Rate Limiting

Named sets let you manage allowlists without editing rules. Define set admin_ips { type ipv4_addr; elements = { 203.0.113.10, 198.51.100.20 } } and then reference ip saddr @admin_ips accept in the input chain. For brute-force protection, add a dynamic set that drops sources with more than four new SSH connections per minute:

set ssh_bruteforce {
    type ipv4_addr
    flags dynamic
    timeout 10m
}
chain input {
    tcp dport 22 ct state new add @ssh_bruteforce { ip saddr limit rate over 4/minute } drop
}

Pair this with key-based authentication and fail2ban for defense in depth. Log dropped packets with a rule like log prefix "nft-drop: " counter drop at the end of the input chain so you can see what is being blocked in journalctl -u nftables or the kernel log.

Testing and Rolling Back Safely

Before you apply anything remotely, validate the file: sudo nft -c -f /etc/nftables.conf parses the ruleset without loading it and reports syntax errors on the exact line. If a ruleset does break connectivity, do not panic — reboot the VPS from the provider panel, and because you enabled the nftables service, the kernel reloads the last saved file. That is why the golden rule is: edit /etc/nftables.conf first, validate, then apply from that same file. If you instead type rules interactively at the shell, save them immediately with sudo nft list ruleset > /etc/nftables.conf so a reboot restores what you tested.

nftables is well worth the syntax shift: one tool, atomic updates, and better performance on the same kernel. If you are coming from iptables, the VPS feature comparison at virtualserversvps.com includes notes on which providers give you full kernel-level control for custom firewalls, and the provider list helps you find a host with a modern kernel and up-to-date base images where nftables is the default.

Ready to harden your server? Deploy a Linux VPS with a modern kernel and put this ruleset to work.

Leave a Reply