Skip to main content

Fail2Ban

Fail2Ban is a log-driven intrusion prevention tool that detects repeated authentication failures (and other suspicious patterns) and applies temporary IP bans using the host firewall. On WordPress VPS servers, it is most commonly used to reduce brute-force pressure against SSH and other exposed services by automatically blocking abusive sources after a configurable number of failures.

Background and history

As SSH and web services became routinely exposed on public servers, automated bots began attempting credential stuffing and brute-force logins at scale. Administrators needed a lightweight way to react automatically without manually updating firewall rules. Fail2Ban emerged as a practical solution: tail logs, match patterns, and ban abusive IPs for a period. Over time it added support for multiple log backends, many service-specific filters, and integration with common firewall frameworks.

Adoption and where it’s commonly used

Fail2Ban is commonly used on:

  • VPS and cloud Linux servers with public SSH
  • Web servers that expose admin endpoints or HTTP auth
  • Mail servers and other credentialed services
  • Small-to-medium fleets where a host-local ban mechanism complements perimeter controls

Maintained by

  • Maintained by the Fail2Ban project community.

Best when to use

  • SSH is exposed to the internet and logs show frequent failed logins.
  • You want automated temporary bans without maintaining manual allow/deny lists.
  • You already have a host firewall (UFW, nftables/iptables, firewalld) and want Fail2Ban to drive it.
  • You can review logs and tune jails to match your stack.

Not suitable when

  • You need centralized, fleet-wide enforcement and reporting as the primary control (use perimeter controls plus centralized security tooling).
  • Your services do not log reliably (Fail2Ban requires logs with consistent patterns).
  • You need Layer 7 bot mitigation, WAF rules, or DDoS protection (use WAF/CDN controls).

Compatibility notes

  • Log paths and service names differ by distro and web stack:

    • Debian/Ubuntu SSH auth logs often live at /var/log/auth.log
    • RHEL-based systems often use /var/log/secure
  • With systemd, Fail2Ban can read from journald; this is often more portable than file paths.

  • Firewall integration varies:

    • UFW: banaction = ufw
    • firewalld: appropriate firewalld action
    • nftables/iptables: appropriate iptables/nftables actions
  • If you use a cloud/provider firewall, Fail2Ban bans on the host do not block traffic before it reaches the server’s network interface.

Lockout risk

Misconfiguring Fail2Ban can ban your own IP. Before enabling jails, ensure you have console/serial/KVM access or a safe allowlist strategy. Keep at least one active SSH session open while testing changes.

Concepts and how it works

Fail2Ban consists of:

  • Filters: regex patterns that detect failures in logs.
  • Jails: service-specific policies that bind filters to log sources and define thresholds.
  • Actions: what happens when a ban triggers (typically add a firewall rule).

Installation

Debian/Ubuntu

sudo apt update
sudo apt install -y fail2ban

Enable and start:

sudo systemctl enable --now fail2ban

Verify:

systemctl status fail2ban --no-pager

RHEL/Fedora/Rocky/AlmaLinux

sudo dnf install -y fail2ban
sudo systemctl enable --now fail2ban

Key files and directories

PathPurpose
--
/etc/fail2ban/jail.confDefault config (do not edit)
/etc/fail2ban/jail.localMain local override file (preferred)
/etc/fail2ban/jail.d/*.confDrop-in jail overrides (clean per-service approach)
/etc/fail2ban/filter.d/Filters (regex definitions)
/var/log/fail2ban.logFail2Ban activity log (when enabled)
Configuration precedence

Do not edit jail.conf. Put changes in jail.local or /etc/fail2ban/jail.d/*.conf so package upgrades do not overwrite your settings.

Baseline configuration for daily VPS use

Step 1: Create local configuration

If you prefer a single file:

sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local

A cleaner production approach is to keep /etc/fail2ban/jail.local minimal and use /etc/fail2ban/jail.d/*.conf drop-ins per service. Both are valid; pick one approach and be consistent.

Step 2: Set global defaults

In /etc/fail2ban/jail.local (or a drop-in such as /etc/fail2ban/jail.d/00-defaults.conf), define defaults appropriate for internet-exposed hosts:

[DEFAULT]
backend = systemd
findtime = 10m
maxretry = 5
bantime = 1h

Firewall action examples:

  • UFW environments:
[DEFAULT]
banaction = ufw
  • firewalld environments:
[DEFAULT]
banaction = firewallcmd-ipset
Firewall integration must match your host

If banaction does not match your firewall stack, bans may not take effect even though Fail2Ban reports activity. Verify bans by inspecting firewall status after a test ban.

Step 3: Enable the SSH jail

Create a dedicated drop-in:

sudo tee /etc/fail2ban/jail.d/sshd.conf >/dev/null <<'EOF'
[sshd]
enabled = true
# Match your SSH port (22, 2222, 2581, etc.)
port = 22
# Debian/Ubuntu file log path; prefer systemd backend when possible
logpath = %(sshd_log)s
maxretry = 5
findtime = 10m
bantime = 1h
EOF

If you changed SSH to port 2581, set:

port = 2581

Apply changes safely

Validate service and reload:

sudo systemctl restart fail2ban
sudo systemctl status fail2ban --no-pager

List enabled jails:

sudo fail2ban-client status

Check SSH jail:

sudo fail2ban-client status sshd

Operational commands (daily use)

Show all jails

sudo fail2ban-client status

Show one jail

sudo fail2ban-client status sshd

Manually ban and unban (useful for validation)

sudo fail2ban-client set sshd banip 198.51.100.10
sudo fail2ban-client set sshd unbanip 198.51.100.10

Unban your own IP if needed

sudo fail2ban-client set sshd unbanip <your_public_ip>

If you cannot reach the server via SSH, use provider console access to unban and adjust configuration.

Logs and verification

Fail2Ban log

sudo tail -n 200 /var/log/fail2ban.log

Follow live:

sudo tail -f /var/log/fail2ban.log

Verify bans reached the firewall

UFW

sudo ufw status numbered

You should see deny entries corresponding to banned IPs.

nftables / iptables / firewalld

Verification method depends on your firewall stack; confirm the action you chose (banaction) and then inspect the active rules.

Protecting services beyond SSH

Fail2Ban works best when a service logs consistent, parseable failures and you have a filter/jail that matches your stack.

Common built-in jails (availability varies)

Service areaTypical jail nameNotes
--
SSHsshdMost common baseline jail
HTTP basic authnginx-http-auth / apache-authProtects endpoints using basic auth
Recidive abusersrecidiveRe-bans repeat offenders across jails

WordPress-specific guidance

Fail2Ban is not a full web application firewall. For WordPress login and bot pressure:

  • Prefer upstream controls (CDN/WAF rules, rate limiting).
  • If you still want host-side protection, base it on web server access logs and be conservative to avoid false positives.
WordPress “login” jails can cause collateral bans

Blocking by IP based on HTTP requests can ban legitimate users behind shared IPs (office NAT, mobile carriers). Use conservative thresholds and consider allowlisting trusted admin IPs.

Allowlisting and reducing false positives

In [DEFAULT]:

ignoreip = 127.0.0.1/8 ::1 203.0.113.10

Add your fixed admin IPs (office/VPN). Avoid allowlisting broad residential ranges.

Avoid allowlisting “Anywhere”

Do not add wide ranges (such as your ISP’s entire CIDR) unless you fully understand the security tradeoff.

Tune thresholds

Guidance for SSH on internet-exposed VPS:

  • maxretry: 3–5
  • findtime: 5m–15m
  • bantime: 1h–24h (shorter for dynamic IP environments, longer for persistent attackers)

Example stricter SSH profile:

[sshd]
enabled = true
port = 2581
maxretry = 3
findtime = 10m
bantime = 6h

Troubleshooting

Jail shows activity but no bans appear

Common causes:

  • Wrong banaction for the firewall stack
  • Running in a container or restricted environment
  • Missing privileges to modify firewall rules

Checks:

sudo fail2ban-client status sshd
sudo fail2ban-client get sshd banaction
sudo systemctl status fail2ban --no-pager

SSH jail not detecting failures

Common causes:

  • Wrong log backend or log path
  • SSH logs go to journald only
  • Different distro log file location

Checks:

sudo fail2ban-client get sshd logpath
sudo journalctl -u ssh --no-pager -n 200 2>/dev/null || true
sudo journalctl -u sshd --no-pager -n 200 2>/dev/null || true
sudo tail -n 200 /var/log/auth.log 2>/dev/null || true
sudo tail -n 200 /var/log/secure 2>/dev/null || true

You got banned (self-lockout prevention and recovery)

Prevention:

  • Add your trusted admin IP to ignoreip.
  • Test from a secondary session before tightening thresholds.

Recovery (requires server access):

  • Unban your IP:
sudo fail2ban-client set sshd unbanip <your_public_ip>

If you cannot SSH in:

  • Use provider console access to unban and adjust ignoreip.

Security notes

  • Fail2Ban reduces brute-force pressure but does not replace:

    • SSH key-based authentication
    • Disabling root SSH login
    • Restricting SSH to trusted IPs where feasible
    • Regular patching and log monitoring
  • For WordPress web-layer abuse, prefer upstream controls (WAF/CDN) and application hardening.

Quick reference

Minimal production baseline (SSH + UFW)

/etc/fail2ban/jail.d/00-defaults.conf:

[DEFAULT]
backend = systemd
banaction = ufw
findtime = 10m
maxretry = 5
bantime = 1h
ignoreip = 127.0.0.1/8 ::1

/etc/fail2ban/jail.d/sshd.conf:

[sshd]
enabled = true
port = 2581

Apply:

sudo systemctl restart fail2ban
sudo fail2ban-client status
sudo fail2ban-client status sshd

Common commands

GoalCommand
--
Start/enablesudo systemctl enable --now fail2ban
Restartsudo systemctl restart fail2ban
List jailssudo fail2ban-client status
Jail statussudo fail2ban-client status sshd
Ban IPsudo fail2ban-client set sshd banip <ip>
Unban IPsudo fail2ban-client set sshd unbanip <ip>
View logssudo tail -f /var/log/fail2ban.log