Any server with a public IP receives a constant stream of login attempts from bots. Fail2Ban watches log files for repeated failures and temporarily bans the offending IP addresses with a firewall rule. It’s lightweight, takes five minutes to set up, and dramatically cuts the noise.

1. Install

sudo apt update
sudo apt install -y fail2ban

On AlmaLinux and Rocky, it’s in EPEL: sudo dnf install epel-release && sudo dnf install fail2ban.

2. Create jail.local — never edit jail.conf

Fail2Ban’s defaults live in /etc/fail2ban/jail.conf, which package updates overwrite. Put your settings in /etc/fail2ban/jail.local:

[DEFAULT]
# Never ban yourself: localhost plus your own static IPs
ignoreip = 127.0.0.1/8 ::1 198.51.100.25

# Ban for 1 hour after 5 failures within 10 minutes
bantime  = 1h
findtime = 10m
maxretry = 5

# Repeat offenders get progressively longer bans
bantime.increment = true
bantime.maxtime   = 1w

# Read logs from the systemd journal (Debian 12+ has no /var/log/auth.log by default)
backend = systemd

[sshd]
enabled = true
port    = ssh

If SSH runs on a different port, set port = 2222. If your system does have /var/log/auth.log and you prefer file-based reading, use backend = auto instead.

3. Choose the ban action

By default, Fail2Ban bans with iptables. On a server using UFW or nftables, match it:

[DEFAULT]
banaction = ufw
# or
banaction = nftables-multiport

Recent Debian packages already default to nftables.

4. Start and check

sudo systemctl enable --now fail2ban
sudo fail2ban-client status
sudo fail2ban-client status sshd
Status for the jail: sshd
|- Filter
|  |- Currently failed: 3
|  |- Total failed:     218
|  `- Journal matches:  _SYSTEMD_UNIT=ssh.service + _COMM=sshd
`- Actions
   |- Currently banned: 7
   |- Total banned:     41
   `- Banned IP list:   …

If the jail fails to start, check the log: sudo tail -n 50 /var/log/fail2ban.log. A common error on Ubuntu 24.04+ is the journal match using sshd.service when the unit is called ssh.service; recent Fail2Ban versions handle both.

5. Unban an IP (including your own)

sudo fail2ban-client set sshd unbanip 198.51.100.25
sudo fail2ban-client unban --all

Locked out completely? Log in through your VPS provider’s VNC console and run the unban command.

Protect Nginx and web apps

Fail2Ban ships filters for many services. Useful Nginx jails in jail.local:

[nginx-http-auth]
enabled = true
logpath = /var/log/nginx/error.log
backend = auto

[nginx-botsearch]
enabled  = true
logpath  = /var/log/nginx/access.log
backend  = auto
maxretry = 10
  • nginx-http-auth bans failed HTTP basic-auth logins.
  • nginx-botsearch bans scanners probing for things like /phpmyadmin or /wp-login.php on sites that don’t have them.

Web-server logs are files, so those jails need backend = auto even if the default is systemd. For WordPress logins, see Fail2Ban for WordPress, and for more Nginx jails see Fail2Ban with Nginx.

Write a custom filter

Suppose your app logs LOGIN FAILED ip=203.0.113.9. Create /etc/fail2ban/filter.d/myapp.conf:

[Definition]
failregex = LOGIN FAILED ip=<HOST>
ignoreregex =

Test it against the log before enabling:

sudo fail2ban-regex /var/log/myapp.log /etc/fail2ban/filter.d/myapp.conf

Then add a jail:

[myapp]
enabled  = true
logpath  = /var/log/myapp.log
backend  = auto
maxretry = 5

Useful commands

sudo fail2ban-client status                 # list jails
sudo fail2ban-client status sshd            # details for one jail
sudo fail2ban-client get sshd banip         # banned IPs
sudo fail2ban-client set sshd banip 192.0.2.66   # ban manually
sudo fail2ban-client reload                 # apply config changes
sudo zgrep 'Ban ' /var/log/fail2ban.log* | wc -l   # how many bans so far

What Fail2Ban doesn’t do

  • It reacts to log entries, so it can’t stop a flood or a distributed attack where each IP tries once or twice.
  • It’s not a replacement for strong authentication. The real fix for SSH brute force is key-only authentication; Fail2Ban then mainly keeps logs clean.
  • Large bans lists in iptables can slow packet processing; long-term blocking of whole networks is better done with ipsets or a firewall upstream.

Alternatives

CrowdSec works on the same principle but shares attacker reputation across its user community and blocks known-bad IPs before they even try. See installing CrowdSec.

A hardened baseline

  1. Create SSH keys and disable passwords.
  2. Enable UFW with only the ports you need.
  3. Run Fail2Ban for SSH and your web apps.
  4. Keep the system patched with unattended upgrades.

That combination stops the overwhelming majority of automated attacks against a Linux VPS.

  • Fail2Ban
  • Security
  • SSH