Quick take: Fail2Ban reads your server's log files and automatically blocks IPs that trigger repeated authentication failures. It takes about fifteen minutes to configure properly and dramatically reduces the noise from automated attacks — even on servers that already use SSH key authentication.
Introduction
Fail2Ban solves a problem that never goes away: automated bots scanning every public IP address on the internet, hammering SSH ports, probing web login endpoints, and looking for exposed admin panels. Even with SSH key authentication in place — which stops password attacks from succeeding — these bots still fill your log files, consume server resources, and mask genuine security events in a sea of noise.
I run Fail2Ban on every server I manage, configured with aggressive rules for SSH and any web application with a login endpoint. The configuration below is what I use on production infrastructure, with bans tuned to be painful for automated scanners without being a risk of locking out legitimate users.
How Fail2Ban Works
Fail2Ban monitors log files and applies regex patterns — called "filters" — to detect repeated failures. When an IP triggers a configured number of failures within a time window ("findtime"), Fail2Ban adds a firewall rule to block that IP for a configured duration ("bantime"). When the ban expires, the rule is removed automatically.
The monitored service is called a "jail". Each jail has a log file to watch, a filter to apply, and ban parameters. Fail2Ban ships with filters for SSH, Nginx, Apache, Postfix, and dozens of other services. You can also write your own for any application that produces log output.
Fail2Ban integrates with the system firewall — usually iptables or nftables on Ubuntu — to add and remove block rules. The bans survive Fail2Ban restarts (they are stored on disk) but are cleared on system reboot by default, which is usually the right behaviour.
Installing Fail2Ban on Ubuntu
sudo apt update
sudo apt install fail2ban -y
Start and enable the service:
sudo systemctl start fail2ban
sudo systemctl enable fail2ban
sudo systemctl status fail2ban
Verify Fail2Ban is running and check which jails are currently active:
sudo fail2ban-client status
Configuration Files Explained
The configuration hierarchy is important to understand before making any changes:
/etc/fail2ban/jail.conf # Default config — DO NOT edit, gets overwritten on upgrades
/etc/fail2ban/jail.local # Your customisations — takes priority over jail.conf
/etc/fail2ban/jail.d/ # Per-service override files — also takes priority
/etc/fail2ban/filter.d/ # Filter (regex) definitions — one file per service
Create your local config file by copying the defaults as a reference:
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
sudo nano /etc/fail2ban/jail.local
In the [DEFAULT] section, set more aggressive defaults than the out-of-box settings:
[DEFAULT]
bantime = 1h
findtime = 10m
maxretry = 3
ignoreip = 127.0.0.1/8 ::1 YOUR_HOME_IP_HERE
Add your own IP address to ignoreip before enabling aggressive banning. Locking yourself out of your own server because you mistyped a password three times is an embarrassing and avoidable situation.
Configuring the SSH Jail
Find the [sshd] section in jail.local and configure it:
[sshd]
enabled = true
port = ssh
filter = sshd
logpath = /var/log/auth.log
maxretry = 3
bantime = 24h
findtime = 1h
This bans any IP that fails SSH authentication three times within one hour, for 24 hours. If you changed SSH from the default port 22, update the port parameter to match:
port = 2299 # your custom SSH port
Restart Fail2Ban after any configuration change:
sudo systemctl restart fail2ban
Verify the SSH jail is active and check its current status:
sudo fail2ban-client status sshd
The output shows the filter being used, the log file being monitored, current ban count, and any currently banned IPs.
Protecting Nginx with Fail2Ban
Fail2Ban includes several built-in Nginx filters. Add these to your jail.local:
[nginx-http-auth]
enabled = true
port = http,https
filter = nginx-http-auth
logpath = /var/log/nginx/error.log
maxretry = 3
bantime = 1h
[nginx-limit-req]
enabled = true
port = http,https
filter = nginx-limit-req
logpath = /var/log/nginx/error.log
maxretry = 10
bantime = 1h
[nginx-botsearch]
enabled = true
port = http,https
filter = nginx-botsearch
logpath = /var/log/nginx/access.log
maxretry = 2
bantime = 24h
The nginx-botsearch jail is particularly effective at catching vulnerability scanners that probe for WordPress admin panels, phpMyAdmin installations, and similar commonly targeted endpoints. With only 2 attempts allowed before a 24-hour ban, the vast majority of automated scanners get blocked on their first visit.
Restart Fail2Ban and verify all jails are active:
sudo systemctl restart fail2ban
sudo fail2ban-client status
Monitoring Active Bans
Check the status of all jails at once:
sudo fail2ban-client status
Check a specific jail including currently banned IPs:
sudo fail2ban-client status sshd
sudo fail2ban-client status nginx-botsearch
See all currently banned IPs across all jails:
sudo fail2ban-client banned
Watch the Fail2Ban log in real time to see bans being applied as they happen:
sudo tail -f /var/log/fail2ban.log
Each ban entry shows the jail name, the IP, and the timestamp. Each unban entry shows when the block was removed.
Unbanning IPs
When you accidentally get yourself banned (it happens to everyone), unban your IP immediately:
# Unban from a specific jail
sudo fail2ban-client set sshd unbanip 1.2.3.4
# Unban from all jails at once
sudo fail2ban-client unban 1.2.3.4
You can also manually ban a persistent attacker for a longer period:
sudo fail2ban-client set sshd banip 5.6.7.8
Writing a Custom Filter and Jail
When you have an application that Fail2Ban has no built-in filter for, write your own. First, identify what a failure entry looks like in the application's log:
sudo tail -50 /var/log/myapp/app.log | grep -i "fail\|error\|invalid"
Suppose failed logins look like this in your log:
2026-06-22 14:32:11 ERROR Failed login from 192.168.1.100 for user admin
Create a filter file:
sudo nano /etc/fail2ban/filter.d/myapp.conf
[Definition]
failregex = ^.*Failed login from <HOST> for user.*$
ignoreregex =
The <HOST> placeholder is replaced by Fail2Ban with the IP address regex automatically. Test your filter before creating the jail:
sudo fail2ban-regex /var/log/myapp/app.log /etc/fail2ban/filter.d/myapp.conf
The output shows how many lines matched. When it looks correct, create the jail in jail.local:
[myapp]
enabled = true
filter = myapp
logpath = /var/log/myapp/app.log
maxretry = 5
bantime = 2h
port = http,https
Beyond Fail2Ban: Layered Security
Fail2Ban is one layer of defence, not the complete picture. Run it as part of a layered approach:
- SSH key authentication with password login disabled. Fail2Ban catches failures, but key auth means there should be no successful brute-force regardless of how persistent the attacker is.
- UFW with default-deny inbound. Only expose ports your server actually uses. A port that is not open cannot be attacked.
- Non-standard SSH port. Moves you off the most-scanned port and eliminates the vast majority of automated scanning noise, leaving Fail2Ban to deal only with more targeted attempts.
- Automatic security updates. Unpatched software is the biggest risk on any server:
sudo apt install unattended-upgrades - Regular log review. Automated tools catch obvious patterns. Manual review occasionally surfaces sophisticated attack patterns that rules-based systems miss.
Final Thoughts
Fail2Ban is one of those tools that operates quietly in the background and you only notice it when you check the logs and see hundreds of IPs blocked. Once configured correctly, it requires almost no ongoing maintenance — the jails watch the logs, apply the rules, and clear expired bans automatically.
The configuration in this guide represents a real-world setup, not a minimal demo. The aggressive bantime values, the Nginx jails covering both auth failures and bot scanning, and the custom filter framework give you a solid starting point that handles the threat landscape on a typical public-facing Linux server.
FAQ: How to Secure Your Linux Server with Fail2Ban
Does Fail2Ban work alongside SSH key authentication?+
Yes, and you should run both. SSH key authentication prevents password attacks from ever succeeding. Fail2Ban stops bots from hammering the server repeatedly, keeps logs clean, and protects other services like Nginx and web applications where key authentication does not apply.
How do I see which IPs Fail2Ban has currently banned?+
Run: sudo fail2ban-client status sshd (replace sshd with any jail name to check that specific jail). To see all bans across all jails at once: sudo fail2ban-client banned. The Fail2Ban log at /var/log/fail2ban.log shows every ban and unban with timestamps.
Will Fail2Ban ban me if I mistype my SSH password?+
Only if you fail more times than maxretry within the findtime window. With maxretry=3, you would need to fail three times in the findtime period. To prevent accidentally locking yourself out, add your home or office IP address to the ignoreip setting in jail.local before enabling strict rules.
How do I make Fail2Ban bans permanent?+
Set bantime = -1 in a specific jail configuration. A value of -1 means permanent ban until manually removed with fail2ban-client unban. Use this carefully — only for jails monitoring clearly hostile traffic patterns, such as a bot-search jail, not for SSH where you might accidentally ban a legitimate user.
Does Fail2Ban protect against DDoS attacks?+
No — Fail2Ban is designed for brute-force and abuse detection, not volumetric DDoS. For DDoS protection you need upstream mitigation from your VPS provider, Cloudflare, or a dedicated scrubbing service. Fail2Ban can be overwhelmed by high-volume attacks and is not a substitute for proper DDoS protection.
Need help with your Linux server or infrastructure?
Work directly with Muhammad Irfan Aslam for Linux, Docker, Nginx, DevOps, cloud, and server support.
Hire Me for Support