Configuration

The recidive jail: ban repeat offenders

recidive watches fail2ban's own log and gives IPs that keep getting banned a long, all-ports ban. Here is how to enable it, make bans permanent, and fix the cases where it silently does nothing.

Updated 8 min readChecked against fail2ban 1.1.1

The recidive jail bans IP addresses that other jails keep banning. It reads fail2ban's own log (/var/log/fail2ban.log), counts the Ban lines per IP, and when an address is banned 5 times within a day it blocks that address on all ports for one week. Enable it with [recidive] and enabled = true in /etc/fail2ban/jail.local. For permanent bans, set bantime = -1.

/etc/fail2ban/jail.local
[recidive]
enabled = true
bash
sudo fail2ban-client reload
sudo fail2ban-client status recidive

What the recidive jail does#

A normal jail like sshd bans an attacker for an hour, on the SSH port. Persistent bots wait out the ban and come back, or move on to your mail or web ports. recidive ("relapse into crime") catches that pattern:

Every ban fail2ban makes is written to its log as a NOTICE line:

log
2026-10-25 18:36:11,027 fail2ban.actions        [1997]: NOTICE  [sshd] Ban 203.0.113.42
  1. The recidive filter (filter.d/recidive.conf) matches those lines and treats each one as a failure for that IP. It ignores recidive's own bans, so it can't feed itself, and Restore Ban lines from restarts don't count.
  2. When an IP reaches maxretry bans within findtime, recidive bans it with banaction_allports, so every port is closed, not only the one it attacked.

You can see recidive counting in the log:

log
2026-10-25 18:36:11,033 fail2ban.filter         [1997]: INFO    [recidive] Found 203.0.113.42 - 2026-10-25 18:36:11

Default recidive settings#

From the stock jail.conf:

/etc/fail2ban/jail.conf (stock)
[recidive]
logpath  = /var/log/fail2ban.log
banaction = %(banaction_allports)s
bantime  = 1w
findtime = 1d
Setting Value Meaning
logpath /var/log/fail2ban.log Reads fail2ban's own log file
banaction %(banaction_allports)s All-ports ban: nftables[type=allports] with the 1.1.1 Debian paths, iptables-allports in older versions
bantime 1w One-week ban
findtime 1d Bans counted over the last 24 hours
maxretry 5 (inherited from [DEFAULT]) 5 bans in a day trigger recidive
filter recidive filter.d/recidive.conf, also has journalmatch = _SYSTEMD_UNIT=fail2ban.service

recidive example: a stricter setup#

Five bans a day is lenient. With bantime = 1h in your other jails, this catches IPs banned three times in a day and locks them out for a month:

/etc/fail2ban/jail.local
[recidive]
enabled  = true
maxretry = 3
findtime = 1d
bantime  = 4w

If you also use incremental bans with bantime.maxtime in [DEFAULT], that cap applies to recidive's repeat bans too. Give recidive its own cap if it should be higher:

ini
[recidive]
enabled         = true
bantime         = 1w
bantime.maxtime = 12w

Permanent bans with recidive#

To ban repeat offenders forever:

/etc/fail2ban/jail.local
[recidive]
enabled  = true
maxretry = 3
findtime = 1d
bantime  = -1

bantime = -1 never expires and is restored after every restart from /var/lib/fail2ban/fail2ban.sqlite3. Keep in mind:

  • IPs get reassigned. A permanently banned cloud or mobile address may belong to a real customer next month. A 4-week or 12-week ban is usually enough.
  • The firewall set only grows. nftables and ipset sets handle many thousands of entries without trouble; individual iptables rules (iptables-allports) slow down with very large lists.
  • Remove a permanent ban with sudo fail2ban-client set recidive unbanip <IP>, or sudo fail2ban-client unban <IP> to clear it from every jail (unban an IP).

Requirements: log level and log target#

recidive only works if fail2ban's Ban lines reach the place it reads. Two settings in fail2ban.conf / fail2ban.local control that (fail2ban.conf reference).

loglevel must be NOTICE, INFO or DEBUG#

Ban lines are logged at NOTICE. With loglevel = WARNING or ERROR, they're never written and recidive never matches anything. The default INFO is fine.

Avoid DEBUG while recidive is enabled. The stock jail.conf warns that debug output can make fail2ban read and log its own lines in a loop.

output
$ sudo fail2ban-client get loglevel
Current logging level is 'INFO'

logtarget: file or systemd journal#

output
$ sudo fail2ban-client get logtarget
Current logging target is:
`- /var/log/fail2ban.log
  • Log file (logtarget = /var/log/fail2ban.log, the default on Debian and Ubuntu): the stock logpath works. If you moved the log, set logpath in [recidive] to the same file.
  • systemd journal (logtarget = SYSTEMD-JOURNAL, or STDOUT/STDERR under systemd): there's no file to read. Tell recidive to read the journal:
/etc/fail2ban/jail.local
[recidive]
enabled = true
backend = systemd

The recidive filter already has journalmatch = _SYSTEMD_UNIT=fail2ban.service, so no logpath is needed. Since 1.1.1, a recidive jail with backend = auto switches to the journal on its own if /var/log/fail2ban.log doesn't exist. More in backends.

The dbpurgeage note in jail.conf#

The stock jail.conf comment above [recidive] recommends raising dbpurgeage in fail2ban.conf (it suggests 648000, 7.5 days) so ban records outlive recidive's long bans. In current versions an active ban stays in the database until it has expired, so recidive bans survive restarts even with the default 1d. Raising dbpurgeage still helps if you combine recidive with bantime.increment, because it decides how long previous bans are remembered:

/etc/fail2ban/fail2ban.local
[Definition]
dbpurgeage = 7d

recidive vs bantime.increment#

Feature recidive bantime.increment
Ban scope All ports Ports of the jail that banned
Source of truth fail2ban's log (file or journal) fail2ban's database
Cross-service Yes, counts bans from every jail Only with bantime.overalljails = true
Escalation One fixed long ban Grows with every ban (1×, 2×, 4×…)

They complement each other: increment makes each jail progressively harsher, recidive closes all ports on the worst offenders. The incremental bans page has the formula and examples.

recidive not working? Checklist#

  1. Jail not enabled or not running. sudo fail2ban-client status must list recidive.
  2. Wrong log source. fail2ban logs to the journal but recidive reads a stale /var/log/fail2ban.log (or the other way round). Compare fail2ban-client get logtarget with fail2ban-client get recidive logpath; use backend = systemd for the journal.
  3. Log level too high. loglevel = WARNING hides Ban lines.
  4. Thresholds never reached. With bantime = 1h an IP can be banned at most about 24 times a day by one jail, but with bantime = 1d it can only be banned once a day, so maxretry = 5 within findtime = 1d can never trigger. Keep findtime several times longer than your normal jails' bantime.
  5. Whitelisted IP. ignoreip applies to recidive too.
  6. Banned but still connecting. The all-ports action is in place but traffic bypasses it (Docker, a different firewall). See banned but not blocked.

Test the filter against your log; it should report matches for every IP that was banned:

bash
sudo fail2ban-regex /var/log/fail2ban.log /etc/fail2ban/filter.d/recidive.conf

The filter's regexes and sample lines are on the recidive filter page.

The recidive filter reference#

The built-in filters this guide uses, exactly as fail2ban 1.1.1 ships them — with the log lines they match and a link to test them. All other built-in filters are in the filter library.

recidive fail2ban itself

The recidive filter reads fail2ban's own log and bans IPs that keep getting banned, for much longer. File: /etc/fail2ban/filter.d/recidive.conf; jail [recidive]; log on Debian/Ubuntu: /var/log/fail2ban.log.

Log lines it bans (from fail2ban's test suite):

  1. 2006-02-13 15:52:30,388 fail2ban.actions: NOTICE [sendmail] Ban 1.2.3.4
  2. 2006-02-13 15:52:30,388 fail2ban.actions[123]: NOTICE [sendmail] Ban 1.2.3.4
  3. Sep 16 00:44:55 spaceman fail2ban.actions: NOTICE [jail] Ban 10.0.0.7
Show the failregex fail2ban compiles (1 line)
failregex (1)
^(?:\[\])?\s*(?:<[^.]+\.[^.]+>\s+)?(?:\S+\s+)?(?:kernel:\s?\[ *\d+\.\d+\]:?\s+)?(?:@vserver_\S+\s+)?(?:(?:(?:\[\d+\])?:\s+[\[\(]?(?:fail2ban(?:-server|\.actions)\s*)(?:\(\S+\))?[\]\)]?:?|[\[\(]?(?:fail2ban(?:-server|\.actions)\s*)(?:\(\S+\))?[\]\)]?:?(?:\[\d+\])?:?)\s+)?(?:\[ID \d+ \S+\]\s+)?(?:\s*fail2ban\.actions\s*(?:\[\d+\])?:\s+)?NOTICE\s+\[(?!recidive\])[^\]]*\]\s+Ban\s+<HOST>

Test recidive in the browser or on the server: sudo fail2ban-regex /var/log/fail2ban.log recidive · source

FAQ#

What is the recidive jail in fail2ban?#

A stock jail that reads fail2ban's own log and bans IPs that other jails have banned repeatedly. By default an IP banned 5 times within a day (findtime = 1d) is banned on all ports for a week (bantime = 1w). It's disabled until you set enabled = true under [recidive] in jail.local.

How do I enable recidive?#

Add [recidive] with enabled = true to /etc/fail2ban/jail.local and run sudo fail2ban-client reload. If fail2ban logs to the systemd journal instead of /var/log/fail2ban.log, also set backend = systemd. Check with sudo fail2ban-client status recidive.

Why is recidive not working?#

Most often it reads the wrong place: fail2ban logs to the journal while recidive watches a file, or the reverse. Other causes are loglevel set above NOTICE, thresholds that can't be reached (findtime too short compared with your jails' bantime), and the jail not being enabled.

How do I permanently ban repeat offenders with fail2ban?#

Enable [recidive] with bantime = -1. IPs banned maxretry times within findtime by any jail are then blocked on all ports forever, and the ban survives restarts. Lift one with sudo fail2ban-client set recidive unbanip <IP>.

Does recidive ban all ports?#

Yes. It uses banaction_allports (for example nftables[type=allports] or iptables-allports), so the IP is blocked on every port, not only the service it attacked. If you change banaction in [DEFAULT], change banaction_allports to the matching firewall too.

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