iptables rules are volatile by default; they live only in memory and disappear when the system reboots.

Let’s see iptables-save in action. Imagine you’ve just set up a firewall with a few rules:

# Allow SSH traffic
sudo iptables -A INPUT -p tcp --dport 22 -j ACCEPT

# Allow HTTP traffic
sudo iptables -A INPUT -p tcp --dport 80 -j ACCEPT

# Drop all other incoming traffic
sudo iptables -A INPUT -j DROP

# Allow established connections to return
sudo iptables -A OUTPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT

After running these, your firewall is active. But if you reboot now, you’re back to square one. To make these rules permanent, we use iptables-save to dump the current rules to a file, and then a script to load them back on boot.

Here’s how it works:

  1. Saving the Rules: The iptables-save command writes the current in-memory rules to standard output. We redirect this to a file, typically /etc/iptables/rules.v4 for IPv4.

    sudo iptables-save > /etc/iptables/rules.v4
    

    This file will contain your rules in a specific, human-readable format, like this:

    *filter
    :INPUT ACCEPT [0:0]
    :FORWARD ACCEPT [0:0]
    :OUTPUT ACCEPT [0:0]
    -A INPUT -p tcp --dport 22 -j ACCEPT
    -A INPUT -p tcp --dport 80 -j ACCEPT
    -A INPUT -j DROP
    -A OUTPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
    COMMIT
    # Completed on Mon Mar 11 10:30:00 2024
    

    The *filter indicates the table (the default filter table). Lines starting with : define chains and their default policies. Lines starting with -A append rules to chains. COMMIT signifies the end of the rules for that table.

  2. Loading the Rules on Boot: The iptables-restore command reads a file (or standard input) and applies the rules within it. To make this happen at boot, we typically use a systemd service or an init script.

    For systemd, you’d create a service file like /etc/systemd/system/iptables-restore.service:

    [Unit]
    Description=Restore iptables rules
    Before=network.target
    Comment=Restore iptables rules from /etc/iptables/rules.v4
    
    [Service]
    Type=oneshot
    ExecStart=/sbin/iptables-restore /etc/iptables/rules.v4
    ExecStop=/sbin/iptables-save > /etc/iptables/rules.v4.current # Optional: save current rules on stop
    RemainAfterExit=yes
    
    [Install]
    WantedBy=multi-user.target
    

    Then, enable and start it:

    sudo systemctl enable iptables-restore.service
    sudo systemctl start iptables-restore.service
    

    The Before=network.target ensures that the rules are restored before the network interfaces are fully brought up, which is crucial for firewalls. Type=oneshot means the service runs a command once and then exits.

  3. The iptables-save Format: The iptables-save format is a declarative way to represent your firewall state. Each section starts with a table name (e.g., *filter, *nat). Within each table, chains are defined (:), and rules are appended (-A) or inserted (-I). The COMMIT keyword marks the end of a table’s definition. This structured format is precisely what iptables-restore expects.

The key to persistence is the iptables-save command writing the current state to a file, and iptables-restore reading that file during the boot process. This ensures your firewall configuration survives reboots, maintaining your security posture without manual intervention.

The next logical step is to handle IPv6 rules, which requires a similar process but uses ip6tables-save and ip6tables-restore with a separate rules file, typically /etc/iptables/rules.v6.

Want structured learning?

Take the full Iptables course →