Fail2ban can look healthy, with the service active and jails listed, while it never bans a single address. Almost always the cause is one of a handful of mismatches: it reads the wrong log, its filter does not fit the log lines, or its ban goes into a firewall your server does not use. This guide checks each one in order, from quickest to most involved.
Before you start
- A Linux VPS running Ubuntu 22.04 or 24.04, Debian 12, or AlmaLinux or Rocky Linux 8 or 9, with Fail2ban installed.
- A user with
sudo. - Your own IP in
ignoreipin/etc/fail2ban/jail.local, so a fix that suddenly starts working does not ban you. - A second way in. If a ban does catch you, the browser console in your VPS provider's control panel still works.
Step 1: Is the service and jail running?
sudo systemctl status fail2ban --no-pager
sudo fail2ban-client statusIf the service failed, read why:
sudo journalctl -u fail2ban -n 50 --no-pagerIf your jail is missing from Jail list, it is not enabled, or it failed to start. Check the config with sudo fail2ban-client -t. A common message is Have not found any log file for sshd jail, which leads straight to step 2.
Step 2: Does the jail read the right log?
Look at what the jail is watching:
sudo fail2ban-client status sshdThe Filter section shows either File list: (a log file) or Journal matches: (the systemd journal).
- Debian 12 does not install rsyslog by default, so
/var/log/auth.logdoes not exist. A file-basedsshdjail has nothing to read. Fix it with the journal backend. - Ubuntu writes
/var/log/auth.log, and AlmaLinux and Rocky write/var/log/secure. File-based jails work there, but the journal works too. - Apps with their own logs, such as Nginx sites with per-site files, need the exact
logpaththat the app writes to.
Switch SSH to the journal in /etc/fail2ban/jail.local:
[sshd]
enabled = true
backend = systemdOn Debian and Ubuntu, the journal backend needs python3-systemd:
sudo apt install python3-systemd
sudo fail2ban-client -t && sudo fail2ban-client reloadStep 3: Does the filter match the log lines?
A jail can read the right log and still match nothing. Test it with fail2ban-regex, which reports matches without banning.
For a log file:
sudo fail2ban-regex /var/log/auth.log /etc/fail2ban/filter.d/sshd.confFor the journal:
sudo fail2ban-regex systemd-journal sshdLook at the Lines: summary at the end. On a public server, matched should be well above zero. If it is zero:
- You tested a quiet log. Generate a failure from another network and test again.
- The log format changed, for example after an OpenSSH upgrade. Update Fail2ban to the current distribution package, which ships updated filters.
- Timestamps are not recognised. Check that
Date template hitsis above zero.
Tip: The sshd filter has a mode setting (normal, ddos, extra, aggressive). aggressive catches more kinds of failed connection. Set it with mode = aggressive in the [sshd] section if normal mode misses real attacks.
Step 4: Does the ban reach your firewall?
If fail2ban-client status sshd shows banned addresses but they can still connect, the ban is not landing in the firewall that actually filters traffic.
Check which action Fail2ban uses:
sudo fail2ban-client -d | grep -i "banaction\|actionban" | head
grep -r banaction /etc/fail2ban/jail.local /etc/fail2ban/jail.d/Match it to your firewall:
- firewalld (AlmaLinux and Rocky): use
firewallcmd-rich-rulesorfirewallcmd-ipset. Thefail2ban-firewalldpackage sets this for you. - nftables only: use
nftables-multiport(ornftables). - UFW:
iptables-multiportusually works because UFW uses the same backend, or use theufwaction.
Set it in jail.local:
[DEFAULT]
banaction = firewallcmd-rich-rules
banaction_allports = firewallcmd-rich-rulesThen confirm the bans appear in the ruleset. For firewalld, sudo firewall-cmd --list-rich-rules. For nftables, sudo nft list ruleset | grep -A5 f2b. See Use Fail2ban with firewalld or nftables for more detail.
Warning: Never run two firewall managers at once, such as UFW and firewalld. Bans and allow rules end up in different places, and you can lock out SSH. Pick one and disable the other.
Step 5: Is the port right?
Fail2ban only blocks the ports in the jail's port setting. If SSH runs on 2222 and the jail says port = ssh (22), attackers are "banned" on a port nobody uses and keep hitting 2222.
[sshd]
enabled = true
port = 2222
backend = systemdReload, then check the rule names the right port in your firewall output. See Change the SSH port safely.
Step 6: Smaller causes
- The attacker is in
ignoreip: a wide range like0.0.0.0/0or a big provider block inignoreipexempts attackers too. Keep it to your own addresses. maxretryandfindtimetoo loose: a slow attack, one try every few minutes, may never hitmaxretrywithinfindtime. Widenfindtimeor enable therecidivejail.- Traffic arrives through a proxy: web jails see the proxy's address unless real IP logging is set up.
- Docker containers: traffic to published container ports can bypass rules in the normal
INPUTchain, so bans may not apply to it.
Check it works
From another network, such as a phone hotspot, make several failed SSH logins. Then:
sudo fail2ban-client status sshd
sudo tail -n 20 /var/log/fail2ban.logYou should see Found lines, a Ban line, the address in the banned list, and new connections from it timing out. Unban it afterwards with sudo fail2ban-client set sshd unbanip IP.
Troubleshooting
Have not found any log file: the path is wrong or the file does not exist. Usebackend = systemd, or fixlogpath.fail2ban-regexmatches but no bans: look infail2ban.logforERRORlines from the action. A missingipsetornftcommand, or a firewall that is not running, makes the ban fail.- Banned in Fail2ban but still connecting: wrong
banactionfor your firewall, or wrongport. - Everything worked before an OS upgrade: check the backend and package versions again. Log locations and default actions can change between releases.
Next steps
- Set sensible ban timings: Configure Fail2ban jails.
- Lift a ban that caught the wrong person: Check banned IPs and unban an address.
- Compare a different approach: CrowdSec vs Fail2ban.
- Still stuck? Open a ticket with the output of
fail2ban-client status.
