Fail2ban on a VPS: Block SSH Attacks Without Locking Yourself Out

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

  1. sudo apt update && sudo apt install -y fail2ban
  2. sudo systemctl enable --now fail2ban
  3. sudo systemctl status fail2ban — confirm it is active (running)
  4. sudo fail2ban-client status — the default sshd jail 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:

SettingDefaultPractical recommendation
bantime10m1h–24h for SSH; longer for repeat offenders
findtime10mTime window in which failures are counted
maxretry53–5 for SSH
ignoreip127.0.0.1/8Add your own static IP — never ban yourself
backendautosystemd reads from journald, immune to log rotation issues

Lock down SSH without locking yourself out

  1. Whitelist your static IP first. In /etc/fail2ban/jail.local, set ignoreip = 127.0.0.1/8 ::1 203.0.113.7 (replace with your real IP) under the [sshd] section.
  2. Restart the service: sudo systemctl restart fail2ban
  3. Verify the jail is loaded: sudo fail2ban-client status sshd
  4. Test a ban manually: sudo fail2ban-client set sshd banip 198.51.100.23, then sudo fail2ban-client set sshd unbanip 198.51.100.23.
  5. Optional: set bantime.increment = true so 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 patterns
  • wordpress — a community filter matching repeated wp-login.php failures
  • recidive — 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 watch sudo tail -f /var/log/fail2ban.log for 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 in fail2ban-client status sshd.

Troubleshooting

  • Jail never bans: the logpath is wrong or logrotate truncated the file — switch to backend = systemd to read from journald instead.
  • Bans disappear after reboot: bans are in-memory by default; that is normal. Ensure systemctl enable fail2ban so the service itself survives reboots.
  • Clock drift: fail2ban is time-sensitive. Keep chrony or NTP running, or findtime windows 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