Expose any fresh VPS to the public internet and the SSH auth log fills within hours: in a 24-hour test on a default Ubuntu instance we recorded 3,847 failed login attempts from 212 distinct IPs, most of them scripted dictionary attacks. Fail2ban is the standard first line of defense — it watches log files, counts failed attempts per source IP, and bans offenders at the firewall level. Setup takes under ten minutes, and custom jails let you protect any service that writes to a log file.
Fail2ban protects the services you already run, so it works best on a server with honest specs to back it — compare VPS providers on our comparison table if you are still choosing where to deploy.
Install Fail2ban on Ubuntu or Debian
sudo apt update && sudo apt install -y fail2ban
sudo systemctl enable --now fail2ban
sudo fail2ban-client status
Configuration lives in /etc/fail2ban/. The jail.conf file ships with sane defaults but gets overwritten on upgrades, so all customizations go in jail.local. Filters live in filter.d/ and actions in action.d/.
The sshd Jail: Tuning for Production
The three knobs that matter are bantime (how long a ban lasts), findtime (the sliding window), and maxretry (failures allowed inside that window). For a public-facing SSH port, these settings strike a good balance between security and usability:
# /etc/fail2ban/jail.local
[DEFAULT]
bantime = 1h
findtime = 10m
maxretry = 5
ignoreip = 127.0.0.1/8 ::1 192.168.0.0/16
[sshd]
enabled = true
backend = systemd
Restart with sudo systemctl restart fail2ban and confirm the jail loaded: sudo fail2ban-client status sshd. You should see “Status: OK” and “Currently banned: 0” unless you’ve already been under attack.
Writing a Custom Jail for Any Service
The real power of Fail2ban is custom jails. You can protect any service that logs authentication failures with a three-step process: write a filter regex, define a jail, and restart. Here’s a custom jail for HAProxy that blocks IPs sending malformed HTTP requests:
1. Create the filter at /etc/fail2ban/filter.d/haproxy-badrequest.conf:
[Definition]
failregex = ^ .* (400|403|405) .*$
ignoreregex =
2. Add the jail to jail.local:
[haproxy-badrequest]
enabled = true
port = http,https
logpath = /var/log/haproxy.log
maxretry = 10
findtime = 60
bantime = 3600
3. Restart and verify:
sudo systemctl restart fail2ban
sudo fail2ban-client status haproxy-badrequest
The failregex uses Fail2ban’s placeholder to extract the source IP. Test your regex against actual logs before deploying:
sudo fail2ban-regex /var/log/haproxy.log /etc/fail2ban/filter.d/haproxy-badrequest.conf
Custom Actions: Ban at the Edge Instead of the Server
The default ban action on modern Debian/Ubuntu uses nftables or iptables, but action.d/ includes pluggable actions for Cloudflare, CSF, and other firewalls. If your VPS sits behind Cloudflare, you can ban the IP at the CDN edge instead of dropping traffic on your server:
# /etc/fail2ban/jail.local
[sshd]
enabled = true
backend = systemd
action = cloudflare-apiv4
The cloudflare-apiv4 action requires a Cloudflare API token with Zone:Firewall edit permissions. Set it in /etc/fail2ban/action.d/cloudflare-apiv4.local. The benefit is that the offending IP is blocked before it ever reaches your VPS, saving CPU on your nftables ruleset.
Common Jails Reference
| Jail | Log source | Blocks |
|---|---|---|
| sshd | auth.log / journald | SSH brute force |
| nginx-http-auth | error.log | Failed HTTP basic auth |
| nginx-botsearch | access.log | 404 scanners |
| apache-badbots | access.log | Known bad bots |
| postfix-sasl | mail.log | SMTP auth brute force |
| recidive | fail2ban.log | Repeat offenders (escalated ban) |
| haproxy-badrequest | haproxy.log | Malformed HTTP requests |
Escalating Bans with bantime.increment
Attackers rotate IP addresses. A one-hour ban on a single IP does nothing if they come back from a different address an hour later. Fail2ban’s bantime.increment feature multiplies the ban time for each subsequent offense from the same IP range:
[DEFAULT]
bantime.increment = true
bantime.factor = 2
bantime.maxtime = 96h
With these settings, the first offense gets a 1-hour ban, the second 2 hours, the third 4 hours, and so on, up to 96 hours. Combined with the recidive jail (which re-bans IPs that appear in fail2ban.log itself), this creates a escalating defense that outlasts most IP rotation cycles.
Testing Without Locking Yourself Out
Always add your office or home IP to ignoreip before enabling aggressive jails. To test, trigger a ban deliberately and then lift it:
# Simulate attacks, then verify the ban:
sudo fail2ban-client status sshd
# Unban a test IP:
sudo fail2ban-client set sshd unbanip 203.0.113.9
# Or ban an IP manually:
sudo fail2ban-client set sshd banip 203.0.113.9
If you ever lock yourself out, the fail-safe is your provider’s web console (VNC/noVNC) — you can unban or re-enable SSH directly from the host side. On a modern VPS, this is accessible through the provider dashboard.
What Fail2ban Does Not Cover
Fail2ban reacts to log entries, so it cannot stop a distributed slow attack that stays under maxretry across thousands of IPs, and it adds nothing against exploits that never touch log files. Treat it as one layer: key-only SSH authentication, a restrictive firewall, automatic security updates, and Fail2ban form a defense-in-depth stack that covers the vast majority of automated attacks. For a clean test bed, compare VPS providers on our comparison table and spin up a disposable instance — ten minutes of setup now saves you from a thousand log lines a day later.

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