Home/Security/When fail2ban Won't Ban IPs: A 5-Minute Diagnostic Guide for Banned: 0
Securityfail2ban

When fail2ban Won't Ban IPs: A 5-Minute Diagnostic Guide for Banned: 0

When fail2ban isn't banning SSH brute-force IPs (Banned: 0), diagnose and fix the six common causes—from systemd backend and failregex through banaction—in five minutes with copy-paste commands.

When fail2ban Won't Ban IPs: A 5-Minute Diagnostic Guide for Banned: 0
Contents
  1. 1. Run these three diagnostic commands first
  2. 2. Cause ①: Can't read the logs (logpath / systemd backend)
  3. 3. Cause ②: Jail isn't running (enabled=false / jail.local not applied)
  4. 4. Cause ③: Detection isn't working (failregex mismatch)
  5. 5. Cause ④: Matching works, but bans don't (banaction mismatch)
  6. 6. Causes ⑤ and ⑥: The ignoreip trap and thresholds
  7. 7. Complete jail.local example and diagnostic flowchart
  8. Frequently asked questions (FAQ)

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.

Bash
# ① jail list — sshd ?
sudo fail2ban-client status

# ② sshd jail
sudo fail2ban-client status sshd

# ③ /Found
sudo systemctl status fail2ban
sudo journalctl -u fail2ban -e

In the status sshd output, the two numbers that matter are Total failed and Banned. Interpret them as follows.

SymptomInterpretationLikely cause
Total failed is climbing but Banned is 0Detection works, but the ban conditions aren't metignoreip (⑤), thresholds (⑥)
Banned is climbing but the attacking IP keeps connectingThe ban was decided, but the firewall rule isn't taking effectbanaction mismatch (④), already-established sessions
Total failed stays at 0Stuck at log input or regex matchinglogpath/backend (①), failregex (③)
sshd is missing from status entirelyJail is not enabledjail.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:

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

INI
# /etc/fail2ban/jail.local
[sshd]
enabled = true
backend = systemd

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

INI
# /etc/fail2ban/jail.local
[sshd]
enabled = true
Bash
# settings when (or when)
sudo fail2ban-client reload
#
sudo systemctl restart fail2ban

# sshd list check
sudo fail2ban-client status

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

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

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

INI
# /etc/fail2ban/jail.local
[sshd]
enabled = true
mode = aggressive
backend = systemd
Bash
sudo apt update && sudo apt install --only-upgrade fail2ban   # Debian/Ubuntu
sudo dnf upgrade fail2ban # RHEL stack

5. 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].

INI
# /etc/fail2ban/jail.local
[DEFAULT]
# firewalld (RHEL 9 )
banaction = firewallcmd-rich-rules
# ufw (Ubuntu)
# banaction = ufw
# iptables
# banaction = iptables-multiport
# nftables
# banaction = nftables-multiport

Verify — Check whether the ban rules actually landed.

Bash
sudo iptables -L -n | grep f2b           # iptables
sudo nft list ruleset | grep f2b         # nftables
sudo firewall-cmd --list-all             # firewalld

If 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."

INI
[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.

INI
[sshd]
findtime = 10m
maxretry = 5
bantime  = 1h

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

Bash
sudo fail2ban-client set sshd banip 1.2.3.4
sudo iptables -L -n | grep 1.2.3.4 # or nft / firewall-cmd

If 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 forgot backend = systemd is what built this habit.

7. Complete jail.local example and diagnostic flowchart

Copy-paste this and uncomment the line that matches your environment.

INI
# /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    = ssh

6-cause diagnostic flowchart

CODE
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/maxretry

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

Verification
✦ ✦ ✦
Sources and verification

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.

Published · ·Updated ·
Topic-related official docs (auto-linked · not per-claim sources)Linux man pages ↗OWASP documentation ↗

Comments

Be the first to comment.