You can replace your entire nftables ruleset without dropping a single packet.

Here’s how it works, using a hypothetical scenario where we’re moving from a basic allow-all inbound SSH policy to a more restrictive one that only allows SSH from a specific IP address.

Let’s say our current ruleset, loaded with nft -f current_rules.nft, looks like this:

table ip filter {
    chain input {
        type filter hook input priority 0; policy accept;
        # Allow all inbound SSH
        tcp dport ssh accept
    }
}

And our desired new ruleset, new_rules.nft, looks like this:

table ip filter {
    chain input {
        type filter hook input priority 0; policy accept;
        # Allow SSH only from 192.168.1.100
        tcp dport ssh ip saddr 192.168.1.100 accept
    }
}

The key to atomic replacement lies in nftables’ nft -f - command combined with careful staging. Instead of directly replacing the active ruleset, we’ll stage the new ruleset and then atomically swap it in.

First, let’s verify our new ruleset for syntax errors without applying it:

nft -c -f new_rules.nft

If this command returns without error, our syntax is good. Now, we’ll load the new ruleset into a temporary table. This is where the magic begins. We’ll use the nft --atomic flag, which ensures that the entire operation within that command is treated as a single, indivisible unit.

nft --atomic -f new_rules.nft

This command, when executed, doesn’t immediately replace our existing table ip filter. Instead, it creates a new table with the same name (ip filter) but with the rules defined in new_rules.nft. Because we used --atomic, if there’s any issue during the creation of this new table and its rules, the entire operation is rolled back, leaving our original table ip filter untouched.

Once the new table is successfully created and populated with the new rules, we need to swap it with the old one. The nft swap command is designed for this. It atomically replaces one table with another.

nft swap table ip filter new_filter

Here, new_filter is the temporary table name that nft --atomic -f new_rules.nft implicitly created (or we could explicitly name it if we were using nft add table followed by nft add set etc. in a more complex sequence). If nft --atomic -f new_rules.nft succeeded, it created a table named ip filter that contains the new rules. We then tell nft swap to swap the existing table ip filter (the old one) with this newly created table ip filter (which is what nft --atomic -f new_rules.nft would have created if no table of that name existed, or it would have replaced it if it did).

Wait, that’s not quite right. The nft --atomic -f new_rules.nft command replaces the table ip filter if it already exists. The swap command is more for when you’re managing multiple distinct tables and want to switch which one is active.

The correct atomic approach for replacing a ruleset is simpler. You load the new ruleset, and if it’s valid, it replaces the old one in a single, atomic operation. The nft -f command itself, when applied to an existing table name, performs this atomic replacement.

Let’s re-trace with the correct atomic mechanism.

First, ensure your existing ruleset is loaded and active:

# Assume current_rules.nft is loaded
nft -f current_rules.nft

Now, prepare your new_rules.nft.

table ip filter {
    chain input {
        type filter hook input priority 0; policy accept;
        # Allow SSH only from 192.168.1.100
        tcp dport ssh ip saddr 192.168.1.100 accept
    }
}

To atomically replace the entire table ip filter with the contents of new_rules.nft, you use nft -f with the new file. The --atomic flag is implicitly used by nft -f when it’s replacing an existing table.

nft -f new_rules.nft

This single command does the following:

  1. It parses new_rules.nft.
  2. If parsing is successful, it creates the new table structure defined in the file.
  3. It then atomically swaps the new table structure with the old table ip filter. This means that at no point is the filter table partially updated. Either the old rules are active, or the new rules are active.

Let’s confirm the new rules are active:

nft list ruleset

You should see the new configuration, with only SSH from 192.168.1.100 allowed.

Common Causes for Failure and How to Fix Them:

  1. Syntax Errors in the New Ruleset: The most common reason for nft -f new_rules.nft to fail is a simple typo or structural mistake in the new file.

    • Diagnosis: Run nft -c -f new_rules.nft. The -c flag (check) will parse the file and report any syntax errors without attempting to apply them.
    • Fix: Carefully review the error message from nft -c and correct the syntax in new_rules.nft. For example, if you forgot a tcp dport keyword, nft -c might show syntax error, unexpected dport.
    • Why it works: This ensures the ruleset is syntactically valid before nftables attempts to load it, preventing partial application.
  2. Existing Table Name Conflicts (if not replacing): If new_rules.nft defines a table with the same name as an existing table, and you’re not intending to replace it atomically, you’ll get errors. However, nft -f does replace the table if it exists. The issue arises if you’re trying to add a table that already exists.

    • Diagnosis: If nft -f new_rules.nft fails with a message like "table already exists," and you intended to replace it, this isn’t actually a failure of the atomic replacement. If you intended to add a new, distinct table, you’d need a different name.
    • Fix: For atomic replacement, nft -f new_rules.nft is correct. If you need a separate table, rename the table in new_rules.nft (e.g., table ip filter new_filter { ... }).
    • Why it works: nft -f is designed to either create a new table or atomically replace an existing one with the same name.
  3. Invalid Hook/Priority Combinations: nftables has strict rules about where chains can be hooked and their priorities.

    • Diagnosis: nft -c -f new_rules.nft will report errors like "invalid hook 'prerouting' for chain type 'filter'" or "chain already exists with different type/hook/priority".
    • Fix: Ensure your type, hook, and priority declarations in the chain definition are correct for the intended traffic flow. For the input chain, type filter hook input priority 0 is standard.
    • Why it works: Correctly defining chain placement and type ensures nftables can integrate the new rules into the kernel’s networking stack without conflicts.
  4. Referencing Non-Existent Sets or Maps: If your new ruleset refers to sets or maps that haven’t been defined yet within the same nft -f operation, it will fail.

    • Diagnosis: nft -c -f new_rules.nft will report errors like "set 'my_set' not found".
    • Fix: Ensure that any sets or maps referenced by rules are defined before the rules that use them within the same nft -f command, or are defined in separate, preceding nft add set commands.
    • Why it works: nftables needs to know the structure of all elements (tables, chains, sets, maps) before it can apply the rules, ensuring all dependencies are met atomically.
  5. Incorrect Address Family: Trying to apply IPv6 rules to an IPv4 table (or vice-versa) will cause issues.

    • Diagnosis: nft -c -f new_rules.nft might complain about incompatible family types or specific rule syntax. For instance, an IPv6 ip6 saddr rule in an ip table.
    • Fix: Ensure your table declaration matches the IP version of the addresses and protocols you’re using (e.g., table ip6 ... for IPv6).
    • Why it works: This respects the fundamental separation of IPv4 and IPv6 routing and filtering paths within the kernel.
  6. Permissions Issues: The user running the nft command might not have the necessary privileges to modify the kernel’s netfilter rules.

    • Diagnosis: The command will likely fail with a "permission denied" error.
    • Fix: Run the nft command using sudo or as the root user.
    • Why it works: Modifying netfilter rules is a privileged operation.

After successfully applying nft -f new_rules.nft, your next potential issue might be that your new restrictive rule is blocking legitimate management traffic, leading to a loss of access if you’re not careful.

Want structured learning?

Take the full Nftables course →