When fail2ban Won't Ban IPs: A 5-Minute Diagnostic Guide for Banned: 0
SSH logs are filling up with hundreds of Failed password lines, but fail2ban-client status sshd shows Banned: 0. "I installed it and enabled it—why isn't it blocking?" This is a 5-minute diagnostic manual for exactly that situation. In practice, fail2ban "not blocking" usually comes down to one of the six causes below. Follow along by copy-pasting the commands as-is.
1. Run these three diagnostic commands first
Before you guess at the cause, find out where the chain broke. Run these three in order.
# ① jail list — sshd ?
sudo fail2ban-client status
# ② sshd jail
sudo fail2ban-client status sshd
# ③ /Found
sudo systemctl status fail2ban
sudo journalctl -u fail2ban -eIn the status sshd output, the two numbers that matter are Total failed and Banned. Interpret them as follows.
| Symptom | Interpretation | Likely cause |
|---|---|---|
| Total failed is climbing but Banned is 0 | Detection works, but the ban conditions aren't met | ignoreip (⑤), thresholds (⑥) |
| Banned is climbing but the attacking IP keeps connecting | The ban was decided, but the firewall rule isn't taking effect | banaction mismatch (④), already-established sessions |
| Total failed stays at 0 | Stuck at log input or regex matching | logpath/backend (①), failregex (③) |
sshd is missing from status entirely | Jail is not enabled | jail.local not applied (②) |
Hold this branching in your head and the rest is fast. Jump to the matching section below.
2. Cause ①: Can't read the logs (logpath / systemd backend)
Symptom: Total failed stays at 0. journalctl -u fail2ban shows a message like Have not found any log file for sshd jail, or there's no error and Total failed simply stays at 0.
This is the #1 most common cause. Ubuntu 22.04/24.04 and the RHEL 9 family include rsyslog in the default install, so logs go to /var/log/auth.log (Ubuntu) or /var/log/secure (RHEL). Debian 12 and later don't install rsyslog by default, so there is no /var/log/auth.log; the same goes for minimal images, servers with rsyslog removed, and containers. In those environments, if fail2ban is pointed at a file that doesn't exist, it reads nothing.
Diagnosis:
# fail2ban
sudo fail2ban-client get sshd logpath
# path
ls -l /var/log/auth.log # Debian/Ubuntu
ls -l /var/log/secure # RHEL/CentOS/Rocky
# journald SSH
sudo journalctl -u ssh -n 50 # Ubuntu ( ssh)
sudo journalctl -u sshd -n 50 # RHEL ( sshd)Fix: If the file log is empty and logs only exist in journald, switch the backend to systemd.
# /etc/fail2ban/jail.local
[sshd]
enabled = true
backend = systemdTo apply it globally, you can put backend = systemd in the [DEFAULT] block instead. After applying, verify the same way: check whether Total failed is climbing.
3. Cause ②: Jail isn't running (enabled=false / jail.local not applied)
Symptom: sshd is missing from the jail list in fail2ban-client status.
Diagnosis & fix: Never edit jail.conf directly. A package update will overwrite it. Put all custom settings in /etc/fail2ban/jail.local.
# /etc/fail2ban/jail.local
[sshd]
enabled = true# settings when (or when)
sudo fail2ban-client reload
#
sudo systemctl restart fail2ban
# sshd list check
sudo fail2ban-client statusreload sometimes fails to pick up jail additions/removals. If you just turned enabled on and it still doesn't show up in the list, don't hesitate—just restart.
4. Cause ③: Detection isn't working (failregex mismatch)
Symptom: Logs are being read, but Total failed is 0. Since OpenSSH 9.8, connections are handled by the sshd-session process, so the process name in the journal changed from sshd to sshd-session. As a result, an sshd filter that does not account for this (upstream fail2ban before 1.1.1) can miss these logs with the systemd backend.
Diagnosis — Count matching lines yourself with fail2ban-regex.
# With a file log backend
sudo fail2ban-regex /var/log/auth.log /etc/fail2ban/filter.d/sshd.conf
# With the systemd backend
sudo fail2ban-regex systemd-journal /etc/fail2ban/filter.d/sshd.confIf matched is 0 in the Lines: ... matched at the end of the output, the regex isn't matching.
Fix: On OpenSSH 9.8 or later, upgrade fail2ban to 1.1.1 or newer, or to a distro package that backports this fix (check with fail2ban-client version). If the messages simply don't match the filter's types, try raising the filter mode to aggressive.
# /etc/fail2ban/jail.local
[sshd]
enabled = true
mode = aggressive
backend = systemdsudo apt update && sudo apt install --only-upgrade fail2ban # Debian/Ubuntu
sudo dnf upgrade fail2ban # RHEL stack5. Cause ④: Matching works, but bans don't (banaction mismatch)
Symptom: Total failed is climbing and Banned is climbing too, but the attacker keeps trying to connect. The firewall backend is mismatched. On systems where nftables is the default, using an iptables-based banaction puts rules in the wrong place and they have no effect.
Fix — Set the banaction that matches your environment in [DEFAULT].
# /etc/fail2ban/jail.local
[DEFAULT]
# firewalld (RHEL 9 )
banaction = firewallcmd-rich-rules
# ufw (Ubuntu)
# banaction = ufw
# iptables
# banaction = iptables-multiport
# nftables
# banaction = nftables-multiportVerify — Check whether the ban rules actually landed.
sudo iptables -L -n | grep f2b # iptables
sudo nft list ruleset | grep f2b # nftables
sudo firewall-cmd --list-all # firewalldIf the rules don't show up, the banaction is wrong.
6. Causes ⑤ and ⑥: The ignoreip trap and thresholds
Cause ⑤ ignoreip: While adding test IPs or internal ranges, if you put something as broad as 0.0.0.0/0, everything is effectively excluded from bans. Or you add your own public IP and then test from that IP, and mistakenly conclude that "it isn't blocking."
[DEFAULT]
ignoreip = 127.0.0.1/8 ::1 10.0.0.0/8 # ,Cause ⑥ thresholds: findtime = 10m, maxretry = 5 means "ban after 5 failures within 10 minutes." If the attack comes in slowly, it never hits the threshold and never gets banned.
[sshd]
findtime = 10m
maxretry = 5
bantime = 1hFor testing, lower them—e.g. maxretry = 2, bantime = 5m—so you can verify quickly.
Isolating the action — The best trick for separating detection from the ban action is a manual ban.
sudo fail2ban-client set sshd banip 1.2.3.4
sudo iptables -L -n | grep 1.2.3.4 # or nft / firewall-cmdIf a manual banip works but automatic bans don't → it's a detection problem (①②③). If even the manual ban doesn't work → it's a banaction problem (④).
A practitioner's note: Whenever I set up a new server, I always verify the action first with
set sshd banip. Eighty percent of a 5-minute debug is just splitting "detection vs. ban"—and one manual banip line cuts that in half immediately. Two hours of wandering on Ubuntu 24.04 because I forgotbackend = systemdis what built this habit.
7. Complete jail.local example and diagnostic flowchart
Copy-paste this and uncomment the line that matches your environment.
# /etc/fail2ban/jail.local
[DEFAULT]
backend = systemd
ignoreip = 127.0.0.1/8 ::1
findtime = 10m
maxretry = 5
bantime = 1h
#
banaction = firewallcmd-rich-rules # firewalld
# banaction = ufw # ufw
# banaction = iptables-multiport # iptables
# banaction = nftables-multiport # nftables
[sshd]
enabled = true
mode = aggressive
backend = systemd
port = ssh6-cause diagnostic flowchart
fail2ban-client status sshd
├─ sshd missing from status? ─────────→ ② jail.local enabled=true + restart
├─ Total failed = 0 ?
│ ├─ get sshd logpath / journalctl empty? → ① backend=systemd
│ └─ fail2ban-regex matched=0 ? → ③ mode=aggressive / upgrade
└─ Total failed↑ but Banned=0 or still not blocked?
├─ set sshd banip 1.2.3.4 also not blocked? → ④ change banaction
├─ your IP / a broad range in ignoreip? → ⑤ clean up ignoreip
└─ threshold not reached? → ⑥ check findtime/maxretryPrevention checklist: ⬜ backend matches the environment (journald or not) ⬜ settings only in jail.local ⬜ banaction matches the firewall backend ⬜ ignoreip kept minimal ⬜ package is up to date ⬜ after changes, verify with fail2ban-regex and a manual banip
Frequently asked questions (FAQ)
Q. Total failed is climbing but Banned is 0.
A. Detection is fine, but the ban conditions aren't being met. Check cause ⑤ (ignoreip) and cause ⑥ (findtime/maxretry). If Banned climbs but connections continue, it's cause ④ (banaction); isolating the action with fail2ban-client set sshd banip is the fast path.
Q. I reloaded but the config didn't take effect.
A. reload sometimes doesn't pick up jail additions/removals. Do a full restart with sudo systemctl restart fail2ban and confirm with fail2ban-client status.
Q. It says it's banned, but SSH still connects.
A. Two things. Existing sessions stay up even after the firewall rule is added (only new connections are blocked). And if banaction doesn't match the actual firewall backend (nftables/firewalld/ufw), the rules are inert. Confirm the rules exist with iptables -L -n | grep f2b or equivalent.
Q. /var/log/auth.log is missing or empty.
A. This happens where rsyslog isn't installed, such as Debian 12+ or minimal installs (the default Ubuntu 22.04/24.04 install does include rsyslog). Confirm the logs are in the journal with journalctl -u ssh and switch to backend = systemd.
Q. Bans disappear after a reboot.
A. fail2ban stores bans in a sqlite DB (dbfile, default /var/lib/fail2ban/fail2ban.sqlite3) and restores bans that haven't expired when it restarts. If they disappear, check whether dbfile in fail2ban.local was changed to :memory:, or whether dbpurgeage (default 1 day) is shorter than bantime so the records were purged. For long bans, set dbpurgeage longer than bantime; use bantime = -1 for permanent bans and incremental banning (bantime.increment = true) for repeat offenders.
Sources and checks vary by article. Only articles with a recorded check date, scope and sources below were checked, and only within that scope. For other articles, verify against the current docs for your version before applying anything, and email us about errors. Editorial policy · email us
This article has no recorded human check (date and scope) yet.
Comments
Be the first to comment.