Any Linux VPS with SSH exposed to the internet starts collecting brute-force attempts within hours of its first boot. A typical fresh instance sees thousands of failed logins per day from scripted dictionaries and botnets. Fail2ban is the standard, low-cost defense: it watches log files, counts failures per source IP, and temporarily bans offenders via the firewall. This guide walks through a safe setup for SSH and web logins — including the whitelisting and testing steps that keep you from locking yourself out of your own server.
How fail2ban works
Fail2ban runs a daemon that tails log files (by default /var/log/auth.log on Debian and Ubuntu), matches entries against regex filters, and increments a counter per source IP. When the counter crosses maxretry within findtime seconds, it applies a jail action — usually adding a firewall rule that drops traffic from that IP for bantime seconds. It is reactive, not preventive: it does not stop the first few attempts, but it makes sustained brute force impractical and keeps your logs readable.
Install and enable
sudo apt update && sudo apt install -y fail2bansudo systemctl enable --now fail2bansudo systemctl status fail2ban— confirm it is active (running)sudo fail2ban-client status— the defaultsshdjail should be listed
On Ubuntu 22.04+, Debian 12, and newer images, fail2ban detects the host firewall backend automatically and uses nftables when the system firewall is nftables-based (which includes default ufw on modern Ubuntu). It coexists with ufw cleanly; just avoid enabling both the iptables and nftables backends at the same time.
The config model: jail.conf vs jail.local
Never edit /etc/fail2ban/jail.conf directly — package updates overwrite it. Put your overrides in /etc/fail2ban/jail.local. The settings that matter most:
| Setting | Default | Practical recommendation |
|---|---|---|
bantime | 10m | 1h–24h for SSH; longer for repeat offenders |
findtime | 10m | Time window in which failures are counted |
maxretry | 5 | 3–5 for SSH |
ignoreip | 127.0.0.1/8 | Add your own static IP — never ban yourself |
backend | auto | systemd reads from journald, immune to log rotation issues |
Lock down SSH without locking yourself out
- Whitelist your static IP first. In
/etc/fail2ban/jail.local, setignoreip = 127.0.0.1/8 ::1 203.0.113.7(replace with your real IP) under the[sshd]section. - Restart the service:
sudo systemctl restart fail2ban - Verify the jail is loaded:
sudo fail2ban-client status sshd - Test a ban manually:
sudo fail2ban-client set sshd banip 198.51.100.23, thensudo fail2ban-client set sshd unbanip 198.51.100.23. - Optional: set
bantime.increment = trueso repeat offenders get progressively longer bans.
Two extra steps make SSH much quieter: switch to key-based authentication and set PasswordAuthentication no in /etc/ssh/sshd_config. Fail2ban then only needs to cover the remaining fallback paths, and the volume of log noise drops sharply. If you administer many servers from one office IP, keep that IP in ignoreip on every host.
Protect web logins too
SSH is not the only brute-force target. WordPress, phpMyAdmin, and other web logins get hammered as well, and fail2ban ships jails for the common cases:
nginx-http-auth— bans IPs that fail HTTP basic auth repeatedly (log:/var/log/nginx/error.log)apache-badbots/nginx-botsearch— bans known bad bots and bogus request patternswordpress— a community filter matching repeatedwp-login.phpfailuresrecidive— a meta-jail that gives longer bans to IPs banned in multiple other jails
Enable any of these by adding the jail name to enabled in jail.local and pointing logpath at the real log file, then verify with sudo fail2ban-client status nginx-http-auth.
Testing without drama
- List banned IPs:
sudo fail2ban-client status sshd - Verify the firewall action end to end with a manual
banip/unbanip. - Simulate failures: attempt three wrong passwords (
ssh baduser@your-server) and watchsudo tail -f /var/log/fail2ban.logfor the ban event. - If you ban yourself: connect through your provider’s out-of-band console (VNC or browser terminal) and run
sudo fail2ban-client unbanip YOUR_IP, or flush the chain directly — the chain name is shown infail2ban-client status sshd.
Troubleshooting
- Jail never bans: the
logpathis wrong or logrotate truncated the file — switch tobackend = systemdto read from journald instead. - Bans disappear after reboot: bans are in-memory by default; that is normal. Ensure
systemctl enable fail2banso the service itself survives reboots. - Clock drift: fail2ban is time-sensitive. Keep chrony or NTP running, or
findtimewindows and ban expiry misbehave. - High CPU from fail2ban: usually log scanning over huge files — rotate logs and exclude noisy services from filters.
Fail2ban is not a replacement for key-only SSH and a properly configured firewall — it is the layer that absorbs the noise. Combined with automatic security updates, it covers the two most common ways VPSes get compromised. And when you pick a host, compare providers on our comparison table for the basics, then see the full specs and pricing before committing — a provider with out-of-band console access turns “I banned myself” into a five-minute fix instead of a support ticket.

Leave a Reply
You must be logged in to post a comment.