nftables Firewall on Linux VPS — Complete Setup Guide

September 2026 · 8 min read

nftables Firewall on Linux VPS

nftables is the modern Linux firewall framework that replaces iptables, ip6tables, arptables, and ebtables — all in a single unified tool. Available since kernel 3.13, nftables is now the default firewall backend in Debian 11+, Ubuntu 20.10+, and RHEL 8+. Its cleaner syntax, better performance for large rulesets, and support for atomic updates make it the right choice for managing firewall rules on a VPS.

Why Switch to nftables

Here's how nftables compares to iptables:

Featureiptablesnftables
IPv4/IPv6Separate tools (iptables / ip6tables)Handled in one tool
SyntaxVerbose, many flagsCleaner, more readable
Atomic updatesNot supported (flush + reload one by one)Supported (entire ruleset loaded at once)
Sets/MapsRequires separate ipset toolBuilt-in, multiple types
PerformanceLinear matchHash/map lookup — faster for many rules

Installing nftables

On Debian/Ubuntu, nftables is usually pre-installed. If not:

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

# RHEL / CentOS / AlmaLinux / Rocky Linux
sudo dnf install -y nftables

# Enable and start the nftables service
sudo systemctl enable --now nftables

# Check installed version
nft --version
Tip: If you are currently using UFW or firewalld, stop and disable them before managing nftables directly to avoid conflicting rules.

Core Concepts

nftables organizes rules in a hierarchy:

Creating a Basic VPS Ruleset

Open the main nftables configuration file:

sudo nano /etc/nftables.conf

Replace the entire contents with the following ruleset:

#!/usr/sbin/nft -f

# Flush all existing rules
flush ruleset

# inet table covers both IPv4 and IPv6
table inet filter {

  # Dynamic blocklist — entries expire after 1 hour
  set blocklist {
    type ipv4_addr
    flags dynamic, timeout
    timeout 1h
  }

  chain input {
    type filter hook input priority 0; policy drop;

    # Allow loopback interface
    iif lo accept

    # Allow established and related connections
    ct state established,related accept

    # Drop invalid packets
    ct state invalid drop

    # Allow ICMP (ping)
    ip protocol icmp accept
    ip6 nexthdr icmpv6 accept

    # Allow SSH
    tcp dport 22 accept

    # Allow HTTP and HTTPS
    tcp dport { 80, 443 } accept

    # Drop IPs in the blocklist
    ip saddr @blocklist drop

    # Log and drop everything else
    log prefix "[nftables-drop] " drop
  }

  chain forward {
    type filter hook forward priority 0; policy drop;
  }

  chain output {
    type filter hook output priority 0; policy accept;
  }
}
# Load the configuration
sudo nft -f /etc/nftables.conf

# Verify the active ruleset
sudo nft list ruleset

Adding and Removing Rules at Runtime

You can modify rules while the system is running without editing the config file:

# Allow an additional port (e.g. 8080)
sudo nft add rule inet filter input tcp dport 8080 accept

# Block a specific IP address
sudo nft add rule inet filter input ip saddr 1.2.3.4 drop

# Add an IP to the blocklist set
sudo nft add element inet filter blocklist { 203.0.113.10 }

# List all chains
sudo nft list chains

# Show rules in the input chain with handle numbers
sudo nft list chain inet filter input

# Delete a rule by its handle number (e.g. handle 8)
sudo nft delete rule inet filter input handle 8
Tip: Runtime changes are not persistent. Save them with sudo nft list ruleset > /etc/nftables.conf to survive a reboot.

Rate Limiting SSH to Prevent Brute Force

# Add before the "tcp dport 22 accept" rule
# Allow max 3 new SSH connections per minute per IP
tcp dport 22 ct state new limit rate 3/minute accept
tcp dport 22 ct state new drop

Or create a dedicated guard chain:

chain ssh_guard {
  ct state new limit rate over 5/minute drop
  accept
}

# In the input chain
tcp dport 22 jump ssh_guard

Saving and Backing Up Configuration

# Save current ruleset to config file
sudo nft list ruleset | sudo tee /etc/nftables.conf

# Backup before making changes
sudo cp /etc/nftables.conf /etc/nftables.conf.bak.$(date +%Y%m%d)

# Reload config atomically (no downtime)
sudo nft -f /etc/nftables.conf

# Validate syntax without applying
sudo nft -c -f /etc/nftables.conf

Verifying nftables Is Working Correctly

# Check service status
sudo systemctl status nftables

# List the full active ruleset
sudo nft list ruleset

# Show packet/byte counters per rule
sudo nft list ruleset -a

# Confirm rules in the input chain
sudo nft list chain inet filter input

# Test from an external host
nc -zv YOUR_VPS_IP 22    # should open
nc -zv YOUR_VPS_IP 8888  # should be refused/timeout
Tip: Run sudo nft monitor to watch rule changes in real time. This is invaluable during debug sessions — you can see exactly which events trigger which rules as they happen.

Troubleshooting Common Issues

Error: "Could not process rule: Table already exists"

This happens when loading a config file that doesn't include flush ruleset at the top, or when a table with the same name already exists from a previous load.

# Fix 1: Add flush ruleset as the first line of your config
flush ruleset

# Fix 2: Manually flush before loading
sudo nft flush ruleset
sudo nft -f /etc/nftables.conf

Error: "syntax error, unexpected identifier" in config file

Caused by missing semicolons, unmatched braces, or wrong keywords in the config file.

# Validate syntax without applying
sudo nft -c -f /etc/nftables.conf

# The error message includes the line number and position
# Fix the issue in the config file, then reload

SSH session drops immediately after applying new rules

Occurs when the default policy is drop and no rule explicitly allows SSH, or the SSH allow rule appears after a catch-all drop rule.

# Always validate before applying
sudo nft -c -f /tmp/new-rules.conf

# Use at(1) for automatic rollback if the session is lost
echo "nft flush ruleset && nft -f /etc/nftables.conf.bak" | at now + 5 minutes
sudo nft -f /tmp/new-rules.conf

Error: "Operation not permitted" without sudo

Modifying kernel firewall tables requires root privileges. Always prefix nft commands with sudo.

sudo nft list ruleset
sudo nft add rule ...

Rules disappear after reboot

The nftables service is not enabled, or the ruleset was not saved to the correct config file path.

# Check whether the service is enabled
sudo systemctl is-enabled nftables

# Enable it if not already done
sudo systemctl enable nftables

# Save the current ruleset to the main config file
sudo nft list ruleset | sudo tee /etc/nftables.conf

# Test the service restart
sudo systemctl restart nftables
sudo nft list ruleset

Frequently Asked Questions

Can nftables and iptables run at the same time?

Running both simultaneously is not recommended. Both use the Linux Netfilter framework, and their rules can conflict or duplicate each other in unpredictable ways. If you are migrating from iptables, use iptables-restore-translate to convert your existing rules to nftables syntax, then disable iptables entirely before switching over.

Can UFW use nftables as its backend?

On newer distributions, UFW can use nftables as its backend. However, managing nftables directly gives you far greater flexibility — native sets, maps, and complex stateful rules that UFW cannot express. UFW is best for simple use cases; direct nftables management is better when you need full control over the firewall behavior.

How do I see which rule is dropping a specific packet?

Add a log statement before the drop verdict. Logs appear in the kernel ring buffer and can be viewed with journalctl or dmesg.

# Add logging before drop
log prefix "[nft-drop] " flags all drop

# View logged packets
sudo journalctl -k | grep nft-drop
sudo dmesg | grep nft-drop