# fail2ban with iptables

> fail2ban's iptables actions after the 1.1.1 rewrite: multiport and allports, f2b- chains, multiple chains, ipset, DROP vs REJECT, and iptables not blocking.
>
> Source: https://fail2ban.dev/guides/iptables/ · Updated 2026-10-25 · Checked against fail2ban 1.1.1
> fail2ban.dev is an independent guide, not the official fail2ban project.

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`.

```ini
[DEFAULT]
banaction          = iptables-multiport
banaction_allports = iptables-allports
```

## The iptables action after the 1.1.1 rewrite

fail2ban 1.1.1 merged the iptables actions into one file, [action.d/iptables.conf](/actions/#action-iptables), 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:

```bash
iptables -w -A f2b-sshd -j RETURN
iptables -w -I INPUT -p tcp -m multiport --dports 22,2222 -j f2b-sshd
```

```bash
```

## View fail2ban's iptables rules

```bash
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-
```

## 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:

```ini
[nginx-http-auth]
enabled   = true
banaction = iptables-multiport
chain     = INPUT,DOCKER-USER
```

## 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:

```bash
```
[DEFAULT]
banaction          = iptables-ipset[type=multiport]
banaction_allports = iptables-ipset[type=allports]
```

```bash
```

## 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.

```ini
[DEFAULT]
banaction = iptables-multiport[blocktype=DROP]
```

## 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:

```bash
```

## "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:

1. **The jump rule doesn't match the traffic.** The `f2b-` chain is only consulted for the jail's `port` and `protocol`. An SSH server on port 2222 with `port = ssh` in the jail is never covered. Check `iptables -S INPUT | grep f2b-`.
2. **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 own `ACCEPT` rules above it wins. Look at the order with `iptables -L INPUT -n --line-numbers`.
3. **The connection was already established**, and your rules accept `ESTABLISHED,RELATED` before the jump. New connections are blocked, but the existing one keeps working.
4. **The traffic doesn't pass `INPUT`.** Docker published ports and VMs go through `FORWARD`. Add `DOCKER-USER` or `FORWARD` to `chain`.
5. **The firewall was reloaded.** `iptables-restore`, `netfilter-persistent reload` or 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](/troubleshooting/not-banning/#part-2-the-ip-is-banned-but-still-connects).

## 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.
