I have this iptable rules:

-N fail2ban-ssh
-A INPUT -p tcp -m multiport --dports 22 -j fail2ban-ssh
-A INPUT -p tcp -m multiport --dports 22 -j fail2ban-ssh
-A INPUT -i lo -j ACCEPT
-A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A INPUT -p tcp -m tcp --dport 22 -j ACCEPT
-A INPUT -p tcp -m tcp --dport 80 -j ACCEPT
-A fail2ban-ssh -s xx.xxx.xx.xx/32 -j REJECT --reject-with icmp-port-unreachable
-A fail2ban-ssh -j RETURN
-A fail2ban-ssh -j RETURN

The lines

-A INPUT -p tcp -m multiport --dports 22 -j fail2ban-ssh


-A fail2ban-ssh -j RETURN

seems to be duplicated or written twice. How can I remove the duplicate?


8 Answers 8


List with line numbers and delete by number.

iptables --line-numbers --list

Then delete one rule using it's line number. Then repeat (line numbers change for following rules when one is deleted so re-list before deleting another).

iptables -D INPUT 6
  • 2
    Thanks for this buddy! Will accept this in a matter of minutes. Commented Sep 15, 2014 at 13:48
  • Will this remain persistent after a reboot? Commented Nov 16, 2016 at 17:05
iptables-save | uniq | iptables-restore

That is all you need really.

  • 8
    AFAICT, this will only remove adjacent duplicates. It will fail to remove non-adjacent duplicates.
    – user61015
    Commented Jan 16, 2018 at 3:36
  • @sampablokuper Then just pipe to sort -u before uniq. Commented Oct 19, 2020 at 21:58
  • 3
    @SkippyleGrandGourou you need to be careful about sorting everything. In the example above, would that not reorder the -j RETURN to before the -s xx.xxx.xx.xx/32 -j REJECT rule, meaning the latter rule would never be seen?
    – mwfearnley
    Commented Oct 29, 2021 at 9:30
  • 2
    @mwfearnley Ha ! You’re right, indeed. Commented Oct 29, 2021 at 13:46
  • 1
    @SkippyleGrandGourou sort's -u flag already removes duplicates, so there's no need to use uniq at all after that.
    – nullromo
    Commented Feb 15 at 22:07

If you only want to delete double lines that are directly one after another you can export, unify and reimport it with

mkdir ~/tmp
iptables-save > ~/tmp/iptables.conf
uniq /tmp/iptables.conf > ~/tmp/iptables_new.conf
iptables-restore < ~/tmp/iptables_new.conf

If you want to delete other lines use an editor on ~/tmp/iptables.conf before you reimport it the same way.

Check your new rules with

  • If stripping adjacent duplicates is what you seek, then Ricky Neff's answer is more secure, because it avoids exposing firewall the rules to other users who have access to /tmp.
    – user61015
    Commented Jan 16, 2018 at 18:10
  • 1
    thx, you are right, you shouldn't use /tmp cause it is world-readable. I changed it to ~/tmp
    – rubo77
    Commented Jan 16, 2018 at 19:17

A comment to fail2ban: fail2ban seems to add its iptables rules itself. So you should not store these rules with e.g. iptables-save. Then after a reboot the rules will be doubled (your saved rule + the rule added by fail2ban).


Delete all duplicated lines except COMMIT

/sbin/iptables-save | awk '!COMMIT||!x[$0]++' | /sbin/iptables-restore
  • Didn't made any changes with "!COMMIT||" part! But in my iptables-save I had COMMIT only at the bottom. So it worked perfectly. But still wondering what was wrong.
    – user109764
    Commented Feb 10, 2021 at 15:49
  • 2
    Shouldn't it be awk '/COMMIT/||... to keep the COMMIT lines? I think !COMMIT would keep every line, since the COMMIT variable is never set.
    – mwfearnley
    Commented Oct 29, 2021 at 9:26
  • The correct command line is: iptables-save | awk '/COMMIT/||!x[$0]++' | iptables-restore -- The awk command prints each line that is COMMIT, as well as the first occurrence of each line.
    – phanaz
    Commented May 17 at 7:51

Here's an improved version using awk. This removes all duplicates in the -A rules, keep the rest of the strucuture correct, which is probably what you want :

iptables-save|awk '{if(/^-A /){if (!a[$0]++)print $0}else if (!/^-A /){print $0}}'|iptables-restore


I am using such a small bash script which runs through cron.

         readarray -t tabl_lines <<< "$(iptables -nL INPUT --line-number | grep "fail2ban-ssh")"
            for tline in "${tabl_lines[@]}"
            #skip the first result
            if [ -n "$i" ]; then
            sudo iptables -D INPUT -p tcp -m multiport --dports 22 -j fail2ban-ssh
            # if necessary, you can erase and other rules, 
            # because they usually repeat the same number of times
            # sudo iptables -D INPUT -p tcp -m multiport --dports 21 -j fail2ban-vsftpd
if [ "$save_iptables" == "yes" ]; then
/sbin/service iptables save

Kudos to Igor for his script. I feel it's useful:

/sbin/service iptables save
/sbin/iptables-save | awk '/^COMMIT$/ { delete x; }; !x[$0]++' > /tmp/iptables.conf
/sbin/iptables -F
/sbin/iptables-restore < /tmp/iptables.conf
/sbin/service iptables save
/sbin/service iptables restart
if [ -f /tmp/iptables.conf ] ; then /bin/rm -f /tmp/iptables.conf ; fi

Caveat: works only on Redhat / Centos, or other distro with iptables service. Thus my shorted mod for cron/one time use is:

/sbin/iptables-save | awk '/^COMMIT$/ { delete x; }; !x[$0]++' > /tmp/iptables.conf
/sbin/iptables -F
/sbin/iptables-restore < /tmp/iptables.conf
if [ -f /tmp/iptables.conf ] ; then /bin/rm -f /tmp/iptables.conf ; fi

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .