Fail2Ban: A Practical Way to Secure Your Server from Brute Force and Bot Attacks
Instantly shield your Ubuntu 24 server from brute-force logins and automated scans with minimal setup
Have you ever checked your server logs and noticed a bunch of bizarre login attempts or requests for files that shouldn’t even be public? If you run a server on the internet, chances are bots and scripts are constantly poking at it – often within minutes of going online. They’ll try logging in via SSH with random usernames, or scour your website for open directories and sensitive files (like a forgotten .env
or .git
folder).
In fact, every website is bombarded with dozens of unwanted requests every day. These automated “vulnerability scanners” will casually check if you left a secret key under the doormat – be it a database dump, an old admin panel, or a default password. As one security researcher put it, “the wolf has been at your door since the day you put that server online.” 😨
So, how do we deal with this constant nuisance (and potential danger) of brute-force and bot attacks? Enter Fail2Ban – the unsung hero and bouncer of the server world.
What is Fail2Ban and How Does It Help?
Fail2Ban is a lightweight, open-source intrusion prevention framework (written in Python) that protects your server from these automated attacks. Think of it as an automated bouncer for your server: it monitors your log files for suspicious patterns (failed logins, 404 errors from bots scanning for exploits, etc.) and automatically bans the offending IPs by adding a rule to your firewall. For example, Fail2Ban will watch something like /var/log/auth.log
for repeated SSH login failures and then block that IP address for a while. This dramatically cuts down the noise from bots hammering your services.
Out of the box, Fail2Ban comes with filters (rules) for many common services – SSH, FTP, web servers (Apache/Nginx), mail servers, etc. – and it’s easily configurable to watch any log file you want. The default configuration alone can stop a lot of bad behavior right away. For instance, the moment some bot throws five wrong passwords at your SSH, Fail2Ban can ban them for 10 minutes or longer (default bantime is 10 minutes, but you can adjust it). The same goes for other protocols: too many FTP login failures, or too many 404 errors on your website, and that IP gets the boot. Essentially, Fail2Ban reduces malicious login attempts by blocking the source IP addresses automatically. It’s like having a security guard who gives any suspicious intruder a time-out.
Importantly, Fail2Ban isn’t a silver bullet – it won’t stop a distributed attack (where each attempt comes from a different IP), and it doesn’t fix weak passwords or outdated software. But for the price of a quick install, it adds a very effective layer of defense. It’s also pretty opinionated software (in a good way): it assumes that if an IP is misbehaving, you’re better off not hearing from it again for a while. And honestly, I agree. 😉
Installing Fail2Ban on Ubuntu 24.04
Installing Fail2Ban on Ubuntu is straightforward since it’s available in the official package repository. Here we’ll focus on Ubuntu 24.04 (or any recent Ubuntu release). Just pop open your terminal and run:
sudo apt update && sudo apt upgrade -y
sudo apt install fail2ban -y
That's it! The Fail2Ban service will start automatically after installation. You can verify it's running by asking it to ping itself:
sudo fail2ban-client ping
If everything is OK, it should respond with PONG
. 🟢
A note on firewalls: Fail2Ban works by adding rules to your firewall (iptables under the hood). On Ubuntu, many people also use UFW (Uncomplicated Firewall). If UFW is active or if you plan to use it, make sure you have an SSH allow rule before enabling UFW, or you might lock yourself out when Fail2Ban bans something. For example:
sudo ufw allow OpenSSH
sudo ufw enable
Fail2Ban will happily work with UFW as the backend (it supports iptables, UFW, firewalld, etc., automatically). If you’re not using UFW, Fail2Ban will just use iptables directly. In any case, after installation, Fail2Ban is up and ready to ban bad actors.
Basic Configuration and Enabling the SSH Jail
Fail2Ban’s default settings are decent, but it’s worth doing a bit of configuration to tailor it to your needs. Configuration is typically done in the file /etc/fail2ban/jail.conf
or, better, in a separate jail.local
file (to override defaults without getting overwritten on updates). The quick way is to copy the default config to a .local
file:
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
Now open /etc/fail2ban/jail.local
in your favorite editor (e.g. sudo nano /etc/fail2ban/jail.local
). You’ll see a bunch of sections for different “jails.” A jail in Fail2Ban parlance is basically a set of rules for a particular service or pattern (e.g., an SSH jail that watches auth log for SSH failures).
Let’s make sure the SSH jail is enabled and tweaked:
[sshd]
enabled = true # Make sure this is true to activate the SSH jail
port = ssh # or specify the port if you changed it from 22
logpath = /var/log/auth.log
maxretry = 5 # max failed attempts before ban (default 5)
findtime = 10m # time window for maxretry (default 10 minutes)
bantime = 1h # ban duration (e.g. 1 hour; default was 10m)
In the snippet above, we enable the [sshd]
jail. We keep maxretry
at 5 (meaning 5 failed login attempts within the findtime
window will trigger a ban) – you can lower this if you want to be stricter. We’ve increased bantime
to 1 hour in this example, instead of the default 10 minutes, because a 10-minute ban is sometimes not much of a deterrent. You could even set bantime = 1d
for a full day, or bantime = -1
for a permanent ban of that IP (though permanent bans might eventually fill up your firewall rules if many accumulate).
Feel free to adjust these values. For instance, if you rarely ever log in to SSH with a password (as you should be using keys!), you might set maxretry
to 3 or even 1 just to ban first-time offenders immediately. The idea is to find a balance – you want to silence the noise of incessant bot attacks without accidentally locking yourself out or banning legitimate users. (Tip: The ignoreip
setting under the [DEFAULT]
section can whitelist IPs you never want to ban – e.g. your home/office IP range – so you don’t ban yourself by mistake.)
When you’re done editing, save the file and restart Fail2Ban to apply changes:
sudo systemctl restart fail2ban
Fail2Ban will now be actively monitoring your SSH logs and banning any IPs that violate the rules. ✅
Adding a Custom Rule (Blocking Specific Bot Patterns)
One of the great things about Fail2Ban is its flexibility. Beyond the pre-packaged jails, you can create your own rules to match patterns specific to your environment. Remember those bots looking for .env
files or other juicy targets? We can make Fail2Ban ban them too!
For example, suppose you want to ban any IP that tries to access a hidden file like .env
on your web server (because legit users would never do that). We can set up a custom jail for that. Let’s check it below.
Create a filter definition
It’s basically a regex pattern to catch the bad behavior. Filters are stored in /etc/fail2ban/filter.d/
. Let’s create one for “env file access attempts”:
sudo nano /etc/fail2ban/filter.d/custom-env.conf
Inside this file, define a filter under a [Definition]
header. For an Nginx access log, a simple filter to match a .env
request might look like:
[Definition]
failregex = ^<HOST> -.* \"GET \/\.env
ignoreregex =
Let’s break that down:
<HOST>
is a placeholder that Fail2Ban uses to identify the attacker’s IP from the log line.The regex here is looking for lines where an IP (that’s the
<HOST>
at the start of an Nginx log line) requests “GET /.env”. We put a backslash before.env
because the dot is a special char in regex. This pattern is simplistic (it assumes the request is a GET and doesn’t fully anchor the line), but it should catch most cases. You can refine regex as needed (check your actual log format!). Theignoreregex
is empty, meaning we’re not defining any pattern to ignore.
Define the jail using this filter
So now open your jail local config (/etc/fail2ban/jail.local
or you can create a new file under jail.d/
for cleanliness) and add a section for this new jail. For example:
[nginx-env-scan]
enabled = true
filter = custom-env # refers to the custom-env.conf filter we just created
action = iptables-multiport[name=NoEnv, port=\"http,https\"]
logpath = /var/log/nginx/access.log
maxretry = 1
findtime = 1m
bantime = 1d
Here, we named the jail "nginx-env-scan" (name it whatever makes sense to you). We enable it and point it to use the custom-env
filter. The action
line tells Fail2Ban how to ban (this uses the standard iptables action for blocking on one or multiple ports – in this case, we block the host on both HTTP and HTTPS ports). We set maxretry = 1
and a short findtime
of 1 minute, meaning a single match is enough to ban the IP for a day. This is a bit aggressive, but for something like .env
access attempts, it’s probably safe – there are virtually no legitimate reasons for a public user to request that file. (If you were doing this for, say, an /admin
URL that could have legit visitors, you’d use a higher threshold like 3 retries in 5 minutes, etc.)
Restart the service
Let’s restart the server to load the new filter and jail:
sudo systemctl restart fail2ban
Now Fail2Ban will watch your Nginx access logs and any IP that tries to GET a .env
file will be banned on the spot. 🎉 Similarly, you could craft rules for other common bot targets – e.g. wp-login.php
(if you’re not running WordPress, no one should be hitting that), or blocking excessive 404 errors in a short time (indicating someone scanning for lots of files). There are community-contributed filters for common CMS exploits and bad bots, or you can write your own as we just did.
Pragmatic tip: Don’t go too overboard with hundreds of custom rules for every little thing – focus on the high-signal patterns (like obvious exploit scans) so you don’t accidentally ban real users. The goal is to reduce malicious traffic, not to create a self-inflicted denial of service. 😉
Monitoring Fail2Ban: Status, Logs, and Unbanning
Once Fail2Ban is running, you’ll want to occasionally check in on what it’s doing. The primary command-line tool is fail2ban-client
, which lets you interact with the Fail2Ban daemon.
Let’s check some useful commands below.
Overall status
We may see a summary of jails and overall bans:
sudo fail2ban-client status
Jail status
We may get details about a specific jail, including currently banned IPs:
sudo fail2ban-client status sshd
This command will show you how many IPs have been banned by the SSH jail and list them. For instance, you might see output indicating X total bans and a list of IP addresses that are currently banned for SSH. Similarly, you can check nginx-env-scan
or any other jail by name.
Logs
Fail2Ban logs its actions to /var/log/fail2ban.log
. It’s often informative to tail this log to see which IPs are getting banned and why. For example:
sudo tail -f /var/log/fail2ban.log
We will see entries whenever a ban or unban happens, e.g., “Ban xxx.x.xxx.xx on sshd” etc.
Unbanning an IP
Occasionally, we might need to manually unban someone (maybe you banned yourself during testing – it happens and be careful with that axe, Eugene careful with this tool on your live server!). To unban an IP:
sudo fail2ban-client set sshd unbanip xxx.x.xxx.xx
Replace xxx.x.xxx.xx
with the IP you want to free from jail. This command tells Fail2Ban to remove that IP from the jail’s ban list, which in turn removes the firewall rule. After unbanning, that IP can connect again (assuming it fixes its behavior).
Banning an IP manually
It is also possible to proactively ban an IP (outside the automatic triggers) using a similar command:
sudo fail2ban-client set sshd banip xxx.x.xxx.xx
Everything you do with fail2ban-client
is also doable by editing config or directly manipulating firewall rules, but the tool provides a nice interface and ensures Fail2Ban’s internal state stays in sync.
Conclusion: High Impact, Low Effort Security
If you’ve made it this far, you’ve seen that Fail2Ban offers a big security win for very little effort. In about 5 minutes, you installed it and enabled a jail or two, and your server gained the ability to automatically smack down many brute-force and bot-driven attacks. It’s not fancy, and it’s certainly not a replacement for good practices (you should still use SSH keys or 2FA, strong unique passwords, keep your software updated, etc.). But Fail2Ban addresses a very common class of threats in a way that’s both simple and effective. It’s basically set-and-forget: install, configure once, and let it quietly do its job in the background.
Personally, I consider Fail2Ban a must-have on any server that faces the wild internet. It’s like having an invisible force field that won’t stop a targeted ninja attack, but will definitely fend off the annoying background noise of the internet’s riff-raff. And that means you (and your server) can sleep a little more soundly at night. 😴👍
So go ahead – give your server that bouncer it deserves. In the eternal words of the security community: “configure Fail2Ban today, and thank yourself later.” Stay safe out there!
Thanks for reading! Subscribe to Labrodev substack and let’s keep in touch!
Picture used in preview credits:
Unsplash, Towfiqu barbhuiya