Quick start: Set up a basic firewall with nftables in 5 minutes
Managing Linux firewalls used to be a hassle when maintaining two separate rule sets: iptables for IPv4 and ip6tables for IPv6. With nftables, Linux consolidates everything into a unified framework. You get cleaner syntax, atomic rule updates with zero downtime, and significantly higher packet-processing performance.
Step 1: Install nftables and disable legacy firewalls
Before enabling nftables, disable higher-level firewall services to avoid netfilter hook conflicts in the Linux kernel:
# On Ubuntu / Debian
sudo apt update && sudo apt install -y nftables
sudo systemctl stop ufw && sudo systemctl disable ufw
# On RHEL / AlmaLinux / Rocky Linux
sudo dnf install -y nftables
sudo systemctl stop firewalld && sudo systemctl disable firewalld
Step 2: Load a minimal ruleset to protect your server
The default configuration file is located at /etc/nftables.conf. Open it in a text editor:
sudo nano /etc/nftables.conf
Delete the existing contents and paste in the standard ruleset below. This configuration maintains active SSH sessions, opens standard web ports, and drops all remaining unauthorized traffic:
#!/usr/sbin/nft -f
flush ruleset
table inet my_firewall {
chain my_input {
type filter hook input priority filter; policy drop;
# Allow local loopback traffic
iif "lo" accept
# Keep active connections established (prevents dropping current SSH session)
ct state established,related accept
ct state invalid drop
# Allow ICMP ping for both IPv4 and IPv6
ip protocol icmp accept
ip6 nexthdr ipv6-icmp accept
# Open service ports: SSH (22), HTTP (80), and HTTPS (443)
tcp dport 22 accept
tcp dport { 80, 443 } accept
}
}
Step 3: Verify syntax and start the service
Don’t rush into starting the service right away. A single typo in your configuration file could lock you out of SSH permanently.
# 1. Check for syntax errors without applying rules (dry-run)
sudo nft -c -f /etc/nftables.conf
# 2. Start and enable service on system boot
sudo systemctl enable --now nftables
# 3. Verify active ruleset in the kernel
sudo nft list ruleset
With just a single inet table, your system is fully protected across both network protocols without duplicating rules.
Core architecture and syntax of nftables
The nftables model is organized into a clean hierarchical tree: Table → Chain → Rule.
1. Tables and Address Families
Unlike iptables, nftables does not come with predefined tables like filter or nat. You explicitly create tables and assign appropriate address families:
- inet: Supports a dual-stack mix of IPv4 and IPv6 in the same table. This is the recommended choice for most internet-facing servers.
- ip / ip6: Handles IPv4 or IPv6 packets exclusively.
- netdev: Supports the
ingresshook, allowing you to filter or drop malicious traffic right at the network driver before it reaches the TCP/IP stack—ideal for DDoS mitigation. - bridge / arp: Used for Layer 2 bridging and handling ARP/MAC address resolution tables.
2. Understanding Regular Chains vs. Base Chains
Chains organize and group packet-filtering rules. nftables distinguishes between two types:
- Base chain: Hooks directly into the Linux kernel network stack using the
hookkeyword (such asinput,output, orforward). This type of chain requires setting an executionpriorityand a defaultpolicy(acceptordrop). - Regular chain: Has no system hooks attached. It acts like a helper function that other rules can
jumpto, making complex configurations cleaner and modular.
3. Rule Syntax: Read naturally from left to right
nftables syntax completely eliminates the cryptic, abbreviated flags of iptables. Rules are expressed intuitively using the pattern: [match criteria] [action].
# Match multiple ports in a single rule
tcp dport { 80, 443, 8080, 8443 } accept
# Rate limit ping requests to prevent ICMP flooding
ip protocol icmp limit rate 5/second accept
Advanced techniques: NAT, Port Forwarding, and Brute-force Prevention
Enable IP Forwarding in the Kernel
Before setting up NAT or port forwarding, you must enable packet forwarding in the Linux kernel:
sudo sysctl -w net.ipv4.ip_forward=1
echo "net.ipv4.ip_forward=1" | sudo tee -a /etc/sysctl.d/99-ipforward.conf
1. Configuring Source NAT (Masquerade)
This scenario applies when turning a Linux server into a gateway sharing an Internet connection with a local LAN or internal containers:
table ip nat_gateway {
chain postrouting {
type nat hook postrouting priority srcnat; policy accept;
oifname "eth0" masquerade
}
}
2. Port Forwarding (DNAT)
Forward traffic from a public IP to an internal application server (e.g., forwarding port 8080 on interface eth0 to port 80 of server 192.168.10.50):
table ip nat_gateway {
chain prerouting {
type nat hook prerouting priority dstnat; policy accept;
iifname "eth0" tcp dport 8080 dnat to 192.168.10.50:80
}
}
3. Automated SSH Brute-force Protection with Dynamic Sets
Instead of installing memory-hungry daemons like Fail2ban, you can leverage native dynamic sets and meters in nftables to drop attack IPs directly at the kernel level:
table inet my_firewall {
# Dynamic blacklist for offending IPs, expires after 15 minutes
set ssh_blacklist {
type ipv4_addr
flags dynamic, timeout
timeout 15m
}
chain my_input {
type filter hook input priority filter; policy drop;
iif "lo" accept
ct state established,related accept
# Immediately drop traffic from blacklisted IPs
ip saddr @ssh_blacklist drop
# Flag IPs opening over 4 new SSH connections per minute and add to blacklist
tcp dport 22 ct state new meter ssh_meter { ip saddr limit rate over 4/minute } add @ssh_blacklist { ip saddr } drop
# Accept remaining legitimate SSH connections
tcp dport 22 ct state new accept
}
}
Real-world production insights
Performance: O(1) vs. O(n)
In a microservice ecosystem processing around 40,000 requests/sec, blocking over 3,000 IPs with iptables once pushed ksoftirqd load to 25% CPU per core. This was because iptables evaluated rules sequentially line by line (O(n) complexity).
Switching to nftables and grouping IP addresses into a Set (backed by kernel hash tables or red-black trees) dropped lookup times to O(1). CPU utilization for softirq dropped from 25% to under 2%, significantly stabilizing network latency.
Automatic migration from iptables to nftables
If your server currently hosts hundreds of complex iptables rules, you don’t need to rewrite them manually:
# 1. Export current ruleset to a file
sudo iptables-save > iptables_backup.rules
# 2. Automatically translate rules to nftables syntax
sudo iptables-restore-translate -f iptables_backup.rules > rules_converted.nft
# 3. Inspect converted ruleset
cat rules_converted.nft
Safety measures to avoid getting locked out of SSH
Whenever modifying firewall rules on a remote server, always set up a rollback mechanism to protect against accidental lockouts:
# Schedule automatic ruleset flush after 5 minutes in background
sleep 300 && sudo nft flush ruleset &
If your SSH session remains active after reloading the rules, simply run kill %1 to cancel the background sleep job. In the worst-case scenario, the firewall ruleset will automatically flush after 5 minutes, restoring normal access.

