Guides
fail2ban with iptables
iptables was fail2ban's default for most of its history, and many servers still use it. In 1.1.1 the iptables action was rewritten into one parameterized action. This guide shows the current syntax, how to read the f2b- chains, when to use ipset, and why a ban sometimes doesn't block.
To ban with iptables, set banaction = iptables-multiport and banaction_allports = iptables-allports under [DEFAULT] in /etc/fail2ban/jail.local. Since fail2ban 1.1.1 you can also write them as iptables[type=multiport] and iptables[type=allports]. Each jail gets its own chain, f2b-<jail>, and every banned IP becomes one rule in it. List them with sudo iptables -L f2b-sshd -n.
[DEFAULT]
banaction = iptables-multiport
banaction_allports = iptables-allportsOn Debian 13 and Ubuntu 24.04 or newer, the packaged default is nftables, and iptables is the choice you opt into. On Debian 12, Ubuntu 22.04 and older, iptables is still the default. For new setups, nftables scales better. Use iptables when your tooling expects it.
The iptables action after the 1.1.1 rewrite#
fail2ban 1.1.1 merged the iptables actions into one file, action.d/iptables.conf, with a type parameter. The old names still exist as thin wrappers:
| Action name | Equivalent | Rule that sends traffic to f2b-<jail> |
|---|---|---|
iptables |
iptables[type=oneport] |
-p tcp --dport <port> (one port) |
iptables-multiport |
iptables[type=multiport] |
-p tcp -m multiport --dports <ports> |
iptables-allports |
iptables[type=allports] |
-p tcp (all ports) |
iptables-ipset |
iptables-ipset[type=oneport] |
-p tcp --dport <port> -m set --match-set f2b-<jail> src -j REJECT |
iptables-ipset-proto6 |
iptables-ipset[type=multiport] |
multiport + ipset |
iptables-ipset-proto6-allports |
iptables-ipset[type=allports] |
allports + ipset |
Parameters you can pass:
| Parameter | Default | Meaning |
|---|---|---|
chain |
INPUT |
Chain(s) to hook into. Since 1.1.1 a list works: INPUT,FORWARD |
port |
from the jail | Port(s) the jump rule matches |
protocol |
tcp |
tcp, udp, tcp,udp, or all |
blocktype |
REJECT --reject-with icmp-port-unreachable (IPv6: icmp6-port-unreachable) |
Target for banned addresses |
returntype |
RETURN |
Last rule of the f2b- chain |
lockingopt |
-w |
Wait for the xtables lock instead of failing |
Plain iptables is oneport, which only works for a single port. Most jails list several (port = http,https), so multiport is the right default.
Compatibility note for custom actions#
The 1.1.1 rewrite changed internal tags. Custom actions that include iptables.conf and use <chain> in their own commands must switch to $chain, and <_ipt_for_proto-iter> became <_ipt-iter>. If a custom action fails after upgrading to 1.1.1, check the commands it inherits with fail2ban-client -d | grep <action>.
What the rules look like#
For the sshd jail with iptables-multiport and port = 22,2222, fail2ban creates on the first ban:
iptables -w -N f2b-sshd
iptables -w -A f2b-sshd -j RETURN
iptables -w -I INPUT -p tcp -m multiport --dports 22,2222 -j f2b-sshdand for each ban inserts a rule at the top of its chain:
iptables -w -I f2b-sshd 1 -s 203.0.113.42 -j REJECT --reject-with icmp-port-unreachableIPv6 addresses get the same rules through ip6tables. The chain is created when the first IP is banned, not at startup, so an empty jail has no f2b- chain yet.
View fail2ban's iptables rules#
# all fail2ban rules, in iptables-save syntax
sudo iptables -S | grep f2b-
# one jail's chain with packet counters
sudo iptables -L f2b-sshd -n -v --line-numbers
# IPv6
sudo ip6tables -S | grep f2b-The packet counter on a ban rule tells you whether the banned address is still sending traffic. fail2ban's own list, with expiry times, comes from sudo fail2ban-client get sshd banip --with-time. More ways are on list banned IPs.
Ban on several chains (Docker, routers)#
Since 1.1.1, chain accepts a comma-separated list, and fail2ban adds the jump rule to each one. That's how you cover traffic that never hits INPUT, such as containers with published ports:
[nginx-http-auth]
enabled = true
banaction = iptables-multiport
chain = INPUT,DOCKER-USERRemember that in DOCKER-USER and FORWARD the destination port is the container's port after DNAT, not the published host port. The Docker guide has complete examples.
Many bans: use ipset#
iptables-multiport evaluates one rule per banned IP, in order, for every new packet to the jail's ports. With hundreds of bans that's measurable. With tens of thousands (a long recidive list) it hurts. iptables-ipset keeps a single rule per chain and stores the addresses in an ipset, which is a hash lookup:
sudo apt install ipset # or: sudo dnf install ipset[DEFAULT]
banaction = iptables-ipset[type=multiport]
banaction_allports = iptables-ipset[type=allports]The set is named f2b-<jail> (IPv6: f2b-<jail>6), type hash:ip, with up to 65536 entries:
sudo ipset list f2b-sshdSet ipsettype = hash:net if you ban subnets, and raise maxelem for very large lists: iptables-ipset[type=allports, maxelem=262144].
DROP vs REJECT#
The default REJECT --reject-with icmp-port-unreachable tells the client immediately that the port is closed. DROP sends nothing, so the client waits for a timeout.
[DEFAULT]
banaction = iptables-multiport[blocktype=DROP]DROP slows down scanners slightly and reveals less. REJECT is friendlier to legitimate users who got banned by mistake, since their client fails fast with a clear error, and it avoids retries piling up. Either is fine. Since 1.1.1 a blocktype passed from the jail applies to both the IPv4 and IPv6 chains. To set them differently, use blocktype?family=inet6.
iptables-nft vs iptables-legacy#
Modern distributions ship iptables as a front end to nftables (iptables-nft). The older iptables-legacy talks to the old kernel tables. Check which one you have:
iptables -VThe version string ends in (nf_tables) or (legacy). Both work with fail2ban. What breaks things is mixing them: if Docker or another tool writes rules with one variant and fail2ban uses the other, each sees only its own rules, and iptables -S may not show the ones that actually block. Use the same variant everywhere (update-alternatives --config iptables on Debian/Ubuntu), or move fail2ban to the native nftables action.
"iptables not found"#
If iptables isn't installed, every ban fails. /var/log/fail2ban.log shows the command -- returned 127 and a HINT on 127: "Command not found". Debian 12+ minimal installs and many cloud images don't include iptables. Either install it (sudo apt install iptables) or, better, switch to the default nftables action, which needs only the nft binary. For ipset actions, the ipset package is needed too.
Why iptables isn't blocking a banned IP#
If fail2ban-client status lists the IP but it still connects:
- The jump rule doesn't match the traffic. The
f2b-chain is only consulted for the jail'sportandprotocol. An SSH server on port 2222 withport = sshin the jail is never covered. Checkiptables -S INPUT | grep f2b-. - An earlier rule accepts it first. fail2ban inserts its jump at the top of
INPUT, but a script or firewall manager that re-inserts its ownACCEPTrules above it wins. Look at the order withiptables -L INPUT -n --line-numbers. - The connection was already established, and your rules accept
ESTABLISHED,RELATEDbefore the jump. New connections are blocked, but the existing one keeps working. - The traffic doesn't pass
INPUT. Docker published ports and VMs go throughFORWARD. AddDOCKER-USERorFORWARDtochain. - The firewall was reloaded.
iptables-restore,netfilter-persistent reloador a firewall manager can wipe fail2ban's chains. Restart fail2ban afterwards to restore active bans from its database.
The full checklist is in banned but not blocked.
FAQ#
Does fail2ban use iptables?#
It can. fail2ban calls whatever ban action you configure. iptables actions were the default for years and still are on Debian 12, Ubuntu 22.04 and older. Debian 13 and Ubuntu 24.04+ default to nftables. Check yours with fail2ban-client get sshd actions.
What is the difference between iptables-multiport and iptables-allports?#
iptables-multiport sends only traffic for the jail's ports (-m multiport --dports 22,2222) to the f2b-<jail> chain, so a banned IP can still reach other services. iptables-allports sends all TCP traffic (or the jail's protocol) there, blocking the IP everywhere. recidive uses allports.
How do I list fail2ban iptables rules?#
Run sudo iptables -S | grep f2b- to see every fail2ban chain and rule, or sudo iptables -L f2b-sshd -n -v for one jail with packet counters. IPv6 bans are in ip6tables. With the ipset actions, list the addresses with sudo ipset list f2b-sshd.
When should I use iptables-ipset?#
When you expect hundreds or thousands of active bans. iptables-multiport adds one rule per IP, which slows every packet to the jail's ports as the list grows. iptables-ipset keeps one rule and stores the addresses in a hash set. It needs the ipset package.
Spotted something wrong or out of date? Tell us — every page is checked against the current fail2ban release.