| |

LFCE 79 ๐Ÿง nftables โ€” Migration from iptables

โฑ๏ธ Estimated read: 12 min | |

The previous chapters covered nftables as a standalone framework. This chapter covers the transition from an existing iptables configuration to nftables, which is the practical situation on every server that has been running for more than a few years. The migration is not a rewrite from scratch. The Netfilter project provides translation tools that convert an iptables ruleset into nftables syntax, and the distributions provide a compatibility layer that allows iptables commands to run on top of the nftables kernel backend while the migration is in progress . The LFCE exam expects the candidate to understand both the legacy tool and the modern framework, and the migration path is the bridge between them.

The migration has three phases: exporting and translating the existing rules, validating the translated ruleset, and switching the active backend. The translation is mechanical, but the result is not necessarily equivalent. A rule that uses a match or target that nftables does not support is translated with a comment noting the limitation, and the administrator must replace it manually . The validation phase is where the translated rules are checked against the actual traffic, and the switch is where the iptables service is disabled and the nftables service is enabled.

Key point: The iptables-restore-translate command converts a saved iptables ruleset into nftables syntax, and the iptables-translate command converts a single iptables command. The translated file must be reviewed before it is applied, because complex matches and targets may not translate perfectly. The iptables-nft compatibility layer allows iptables commands to continue working on top of the nftables backend, but it is a bridge, not a destination; the native nftables syntax is the long-term configuration.


Why migration requires a phased approach

The translation-limitation problem.

The iptables-restore-translate tool handles the common matches and targets, but it does not handle every extension. A rule that uses a match module that nftables does not implement is translated with a comment, and the rule is effectively disabled in the translated file . The administrator must read the translated file and replace the commented rules with native nftables equivalents. The migration is therefore not a one-command operation; it is a translation followed by a review.

The coexistence problem.

During the migration, both iptables and nftables may be active. The iptables-nft compatibility layer runs iptables commands through the nftables kernel backend, so a rule added with iptables appears in the nftables ruleset . If the legacy iptables-legacy backend is also active, the two rulesets are independent and may conflict. The migration requires choosing one backend and disabling the other, or using the compatibility layer as the single backend during the transition .

The lockout problem.

The switch from iptables to nftables can lock the administrator out if the translated ruleset does not allow SSH. The same precautions as the iptables persistence chapter apply: keep a second session open, test the translated ruleset in a staging environment, and use a rollback mechanism. The nft -c -f command checks the syntax without applying the rules, and the rules can be applied and then flushed if the session survives .


a. Exporting and translating the iptables ruleset

The first step is to save the existing iptables rules to a file. The iptables-save and ip6tables-save commands write the rules in a format that the translation tool can read.

# Save the IPv4 rules
sudo iptables-save > ~/iptables-backup.rules

# Save the IPv6 rules
sudo ip6tables-save > ~/ip6tables-backup.rules

The saved file contains the tables, the chains, the policies, and the rules. The iptables-restore-translate command reads the file and writes the equivalent nftables syntax.

# Translate the IPv4 rules
sudo iptables-restore-translate -f ~/iptables-backup.rules > ~/nftables-translated.nft

# Translate the IPv6 rules and append to the same file
sudo ip6tables-restore-translate -f ~/ip6tables-backup.rules >> ~/nftables-translated.nft

The -f flag specifies the input file, and the redirect writes the output to a new file . The translated file contains the table, chain, and rule statements that nftables expects. The iptables-translate command does the same for a single rule, which is useful for checking the syntax of a specific rule before translating the whole file.

# Translate a single rule
sudo iptables-translate -A INPUT -p tcp --dport 22 -j ACCEPT
# Output: nft add rule ip filter INPUT tcp dport 22 counter accept

The single-rule form shows the nftables equivalent of the iptables command, which is useful for understanding the syntax differences . The translated rule includes the counter statement, which nftables adds by default in some translations, and the accept verdict.


b. Reviewing and validating the translated ruleset

The translated file must be reviewed before it is applied. The iptables-restore-translate tool comments out rules that it cannot translate, and the administrator must replace them manually.

# Review the translated file
cat ~/nftables-translated.nft

The file begins with the table definitions and the chain definitions. The rules are listed under the chains. A rule that could not be translated appears as a comment, with a note about the unsupported match or target. The administrator must check for these comments and rewrite the rules using nftables syntax .

The nft -c -f command checks the syntax of the translated file without applying it. The -c flag enables the check mode, and the -f flag specifies the file.

# Check the syntax
sudo nft -c -f ~/nftables-translated.nft

The check mode reports syntax errors and does not modify the running ruleset. If the file is syntactically valid, it can be applied with nft -f. The flush ruleset command at the top of the file clears the existing ruleset before the new rules are applied, which is necessary for a clean migration .


c. Switching the backend and disabling the old service

The migration is complete when the nftables service is enabled and the iptables service is disabled. The iptables-nft compatibility layer can remain in place for scripts that still call iptables, but the native nftables configuration is the source of truth.

# Install nftables if it is not already installed
sudo apt install nftables    # Debian
sudo dnf install nftables    # RHEL

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

# Disable the iptables service
sudo systemctl disable --now iptables
sudo systemctl disable --now ip6tables

The systemctl enable --now nftables command enables the service and starts it immediately. The systemctl disable --now iptables command stops the iptables service and prevents it from starting at boot . On RHEL, the firewalld service must also be disabled if it is installed, because it manages the firewall independently of the nftables service.

The update-alternatives command selects the iptables implementation. The iptables-nft binary uses the nftables kernel backend, and the iptables-legacy binary uses the legacy backend.

# Check the current backend
sudo update-alternatives --display iptables

# Switch to iptables-nft
sudo update-alternatives --set iptables /usr/sbin/iptables-nft
sudo update-alternatives --set ip6tables /usr/sbin/ip6tables-nft

The update-alternatives --set command changes the default binary. The iptables-nft binary is the compatibility layer that translates the iptables commands into nftables rules . The switch is optional if the native nftables configuration is already in place, but it is useful for scripts that have not been updated to use nft directly.


Complete Example Session

# ============================================
# PART 1: BACK UP THE IPTABLES RULES
# ============================================
sudo iptables-save > ~/iptables-backup.rules
sudo ip6tables-save > ~/ip6tables-backup.rules

# ============================================
# PART 2: TRANSLATE THE RULES
# ============================================
sudo iptables-restore-translate -f ~/iptables-backup.rules > ~/nftables-translated.nft
sudo ip6tables-restore-translate -f ~/ip6tables-backup.rules >> ~/nftables-translated.nft

# ============================================
# PART 3: REVIEW THE TRANSLATED FILE
# ============================================
cat ~/nftables-translated.nft
# Check for commented-out rules and unsupported matches

# ============================================
# PART 4: CHECK THE SYNTAX
# ============================================
sudo nft -c -f ~/nftables-translated.nft
# No output means the syntax is valid

# ============================================
# PART 5: APPLY THE RULES
# ============================================
sudo nft -f ~/nftables-translated.nft

# ============================================
# PART 6: VERIFY THE RULESET
# ============================================
sudo nft list ruleset
# Output shows the translated tables and chains

# ============================================
# PART 7: TEST CONNECTIVITY
# ============================================
# From another host: ssh, curl, ping the server
# Confirm that the required services are reachable

# ============================================
# PART 8: DISABLE IPTABLES SERVICE
# ============================================
sudo systemctl disable --now iptables
sudo systemctl disable --now ip6tables

# ============================================
# PART 9: ENABLE NFTABLES SERVICE
# ============================================
sudo systemctl enable --now nftables

# ============================================
# PART 10: SWITCH THE COMPATIBILITY LAYER
# ============================================
sudo update-alternatives --set iptables /usr/sbin/iptables-nft
sudo update-alternatives --set ip6tables /usr/sbin/ip6tables-nft

The ten parts demonstrate the migration workflow: backup, translation, review, syntax check, apply, verify, test, disable the old service, enable the new service, and switch the compatibility layer. The critical step is Part 4: the syntax check catches errors before the ruleset is applied, and the review in Part 3 catches the rules that the translation tool could not convert.


Quick Reference

Migration Commands

CommandPurpose
iptables-save > fileSave IPv4 rules
ip6tables-save > fileSave IPv6 rules
iptables-restore-translate -f fileTranslate a saved ruleset
iptables-translate <command>Translate a single command
nft -c -f fileCheck syntax without applying
nft -f fileApply the ruleset

Service Management

CommandPurpose
systemctl enable --now nftablesEnable and start nftables
systemctl disable --now iptablesDisable and stop iptables
systemctl disable --now ip6tablesDisable and stop ip6tables
systemctl mask firewalldPrevent firewalld from starting

Compatibility Layer

CommandPurpose
update-alternatives --display iptablesShow the current backend
update-alternatives --set iptables /usr/sbin/iptables-nftSwitch to nftables backend
iptables-nft -LList rules through the compatibility layer

Translation Limitations

LimitationHandling
Unsupported match moduleRule commented out; rewrite in nftables
Unsupported targetRule commented out; rewrite in nftables
Complex chainsManual review and adjustment

Best Practices

โœ… Do This:

# Back up the iptables rules first
iptables-save > ~/iptables-backup.rules                         # โœ…

# Translate the whole ruleset
iptables-restore-translate -f ~/iptables-backup.rules > rules.nft # โœ…

# Review the translated file for commented rules
cat rules.nft                                                   # โœ…

# Check the syntax before applying
nft -c -f rules.nft                                             # โœ…

# Test connectivity after applying
ssh user@server                                                 # โœ…

# Disable the old service after the new one is working
systemctl disable --now iptables                                # โœ…

โŒ Don’t Do This:

# Apply the translated rules without reviewing
nft -f rules.nft    # Unsupported rules are disabled silently    # โŒ

# Skip the syntax check
nft -f rules.nft    # A syntax error prevents the ruleset from applying # โŒ

# Disable iptables before the new rules are tested
systemctl disable --now iptables    # Lockout if nftables fails # โŒ

# Mix iptables-legacy and nftables
# The rulesets are independent and conflict                   # โŒ

# Assume the translation is perfect
# Complex matches and targets may not translate                # โŒ

Common Pitfalls

PitfallWhy It HappensFix
Unsupported rule silently disabledTranslation tool comments it outReview the file; rewrite in nftables
Syntax error on applyTranslated file has invalid syntaxnft -c -f reports the error
Lockout after migrationSSH rule not translated or not testedTest before disabling iptables
Rules not persistentnftables service not enabledsystemctl enable --now nftables
firewalld conflictfirewalld still active on RHELsystemctl mask firewalld

Real-World Examples

1. Back up iptables rules

iptables-save > ~/iptables-backup.rules

2. Translate a saved ruleset

iptables-restore-translate -f ~/iptables-backup.rules > rules.nft

3. Translate a single rule

iptables-translate -A INPUT -p tcp --dport 22 -j ACCEPT

4. Check syntax

nft -c -f rules.nft

5. Apply the ruleset

nft -f rules.nft

6. Verify the ruleset

nft list ruleset

7. Disable iptables

systemctl disable --now iptables

8. Enable nftables

systemctl enable --now nftables

9. Switch the compatibility layer

update-alternatives --set iptables /usr/sbin/iptables-nft

10. Disable firewalld on RHEL

systemctl mask firewalld

Visual

Migration Workflow

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  BACKUP โ†’ TRANSLATE โ†’ REVIEW โ†’ CHECK โ†’ APPLY โ†’ SWITCH        โ”‚
โ”‚                                                              โ”‚
โ”‚  iptables-save > backup.rules                                โ”‚
โ”‚       โ”‚                                                      โ”‚
โ”‚       โ–ผ                                                      โ”‚
โ”‚  iptables-restore-translate -f backup.rules > rules.nft      โ”‚
โ”‚       โ”‚                                                      โ”‚
โ”‚       โ–ผ                                                      โ”‚
โ”‚  Review rules.nft for commented rules                        โ”‚
โ”‚       โ”‚                                                      โ”‚
โ”‚       โ–ผ                                                      โ”‚
โ”‚  nft -c -f rules.nft    (syntax check)                       โ”‚
โ”‚       โ”‚                                                      โ”‚
โ”‚       โ–ผ                                                      โ”‚
โ”‚  nft -f rules.nft       (apply)                              โ”‚
โ”‚       โ”‚                                                      โ”‚
โ”‚       โ–ผ                                                      โ”‚
โ”‚  Test connectivity                                           โ”‚
โ”‚       โ”‚                                                      โ”‚
โ”‚       โ–ผ                                                      โ”‚
โ”‚  Disable iptables, enable nftables                           โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Translation Output

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  iptables-restore-translate PRODUCES NFTABLES SYNTAX         โ”‚
โ”‚                                                              โ”‚
โ”‚  Input (iptables-save):                                      โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”‚
โ”‚  โ”‚ *filter                                                โ”‚  โ”‚
โ”‚  โ”‚ :INPUT DROP [0:0]                                      โ”‚  โ”‚
โ”‚  โ”‚ -A INPUT -p tcp --dport 22 -j ACCEPT                   โ”‚  โ”‚
โ”‚  โ”‚ COMMIT                                                 โ”‚  โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ”‚
โ”‚                           โ”‚                                  โ”‚
โ”‚                           โ–ผ                                  โ”‚
โ”‚  Output (nftables):                                          โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”‚
โ”‚  โ”‚ table ip filter {                                      โ”‚  โ”‚
โ”‚  โ”‚   chain INPUT {                                        โ”‚  โ”‚
โ”‚  โ”‚     type filter hook input priority 0; policy drop;    โ”‚  โ”‚
โ”‚  โ”‚     tcp dport 22 counter accept                        โ”‚  โ”‚
โ”‚  โ”‚   }                                                    โ”‚  โ”‚
โ”‚  โ”‚ }                                                      โ”‚  โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Compatibility Layer

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  iptables-nft RUNS IPTABLES COMMANDS ON NFTABLES             โ”‚
โ”‚                                                              โ”‚
โ”‚  iptables -A INPUT -p tcp --dport 8080 -j ACCEPT             โ”‚
โ”‚       โ”‚                                                      โ”‚
โ”‚       โ–ผ                                                      โ”‚
โ”‚  iptables-nft (compatibility layer)                          โ”‚
โ”‚       โ”‚                                                      โ”‚
โ”‚       โ–ผ                                                      โ”‚
โ”‚  nftables kernel backend                                     โ”‚
โ”‚       โ”‚                                                      โ”‚
โ”‚       โ–ผ                                                      โ”‚
โ”‚  nft list ruleset shows the rule                             โ”‚
โ”‚                                                              โ”‚
โ”‚  The compatibility layer is a bridge, not the destination.   โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Summary

ItemValue
Backup commandiptables-save > file
Translate rulesetiptables-restore-translate -f file
Translate single ruleiptables-translate <command>
Syntax checknft -c -f file
Apply rulesetnft -f file
Enable nftablessystemctl enable --now nftables
Disable iptablessystemctl disable --now iptables
Compatibility layeriptables-nft
Switch backendupdate-alternatives --set iptables /usr/sbin/iptables-nft
RHEL conflictsystemctl mask firewalld

Key takeaways:

  • The migration is a translation followed by a review. The iptables-restore-translate command converts the saved ruleset into nftables syntax, but it comments out the rules that it cannot translate. The administrator must read the translated file and replace the commented rules manually .
  • The syntax check catches errors before the ruleset is applied. The nft -c -f command validates the file without modifying the running ruleset. It should be run before the nft -f command that applies the rules .
  • The compatibility layer allows iptables commands to run on nftables. The iptables-nft binary translates the iptables syntax into nftables rules, which allows existing scripts to continue working during the transition. It is a bridge, not a destination .
  • The old service must be disabled after the new one is working. The systemctl disable --now iptables command stops the iptables service and prevents it from starting at boot. The nftables service must be enabled first, and the connectivity must be tested before the old service is disabled.
  • On RHEL, firewalld must be disabled. The firewalld service manages the firewall independently of the nftables service, and the two conflict. The systemctl mask firewalld command prevents it from starting .
  • The translation is not guaranteed to be equivalent. A rule that uses an unsupported match or target is disabled in the translated file, and the behavior of the firewall changes. The review and the connectivity test are the steps that catch the difference.

Remember: The migration from iptables to nftables is a phased process. Back up the iptables rules, translate them with iptables-restore-translate, review the translated file for commented rules, check the syntax with nft -c, apply the rules with nft -f, test the connectivity, and then disable the iptables service and enable the nftables service. The iptables-nft compatibility layer allows iptables commands to continue working on the nftables backend, but the native nftables configuration is the long-term source of truth. The translation is mechanical, but the equivalence is not guaranteed; the review and the test are the steps that make the migration safe.


Was this helpful?


Stop using slow, ad-bloated tool sites! ๐Ÿคฎ

๐Ÿ”Ž Search “KandZ Tools” on Google to use many professional utilities for free.

KandZ.me is the ultimate minimalist hub for:
โœ… Finance (Mortgage, Interest, Inflation)
โœ… Tech (Base64, JSON, Dev Suite, IP)
โœ… Health (BMI, BMR, TDEE)
โœ… Productivity (Timer, Workspace, QR)

โšก๏ธ Fast & Private
๐Ÿ”’ No data leaves your device
๐Ÿ’Ž 100% Free

๐Ÿ”— Use it now: https://tools.kandz.me
๐Ÿ”– Bookmark itโ€”youโ€™ll need it later!