Security

Setting Up Fail2ban to Automatically Block Attackers

If your SSH or WordPress log is full of failed attempts, this guide walks you step by step through the default jail, writing a custom filter, and preventing yourself from getting blocked.

Security

If you open journalctl -u ssh and see that 4,000 lines of Failed password for root have been logged in the past hour, the problem is no longer "probably." Your server is exposed to the internet and someone is trying passwords with a dictionary. The first thing most admins do is change the SSH port; this only reduces the noise from lazy scanners and doesn't stop a real scanner. What actually works is blocking the attacker's address after a few failed attempts. That's exactly what Fail2ban does.

What Fail2ban actually blocks and what it doesn't

Fail2ban is a Python daemon that tails a log file, runs regex patterns against new lines, and when the number of matches exceeds a threshold within a time window, it writes a rule into the system firewall. On Debian and Ubuntu this rule is applied via iptables or nftables; on systems with firewalld, via firewall-cmd.

You need to know three things from the start. First, Fail2ban doesn't stop volumetric DDoS attacks; if the traffic is at the network layer, blocking IPs at the server level is useless and you need to look into DDoS protection. Second, this tool doesn't stop logins with a stolen private key, because that login is successful and isn't recorded as an error in the log. Third, if you're behind a CDN or reverse proxy, you don't see the user's real address and you might end up blocking the CDN's own IP.

Installation and the first jail on SSH

On Ubuntu 22.04 or 24.04, installation is one line:

apt install fail2ban -y
systemctl enable --now fail2ban

Never edit directly inside /etc/fail2ban/jail.conf. That file gets overwritten with every package update and your settings silently disappear. Create a separate file:

cat > /etc/fail2ban/jail.local <<'EOF'
[DEFAULT]
bantime  = 1h
findtime = 10m
maxretry = 5
ignoreip = 127.0.0.1/8 185.51.200.14

[sshd]
enabled = true
port    = ssh
logpath = %(sshd_log)s
backend = systemd
EOF

The value maxretry = 5 means five failed attempts within the findtime window. The package default is 5 and it's reasonable for most servers. If you set it to 2, a user who mistypes their password twice gets locked out and you'll get a support ticket. If you set it to 20, you've effectively caught no one.

Take the ignoreip line seriously. Write your office or VPN IP address there from the start, otherwise the first person to get blocked is you. If your access is through an enterprise VPN for secure employee access, add that VPN's address range too.

After saving, validate and restart:

fail2ban-client -t
systemctl restart fail2ban
fail2ban-client status sshd

The output should look something like this: Currently banned: 3 and Total banned: 47. If Currently banned stays at zero and you see Failed to access socket path in journalctl -u fail2ban, it means the service didn't come up and you need to read the log from the beginning.

Writing a filter for an app whose log nobody reads

The default jail only covers SSH. If you have WordPress, failed login logs aren't in /var/log/auth.log; they're in the database or a security plugin's log. For this case you need to write a custom filter.

Suppose you have an app that writes a line like this in /var/log/myapp/access.log:

2025-02-11 09:14:22 WARN login_failed ip=91.240.118.7 user=admin

The filter in /etc/fail2ban/filter.d/myapp.conf:

[Definition]
failregex = ^.*login_failed ip=<HOST>.*$
ignoreregex =

And the corresponding jail in jail.local:

[myapp]
enabled  = true
filter   = myapp
logpath  = /var/log/myapp/access.log
maxretry = 8
bantime  = 24h

Before restarting, test the regex separately. This step is skipped by 90% of people and then they say "Fail2ban doesn't work":

fail2ban-regex /var/log/myapp/access.log /etc/fail2ban/filter.d/myapp.conf

The output should give a Lines matched count greater than zero. If it's zero, the regex is wrong, not the server. A point that trips up many people: <HOST> only matches IPv4 and IPv6, and if the address comes with a port you need to write <HOST>:<PORT>.

This is where they go wrong

The most common mistake I see in tickets is this: someone sets bantime = -1 to block "forever," then three weeks later realizes their real users were behind a shared NAT and all come to the site from one external IP. One person mistypes a password and the whole company gets locked out. The sign is that site traffic halves but CPU and bandwidth haven't changed at all.

Second mistake: blocking a search engine's IP range or a payment service's webhook. If you don't put the payment server's address in ignoreip and that server makes several failed requests to a protected endpoint, Fail2ban will block it and transactions will silently fail. Keep the app log intact so you can reconstruct this later; I've written about it in What are audit logs and how do we keep them intact?.

Comparing blocking options

MethodWhere it worksReal cost
Fail2banServer layer, on log-based servicesRequires writing and testing regex for each app
Changing SSH portOnly lazy scannersNo real protection; just less noise
Public key + disabling passwordsCloses the root of the attackKey management for each user
Cloud firewallBefore traffic reaches the serverMonthly cost and panel dependency

If you can only do one thing, disable SSH password login entirely and use a public key. This eliminates 95% of brute-force attempts at the root, and Fail2ban becomes the second layer. That's the correct order, not the reverse. The details are in SSH security; from changing the port to fail2ban and public keys.

When you block yourself

This happens to everyone. If your SSH access is cut off and you're sure your own IP is blocked, log in through the server's emergency console (KVM or the panel's VNC) and run:

fail2ban-client set sshd unbanip 185.51.200.14

To see the full list:

fail2ban-client banned

A practical tip: before any change to jail.local, keep an SSH session open and apply the change in a second session. If the settings are wrong, the first session stays alive and you can roll back. Keep this habit for any firewall change too.

If the server is behind a CDN, you need to read X-Forwarded-For in the filter, otherwise you'll block the CDN edge IP and the site becomes unreachable for everyone. To check what the user's real address is, use the DNS and network lookup tools and compare headers before and after the proxy.

Ultimately, Fail2ban is a reactive tool. If you want to know what state your server is in right now, check the service status before making any change and then get to work. If you'd rather have this layer run on managed infrastructure, ServerNet's security services cover this.

Frequently asked questions

Does Fail2ban stop DDoS attacks?

No. Fail2ban reacts after a packet reaches the server and is recorded in the log, so it's too late for a volumetric attack that saturates bandwidth. For that layer you need network-edge filtering or a DDoS protection service.

Why isn't Fail2ban blocking any IPs?

In most cases the filter's regex doesn't match the actual log format or the logpath is wrong. Test with fail2ban-regex against the same log file; if the match count is zero, the problem is in the filter, not the service.

How do I get back in after blocking myself?

Log in through the server's emergency console and run fail2ban-client set sshd unbanip <IP>. To prevent a repeat, add your static IP address to ignoreip in the jail.local file.

Should I also change the SSH port?

Changing the port alone doesn't add security and only reduces the log volume from simple scanners. If you really want to stop brute-force, disable password login and enforce public key authentication.

ServerNet Support

ServerNet engineering & editorial team — specialists in infrastructure, networking and web hosting.

Security Services
Share:

Comments 0

No comments yet — be the first!

Leave a comment

Related service

Security Services

Penetration testing by OSCP-certified specialists, infrastructure hardening and 24/7 security monitoring — reports managers understand and engineers can act on.