Security

Migrate iptables rules to nftables

Move an existing iptables firewall to native nftables. Check whether you run iptables-nft or legacy, export with iptables-save, translate the rules, test them and switch the boot service.

AdvancedLinux server5 min read

Many servers still carry an iptables firewall written years ago. Current distributions are built around nftables, and the iptables tools now mostly act as a translation layer. Moving to native nftables gives you one config file for IPv4 and IPv6, sets instead of long rule lists, and fewer surprises when tools mix. This guide converts an existing ruleset with the official translation tools and switches over without losing SSH access.

Before you start

  • A Linux VPS running Ubuntu 22.04 or 24.04, Debian 12, or AlmaLinux or Rocky Linux 8 or 9, with an existing iptables ruleset.
  • Root or a sudo user.
  • No UFW or firewalld in use. Those tools manage their own rules; keep using them, or disable them first. See UFW vs firewalld vs nftables vs iptables.
  • Access to the browser console in your VPS provider's control panel.

Warning: Switching firewalls changes every rule at once. Make sure the translated ruleset allows your SSH port before you load it, and schedule an automatic rollback as shown below. If SSH stops working, log in through the browser console and run sudo nft flush ruleset.

Install the tools if they are missing:

Bash
# Ubuntu and Debian
sudo apt install nftables iptables
# AlmaLinux and Rocky Linux
sudo dnf install nftables iptables-nft

Check which iptables backend you use

Bash
sudo iptables -V
  • iptables v1.8.x (nf_tables): you run iptables-nft. Your rules already live in the kernel's nftables engine, written through a compatibility layer. This is the default on all the distributions above.
  • iptables v1.8.x (legacy): you run the old x_tables backend. Rules live in a separate kernel subsystem that nftables does not see.

On Debian and Ubuntu, the backend is selected with alternatives:

Bash
update-alternatives --display iptables

Either way, translating to native nft syntax is the clean path. Dumping nft list ruleset on an iptables-nft system is not enough: those tables are owned by iptables, and recent nft versions mark them as such.

Export the current rules

Save both IPv4 and IPv6 rules. These files are also your backup:

Bash
sudo iptables-save  | sudo tee /root/iptables-v4.rules
sudo ip6tables-save | sudo tee /root/iptables-v6.rules

Look through them and note which ports are allowed, especially SSH.

Translate the ruleset

iptables-restore-translate converts a whole saved file into nft commands:

Bash
sudo iptables-restore-translate  -f /root/iptables-v4.rules | sudo tee /root/ruleset-v4.nft
sudo ip6tables-restore-translate -f /root/iptables-v6.rules | sudo tee /root/ruleset-v6.nft

You can also translate a single rule to learn the syntax:

Bash
iptables-translate -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -j ACCEPT
nft 'add rule ip filter INPUT tcp dport 22 ct state new counter accept'

Check for anything the tool could not convert. Untranslated rules appear as comment lines starting with #:

Bash
grep -n '^#' /root/ruleset-v4.nft /root/ruleset-v6.nft

Rewrite those by hand, or decide you no longer need them. Rules added by Docker or Fail2ban usually show up here too. Leave those out, because those services recreate their own rules when they start.

Build and test the new config

Create the config file. On Ubuntu and Debian it is /etc/nftables.conf; on AlmaLinux and Rocky Linux it is /etc/sysconfig/nftables.conf:

#!/usr/sbin/nft -f

flush ruleset

include "/etc/nftables.d/ruleset-v4.nft"
include "/etc/nftables.d/ruleset-v6.nft"

Move the translated files into place and check the syntax:

Bash
sudo mkdir -p /etc/nftables.d
sudo cp /root/ruleset-v4.nft /root/ruleset-v6.nft /etc/nftables.d/
sudo nft -c -f /etc/nftables.conf

The translated output keeps the iptables layout: separate ip filter and ip6 filter tables with INPUT, FORWARD and OUTPUT chains. That works as it is. Later you can merge them into one inet table, as shown in Get started with nftables.

Switch over

  1. Schedule a rollback that clears all nftables rules in five minutes, in case something is wrong. The pattern is explained in Test firewall changes with an automatic rollback:
Bash
   sudo systemd-run --on-active=5m --unit=nft-rollback /usr/sbin/nft flush ruleset
  1. Stop the old service that restored iptables rules at boot:
Bash
   # Ubuntu and Debian
   sudo systemctl disable --now netfilter-persistent
   # AlmaLinux and Rocky Linux
   sudo systemctl disable --now iptables ip6tables

Ignore "unit not found" errors if you never used them.

  1. If you were on the legacy backend, clear the old rules so they cannot conflict:
Bash
   sudo iptables-legacy -F && sudo iptables-legacy -X && sudo iptables-legacy -P INPUT ACCEPT
   sudo ip6tables-legacy -F && sudo ip6tables-legacy -X && sudo ip6tables-legacy -P INPUT ACCEPT
  1. Load the new ruleset and test with a new SSH session:
Bash
   sudo nft -f /etc/nftables.conf
  1. If SSH works, cancel the rollback and enable nftables at boot:
Bash
   sudo systemctl stop nft-rollback.timer
   sudo systemctl enable --now nftables

Check it works

Bash
sudo nft list ruleset
systemctl is-enabled nftables

Compare the allowed ports with your original iptables-save output. From another computer, test that allowed ports connect and blocked ports do not:

Bash
nc -vz 203.0.113.10 22
nc -vz -w 3 203.0.113.10 3306

Reboot during a quiet period and check nft list ruleset again.

Troubleshooting

  • Translated file fails nft -c: an extension had no direct nft equivalent. Find the line number in the error and rewrite that rule by hand.
  • Old rules still apply after switching: you were on the legacy backend and did not flush it. Check with sudo iptables-legacy -S.
  • Rules disappear at boot: netfilter-persistent or iptables.service is still enabled and loads the old file, or nftables is not enabled.
  • Docker or Fail2ban stopped working: restart them after loading, so they add their rules to the new ruleset.
  • Locked out: log in through the browser console, run sudo nft flush ruleset, and fix the SSH rule.

Next steps

Was this guide helpful?

Still stuck?

Ask MdmHost AI for a quick answer, or open a ticket and our team will take a look. We are here 24/7.