Fail2Ban is the simplest security tool you can install on a Linux server. It watches log files for suspicious activity — repeated failed logins, exploit scanning, excessive requests — and temporarily bans the offending IP addresses using iptables. It is not a substitute for a WAF or proper authentication, but it stops the endless background noise of automated attacks from filling your logs and consuming resources.

How Fail2Ban works

  1. A filter defines a regex pattern to match in a log file
  2. A jail combines a filter with an action (usually banning the IP)
  3. Fail2Ban tails the log file. When an IP matches the filter too many times within a window, it triggers the action
  4. After a configurable time, the ban expires

Installation

# Debian/Ubuntu
sudo apt update
sudo apt install fail2ban

# Create a local config (never edit the default config files directly)
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local

Always customise jail.local, not jail.conf. Updates overwrite jail.conf.

Basic configuration

# /etc/fail2ban/jail.local
[DEFAULT]
ignoreip = 127.0.0.1/8 ::1 192.168.0.0/16
bantime = 3600
findtime = 600
maxretry = 5
destemail = your@email.com
action = %(action_mwl)s

[sshd]
enabled = true
port = ssh
logpath = %(sshd_log)s
maxretry = 3
bantime = 86400
  • bantime: How long an IP stays banned (seconds)
  • findtime: The window in which repeated failures count (seconds)
  • maxretry: Number of failures before banning
  • action_mwl: Ban + send email with whois info and log lines

For SSH, 3 failures in 10 minutes = 24-hour ban is a good starting point. Bots move on quickly.

WordPress brute force protection

Create a custom filter for WordPress login attempts:

# /etc/fail2ban/filter.d/wordpress.conf
[Definition]
failregex = ^<HOST> .* "POST /wp-login\.php
            ^<HOST> .* "POST /xmlrpc\.php
ignoreregex =
# In jail.local
[wordpress]
enabled = true
filter = wordpress
logpath = /var/log/nginx/access.log
maxretry = 5
findtime = 300
bantime = 3600

This watches Nginx access logs for repeated POSTs to login and xmlrpc endpoints. Works with Apache access logs too — just point logpath to the right file.

WordPress auth filter with wp-login.php failures

For a more precise filter that catches actual login failures (not just access):

# /etc/fail2ban/filter.d/wordpress-auth.conf
[Definition]
failregex = ^<HOST> .* "POST /wp-login\.php HTTP.* 200

This only triggers on successful POSTs that result in HTTP 200 — login page reloads after a failed attempt. False positives are minimal with this approach.

Custom jails for web applications

Rate limiting API endpoints

# /etc/fail2ban/filter.d/api-abuse.conf
[Definition]
failregex = ^<HOST> .* "(GET|POST) /api/.* HTTP.* (429|403)
ignoreregex =
[api-abuse]
enabled = true
filter = api-abuse
logpath = /var/log/nginx/access.log
maxretry = 10
findtime = 60
bantime = 7200

Generic 404 scanner protection

# /etc/fail2ban/filter.d/nginx-404.conf
[Definition]
failregex = ^<HOST> .* "(GET|POST|HEAD) .* HTTP.*" 404
ignoreregex =
[nginx-404]
enabled = true
filter = nginx-404
logpath = /var/log/nginx/access.log
maxretry = 20
findtime = 60
bantime = 3600

Be careful with 404 jails — a misconfigured site with broken internal links can generate legitimate 404s.

Monitoring Fail2Ban

# Check status of all jails
sudo fail2ban-client status

# Check a specific jail
sudo fail2ban-client status sshd

# See currently banned IPs
sudo fail2ban-client banned

# Check the log
sudo tail -f /var/log/fail2ban.log

# Unban an IP (your own, if locked out)
sudo fail2ban-client set sshd unbanip 192.168.1.100

Common issues

1. Fail2Ban not starting

sudo fail2ban-client -d  # Dry run — shows config errors
sudo journalctl -u fail2ban --no-pager | tail -20

2. Jails not banning

Check the log path is correct and the filter regex matches:

sudo fail2ban-regex /var/log/nginx/access.log /etc/fail2ban/filter.d/wordpress.conf

3. Can’t connect after getting banned

If you lock yourself out:

  1. Connect via out-of-band console (VPS provider’s web console)
  2. sudo fail2ban-client set sshd unbanip YOUR_IP
  3. Or: whitelist your static IP in ignoreip

4. Logrotate breaks Fail2Ban

When logs rotate, Fail2Ban can lose its position. Use logpath with globs:

logpath = /var/log/nginx/access.log
          /var/log/nginx/access.log.1

Security philosophy

Fail2Ban stops automated attacks. It does not stop:

  • Targeted attacks from distributed IPs
  • Credential stuffing (one attempt per account per IP)
  • Zero-day exploits

Fail2Ban is one layer. Pair it with:

  • SSH key-only authentication (disable password auth)
  • Rate limiting at the application level
  • A WAF for web application attacks
  • Regular security updates

Quick start: the 5-minute setup

# Install
sudo apt install fail2ban -y

# Configure SSH jail
sudo tee -a /etc/fail2ban/jail.local << 'EOF'
[sshd]
enabled = true
maxretry = 3
bantime = 86400
findtime = 600
EOF

# Start and enable
sudo systemctl enable --now fail2ban

# Verify
sudo fail2ban-client status sshd

That single SSH jail blocks 99% of the background noise on a public server. Everything else is incremental improvement.