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.
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.
[recidive]
enabled = truesudo fail2ban-client reload
sudo fail2ban-client status recidiveWhat 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:
2026-10-25 18:36:11,027 fail2ban.actions [1997]: NOTICE [sshd] Ban 203.0.113.42- 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, andRestore Banlines from restarts don't count. - When an IP reaches
maxretrybans withinfindtime, recidive bans it withbanaction_allports, so every port is closed, not only the one it attacked.
You can see recidive counting in the log:
2026-10-25 18:36:11,033 fail2ban.filter [1997]: INFO [recidive] Found 203.0.113.42 - 2026-10-25 18:36:11Default recidive settings#
From the stock jail.conf:
[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:
[recidive]
enabled = true
maxretry = 3
findtime = 1d
bantime = 4wIf 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:
[recidive]
enabled = true
bantime = 1w
bantime.maxtime = 12wPermanent bans with recidive#
To ban repeat offenders forever:
[recidive]
enabled = true
maxretry = 3
findtime = 1d
bantime = -1bantime = -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>, orsudo 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.
$ sudo fail2ban-client get loglevel
Current logging level is 'INFO'logtarget: file or systemd journal#
$ 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 stocklogpathworks. If you moved the log, setlogpathin[recidive]to the same file. - systemd journal (
logtarget = SYSTEMD-JOURNAL, orSTDOUT/STDERRunder systemd): there's no file to read. Tell recidive to read the journal:
[recidive]
enabled = true
backend = systemdThe 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:
[Definition]
dbpurgeage = 7drecidive 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#
- Jail not enabled or not running.
sudo fail2ban-client statusmust listrecidive. - Wrong log source. fail2ban logs to the journal but recidive reads a stale
/var/log/fail2ban.log(or the other way round). Comparefail2ban-client get logtargetwithfail2ban-client get recidive logpath; usebackend = systemdfor the journal. - Log level too high.
loglevel = WARNINGhidesBanlines. - Thresholds never reached. With
bantime = 1han IP can be banned at most about 24 times a day by one jail, but withbantime = 1dit can only be banned once a day, somaxretry = 5withinfindtime = 1dcan never trigger. Keepfindtimeseveral times longer than your normal jails'bantime. - Whitelisted IP.
ignoreipapplies to recidive too. - 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:
sudo fail2ban-regex /var/log/fail2ban.log /etc/fail2ban/filter.d/recidive.confThe 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):
2006-02-13 15:52:30,388 fail2ban.actions: NOTICE [sendmail] Ban 1.2.3.42006-02-13 15:52:30,388 fail2ban.actions[123]: NOTICE [sendmail] Ban 1.2.3.4Sep 16 00:44:55 spaceman fail2ban.actions: NOTICE [jail] Ban 10.0.0.7
Show the failregex fail2ban compiles (1 line)
^(?:\[\])?\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.