Configuration

fail2ban banaction: choosing how IPs are blocked

banaction decides which firewall fail2ban talks to. Pick the one that matches the firewall you actually run, or bans are logged but nothing is blocked.

Updated 7 min readChecked against fail2ban 1.1.1

banaction is the firewall action fail2ban uses to block an IP: nftables, iptables-multiport, ufw, firewallcmd-rich-rules and so on (files in /etc/fail2ban/action.d/). Set it once in [DEFAULT] of /etc/fail2ban/jail.local and every jail uses it. Pick the firewall your system actually runs: nftables on current Debian, Ubuntu and Arch; firewalld on RHEL, Rocky, Alma and Fedora; ufw if you manage your firewall with ufw.

/etc/fail2ban/jail.local
[DEFAULT]
banaction          = nftables
banaction_allports = nftables[type=allports]
bash
sudo fail2ban-client reload
output
$ sudo fail2ban-client get sshd actions
The jail sshd has the following actions:
nftables

banaction vs action vs banaction_allports#

Three options, three jobs:

Option What it is Default
banaction The firewall action for per-port bans nftables (1.1.1 Debian paths), iptables-multiport before 1.1.1
banaction_allports The firewall action for all-port bans (used by recidive, pam-generic) nftables[type=allports], iptables-allports before 1.1.1
action Everything that happens on a ban: the firewall plus optional email, reporting… %(action_)s

action is built from banaction. The default chain in jail.conf is:

ini
action_ = %(banaction)s[port="%(port)s", protocol="%(protocol)s", chain="%(chain)s"]
action  = %(action_)s

So changing banaction changes the firewall for every jail that uses the shortcuts (action_, action_mw, action_mwl), while each jail's port and protocol are passed along automatically. Set action directly only when you want a different combination, for example ban plus email alerts or ban plus AbuseIPDB reporting. If you set action to a bare firewall action, you have to pass port yourself.

Always change banaction and banaction_allports together. Otherwise recidive keeps using the old firewall.

Which banaction should I use?#

Banaction Firewall Use it when Per-port / all ports
nftables nftables (own table inet f2b-table) Debian 11+, Ubuntu 22.04+, Arch, any system without firewalld or ufw nftables / nftables[type=allports]
iptables-multiport iptables (legacy or iptables-nft) Older systems, or rules that must sit in a specific iptables chain (Docker) iptables[type=multiport] / iptables-allports
iptables-ipset iptables + ipset Large numbers of bans on iptables systems iptables-ipset[type=multiport] / [type=allports]
ufw ufw You manage the firewall with ufw (Ubuntu desktops, many VPS guides) Blocks all ports of the IP
firewallcmd-rich-rules firewalld RHEL, Rocky, Alma, Fedora with firewalld running per port
firewallcmd-ipset firewalld + ipset firewalld with many bans firewallcmd-ipset[actiontype=<allports>]
route routing table No firewall at all; blackholes the IP with ip route add unreachable all traffic
hostsdeny /etc/hosts.deny Only programs built with TCP Wrappers; rarely useful today per daemon
cloudflare-token Cloudflare API Sites behind Cloudflare: block at the edge, use in addition to a local action edge
pf pf FreeBSD, OpenBSD, macOS pf[actiontype=<allports>]

The action library lists every ban action with its parameters.

iptables or nftables?#

Use nftables unless something forces iptables. Since fail2ban 1.1.1 the upstream defaults are nftables (Debian paths), and Debian 13, Ubuntu 24.04 and Ubuntu 26.04 packages set it too. On systems where iptables is really iptables-nft, both end up in nftables anyway; the nftables action is just more efficient because it keeps bans in a set instead of one rule per IP.

Choose iptables when other tooling expects iptables chains: Docker's DOCKER-USER chain, Shorewall, or scripts that parse iptables -S. Don't mix: if a host runs firewalld, use the firewalld actions; with ufw, use ufw. A ban added behind the firewall manager's back can be flushed when the manager reloads.

ufw#

/etc/fail2ban/jail.local
[DEFAULT]
banaction          = ufw
banaction_allports = ufw

The ufw action inserts ufw prepend reject from <ip> to any rules, so the IP is blocked on all ports, whatever the jail's port says. ufw must be enabled (sudo ufw status). More in the UFW guide.

firewalld#

On Fedora, RHEL, Rocky and Alma the fail2ban-firewalld package installs /etc/fail2ban/jail.d/00-firewalld.conf, which already sets firewalld actions. Don't override banaction in jail.local unless you've disabled firewalld. Verify with:

bash
cat /etc/fail2ban/jail.d/00-firewalld.conf
sudo firewall-cmd --list-rich-rules

Details in the firewalld guide.

Default banaction per distribution#

System fail2ban Default banaction Set by
Upstream 1.1.1 tarball 1.1.1 nftables / nftables[type=allports] paths-debian.conf (included by stock jail.conf)
Upstream 1.1.0 and older ≤ 1.1.0 iptables-multiport / iptables-allports jail.conf
Debian 13, Ubuntu 26.04 1.1.0 nftables jail.d/defaults-debian.conf
Ubuntu 24.04 1.0.2 nftables jail.d/defaults-debian.conf
Debian 12, Ubuntu 22.04 1.0.2 / 0.11.2 iptables-multiport jail.conf
Fedora, RHEL/Rocky/Alma (EPEL) 1.1.0 firewalld rich rules jail.d/00-firewalld.conf from fail2ban-firewalld
FreeBSD, macOS (current development) 1.1.2-dev pf paths-freebsd.conf, paths-osx.conf

In the 1.1.2 development branch, paths-arch.conf, paths-fedora.conf and paths-opensuse.conf also default to nftables. Always confirm on your machine:

bash
sudo fail2ban-client -d | grep -E "addaction" | sort -u

DROP vs REJECT (blocktype)#

Most firewall actions take a blocktype parameter:

Action Default blocktype Drop instead
nftables reject nftables[blocktype=drop]
iptables-* REJECT --reject-with icmp-port-unreachable iptables-multiport[blocktype=DROP]
ufw reject ufw[blocktype=deny]
route unreachable route[blocktype=blackhole]
  • REJECT answers immediately ("port unreachable"). Clients fail fast, including your own users if they're banned by mistake, which makes problems easy to diagnose.
  • DROP silently discards packets. The client waits for a timeout, which slows down some scanners, and your server reveals slightly less.

For brute-force protection the difference is small; the attacker is blocked either way. Set it inside the brackets of banaction:

/etc/fail2ban/jail.local
[DEFAULT]
banaction          = nftables[blocktype=drop]
banaction_allports = nftables[type=allports, blocktype=drop]

A separate blocktype = drop line in a jail has no effect: action parameters only reach the action through the brackets.

Chains: INPUT, FORWARD and DOCKER-USER#

The chain option tells iptables-based actions where to hook in. The stock value chain = <known/chain> means "the action's own default": INPUT for the iptables actions. The nftables action creates its own chain f2b-chain in table inet f2b-table, hooked on input.

Traffic to Docker containers with published ports goes through FORWARD, not INPUT, so an INPUT ban doesn't stop it. With iptables, put the jail's rules in DOCKER-USER:

/etc/fail2ban/jail.local
[vaultwarden]
enabled   = true
banaction = iptables-multiport
chain     = DOCKER-USER

Since 1.1.1 the iptables actions accept several chains: chain = INPUT,DOCKER-USER. The full Docker setup is in Docker: banned but not blocked.

Check that bans reach the firewall#

After a ban, look for the IP in the firewall itself:

bash
# nftables: one set per jail, e.g. addr-set-sshd (IPv6: addr6-set-sshd)
sudo nft list table inet f2b-table

# iptables / ipset: chain f2b-<jail>, set f2b-<jail>
sudo iptables -S f2b-sshd
sudo ipset list f2b-sshd

# ufw
sudo ufw status numbered | grep 203.0.113.42

# firewalld
sudo firewall-cmd --list-rich-rules

To test without waiting for an attack, ban a documentation address by hand and look for it:

output
$ sudo fail2ban-client set sshd banip 192.0.2.15 192.0.2.16
2
bash
sudo nft list table inet f2b-table | grep 192.0.2.15
sudo fail2ban-client set sshd unbanip 192.0.2.15 192.0.2.16

If fail2ban lists the IP as banned but the firewall has no entry, or has it and traffic still gets through, see banned but still connecting. Most cases are the wrong banaction for the active firewall, traffic going through FORWARD (Docker), or a wrong port in the jail.

FAQ#

What is banaction in fail2ban?#

banaction names the firewall action, a file in /etc/fail2ban/action.d/, that fail2ban uses to block IPs, for example nftables, iptables-multiport, ufw or firewallcmd-rich-rules. The default action = %(action_)s calls it with each jail's port and protocol, so one banaction line in [DEFAULT] sets the firewall for all jails.

What is the difference between action and banaction?#

banaction is only the firewall part. action is the complete list of things done on a ban, and by default it's built from banaction (action_, action_mw, action_mwl). Change banaction to switch firewalls; change action to add email alerts, reporting or several actions at once.

Should fail2ban use iptables or nftables?#

nftables on any current Linux distribution: it's the default since fail2ban 1.1.1 and on Debian 13 and Ubuntu 24.04+, and it stores bans in efficient sets. Use iptables actions only when you need specific iptables chains such as DOCKER-USER, and the firewalld or ufw actions if those manage your firewall.

Should fail2ban DROP or REJECT?#

Either works. REJECT (the default for nftables and iptables) tells the client immediately that the port is closed; DROP silently discards packets so the client waits for a timeout. To drop, use banaction = nftables[blocktype=drop] or iptables-multiport[blocktype=DROP].

What is banaction_allports?#

The action used by jails that should block every port of an attacker, such as recidive and pam-generic. It defaults to nftables[type=allports] in 1.1.1 (iptables-allports before). Change it whenever you change banaction, so all jails use the same firewall.

Spotted something wrong or out of date? Tell us — every page is checked against the current fail2ban release.