fail2ban reads log files for failed logins and blocks the source address in your firewall. On many fresh installs it bans nothing at all, because the sshd jail is watching a log file the system no longer writes.
1. Install it
Ubuntu and Debian use apt and ufw. AlmaLinux, Rocky, and other RHEL-based distros use dnf and firewalld.
$ sudo apt update && sudo apt install -y fail2ban$ sudo dnf install -y epel-release $ sudo dnf install -y fail2ban fail2ban-firewalld
fail2ban is not in the AlmaLinux, Rocky, or RHEL base repositories, so EPEL comes first. The fail2ban-firewalld package sends bans through firewalld, which is the default firewall on those distros. Ubuntu and Debian need no extra package.
Check what you got with fail2ban-client --version. The current release is 1.1.0, from April 2024, and everything below works on 0.11 and later.
2. Put your settings in jail.local
The header of /etc/fail2ban/jail.conf tells you not to edit that file, because a package update will overwrite it. Your changes go in /etc/fail2ban/jail.local, which is read afterwards and wins.
[DEFAULT] bantime = 1h findtime = 10m maxretry = 5 ignoreip = 127.0.0.1/8 ::1 203.0.113.25 2001:db8:1234::/64 [sshd] enabled = true
The shipped defaults are bantime = 10m, findtime = 10m, maxretry = 5: five failures inside ten minutes gets an address banned for ten minutes. Ten minutes is a short ban. An hour is a more useful default.
ignoreip accepts IPv4 addresses, IPv6 addresses, CIDR ranges, and hostnames, separated by spaces. Put your own home or office address there. The server's own addresses are already ignored, because ignoreself defaults to true.
Every jail in jail.conf ships disabled, so enabled = true under [sshd] is the line that turns SSH protection on. Some distro packages enable it for you; setting it again does no harm.
3. Check which log the jail reads
This is where most installs quietly fail.
The sshd jail takes its log path from a paths file that ships with fail2ban and differs per distro. On Ubuntu and Debian it resolves to /var/log/auth.log. On AlmaLinux, Rocky, and RHEL it resolves to /var/log/secure. Some distro packages point the jail at the journal instead. Check yours:
$ sudo fail2ban-client get sshd logpath $ sudo systemctl status fail2ban
Debian 12 and later no longer install rsyslog, so /var/log/auth.log is never created and sshd logs to the journal. When the jail is pointed at a file that does not exist, fail2ban refuses to start it:
ERROR No file(s) found for glob /var/log/secure ERROR Failed during configuration: Have not found any log file for sshd jail
The fix is to move that jail to the journal:
[sshd] enabled = true backend = systemd
Do not add logpath next to it. The systemd backend does not accept a log path, and takes its journalmatch from the filter instead.
Do not set the systemd backend for every jail
The journal always exists, so fail2ban can no longer warn you that a log file is missing. A jail for a service that writes its own log file will then find no failures and give no reason. Set the backend per jail, on the jails whose service you know logs to the journal.
4. Start it and confirm it bans
$ sudo fail2ban-client -t $ sudo systemctl enable --now fail2ban $ sudo fail2ban-client status $ sudo fail2ban-client status sshd
-t checks the configuration without touching the running service. status lists the jails that started. status sshd shows the jail's failure and ban counts, plus the addresses banned right now. On a public IPv4 address the failure count starts climbing within minutes.
If the jail is up but nothing is ever found, test the filter against your real log:
$ sudo fail2ban-regex systemd-journal sshd $ sudo fail2ban-regex /var/log/auth.log sshd
Use the first form for the systemd backend and the second for a file. The output counts matched and missed lines. Plenty of failed logins in the log and zero matches usually means the jail is reading the wrong source. The filter itself is rarely the problem.
5. Ban repeat offenders for longer
The same networks come back. bantime.increment multiplies the ban every time an address is banned again:
[DEFAULT] bantime.increment = true bantime.factor = 1 bantime.maxtime = 4w
With the default formula the ban grows by 1, 2, 4, 8, 16 times the starting bantime, and bantime.maxtime caps it. Bans and ban counts are stored in /var/lib/fail2ban/fail2ban.sqlite3, so they survive a service restart and a reboot.
That history is trimmed by dbpurgeage, which defaults to one day. Raise it if you want the increment to have a longer memory:
[DEFAULT] dbpurgeage = 30d
6. Unban an address
When you ban yourself, log in through the browser console in your client area and run:
$ sudo fail2ban-client banned $ sudo fail2ban-client set sshd unbanip 203.0.113.25 $ sudo fail2ban-client unban 203.0.113.25 $ sudo fail2ban-client unban --all
banned prints every banned address, grouped by jail. set sshd unbanip clears one address from one jail, unban clears it from every jail and from the database, and unban --all clears everything. Then add the address to ignoreip so it cannot happen twice.
What fail2ban does not cover
fail2ban bans by adding firewall rules, and on Ubuntu and Debian those land where ufw works. Docker documents that publishing a container port routes its traffic in the nat table, so packets are diverted before they reach the INPUT and OUTPUT chains ufw uses. A ban therefore has no effect on traffic to a published container port. Your sshd jail is fine, because sshd runs on the host. A jail watching a containerised web app will report bans that change nothing. Publish container ports to 127.0.0.1 and put a reverse proxy in front instead.
fail2ban also raises the cost of password guessing without removing it. Key-only SSH removes it. Use keys, and keep fail2ban for the log noise and for the services that do not have an equivalent.
Quick reference
| Task | Command |
|---|---|
| Test the config before restarting | fail2ban-client -t |
| List running jails | fail2ban-client status |
| Failures and bans for a jail | fail2ban-client status sshd |
| Which log a jail reads | fail2ban-client get sshd logpath |
| Test the filter against a log | fail2ban-regex systemd-journal sshd |
| List every banned address | fail2ban-client banned |
| Unban across all jails | fail2ban-client unban 203.0.113.25 |
| Apply config changes | fail2ban-client reload |
Where to go next
fail2ban writes rules into your firewall, so set the firewall to default deny first with our firewall guide. For the rest of the baseline, the first ten minutes on a new VPS covers keys, the sudo user, and automatic security updates. DDoS protection is included on all D4 Networks plans.