Within minutes of a dedicated server getting a public IP, automated bots start knocking on SSH. Not targeted attacks — mass scans, working through credential lists, trying root/admin with thousands of common passwords, all day, every day. SSH key authentication stops them from getting in, but it doesn't stop them from trying, and a server can end up processing hundreds of failed login attempts a day just sitting there.
Fail2Ban is the standard answer to this. It watches your authentication logs, and once an IP crosses a threshold of failed attempts, it automatically bans that IP at the firewall level — no manual intervention needed. This guide covers installing it, setting up the SSH jail properly, and confirming it's actually banning IPs before you rely on it.
Who This Guide Is For
This is for anyone running a dedicated server or VPS with SSH exposed to the internet who wants automated protection against brute-force login attempts, on top of (not instead of) SSH key authentication.
Prerequisites
A Linux server running Ubuntu 24.04 (or a recent Debian-based distro — the package and config paths are the same).
Root or sudo access.
SSH already working normally, ideally with key-based authentication already set up.
A second way to reach the server — console access, a hosting provider's KVM/IPMI panel, or a second SSH session kept open — before you start changing firewall behavior.
That last point matters more than it sounds like. A misconfigured jail can ban your own IP, and if SSH is your only way in, that's a support ticket instead of a 30-second fix. Keep a fallback session open while you test.
Step 1: Install Fail2Ban
Update your package index and install Fail2Ban from Ubuntu's repository:
sudo apt update
sudo apt install -y fail2ban
Enable and start the service:
sudo systemctl enable --now fail2ban
Confirm it's running:
sudo systemctl status fail2ban
You should see active (running). Out of the box, Fail2Ban ships with the SSH jail already defined but check its actual status in Step 4 — "installed" isn't the same as "actively watching."
Step 2: Create a Local Configuration File
Fail2Ban's main config file is /etc/fail2ban/jail.conf. Don't edit it directly — package updates overwrite it, and you'll lose your changes. Instead, copy it to jail.local, which Fail2Ban reads as an override:
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
From here on, all your edits go in jail.local, never jail.conf.
Step 3: Configure the SSH Jail
Open the local config file:
sudo nano /etc/fail2ban/jail.local
First, find the [DEFAULT] section near the top and set ignoreip to include your own IP address, so you can never accidentally ban yourself:
[DEFAULT]
ignoreip = 127.0.0.1/8 ::1 YOUR_OWN_IP_HERE
Replace YOUR_OWN_IP_HERE with your actual current public IP (check it with curl ifconfig.me from your own machine, not the server). If you manage this server from more than one location — home and office, for example — add all of those IPs here, space-separated.
Next, find the [sshd] section and set the ban behavior:
[sshd]
enabled = true
port = ssh
maxretry = 5
findtime = 10m
bantime = 1h
What these actually control:
maxretry— how many failed attempts are allowed before a ban.findtime— the time window those failures have to happen within to count. Five failures spread across three days won't trigger a ban; five failures in ten minutes will.bantime— how long the ban lasts.1hmeans one hour; you can also use1dfor a day, or-1for a permanent ban.
If your server uses a non-standard SSH port instead of the default 22, set that explicitly instead of relying on port = ssh:
port = 2222
Save the file and exit.
Step 4: Restart Fail2Ban and Verify the Jail Is Active
Apply your changes:
sudo systemctl restart fail2ban
Check that the SSH jail is actually enabled and running:
sudo fail2ban-client status
This lists active jails — you should see sshd in the list. For details on that specific jail, including current bans:
sudo fail2ban-client status sshd
This shows the number of failed attempts, currently banned IPs, and total bans since the jail started.
Step 5: Test That It Actually Works
Don't assume it's working — confirm it. From a different machine (not your whitelisted IP), attempt to SSH into the server with an intentionally wrong password, repeating past your maxretry threshold:
ssh [email protected]
After the number of failed attempts set in maxretry, that IP should be refused entirely — the connection will hang or get actively rejected. Back on the server, confirm the ban registered:
sudo fail2ban-client status sshd
The test IP should now appear under "Banned IP list." You can also check the raw log directly:
sudo tail -f /var/log/fail2ban.log
Step 6: Manage Bans Manually When Needed
To unban a specific IP — useful if you test-banned yourself or a legitimate user got caught:
sudo fail2ban-client set sshd unbanip 203.0.113.89
To clear every ban across all jails at once:
sudo fail2ban-client unban --all
Step 7: Confirm the Firewall Integration
Fail2Ban doesn't block traffic itself — it works by dynamically inserting rules into your existing firewall (UFW, iptables, or nftables, depending on your setup). If you're running UFW, confirm the ban action is actually configured to use it rather than a default that bypasses your existing firewall rules. Check the banaction setting in jail.local:
banaction = ufw
If this is missing or set to something else while you're running UFW as your primary firewall, Fail2Ban may be creating rules through a different backend that don't align with your existing setup. Match banaction to whatever firewall you're actually running.
Going Further: Protecting Other Services
SSH is usually the priority, but Fail2Ban ships with filters for plenty of other services out of the box — Nginx, Apache, Postfix, and more. Enabling a jail for any of them follows the same pattern: find the relevant section in jail.local, set enabled = true, and adjust maxretry/bantime as needed.
For repeat offenders — an IP that keeps coming back after its ban expires — the built-in recidive jail escalates the punishment automatically by watching Fail2Ban's own log for IPs that get banned repeatedly, and applying a longer ban the second time around. This is usually a better long-term approach than setting a permanent ban on every jail by default.
Common Errors and How to Fix Them
| Error Symptom | How to Fix It |
|---|---|
| You've locked yourself out | If you still have console/KVM access through your hosting provider, log in that way and run sudo fail2ban-client set sshd unbanip YOUR_IP. This is exactly why keeping a fallback access method before starting was step one. |
| The jail shows enabled but never bans anything | Usually a log path or backend mismatch. Check sudo fail2ban-client status sshd for the log path being monitored. On systemd-based systems, this is sometimes the journal rather than a flat file, which needs backend = systemd set in the jail. |
| Failed logins aren't triggering a ban fast enough | Double check findtime and maxretry are set correctly. A large findtime window with a high maxretry count will tolerate far more failed attempts than you might assume before banning. |
| Fail2Ban service won't start after editing jail.local | This is almost always a syntax error in the config file. Check the service logs for the specific line: sudo journalctl -u fail2ban -n 50. |
Next Steps
Fail2Ban is one layer of server security, not the whole picture. If you haven't already, make sure SSH key authentication is enforced and password login is disabled, and confirm your firewall (UFW or otherwise) is configured with only the ports you actually need open. Once SSH is locked down, the same jail pattern here extends naturally to protecting any other public-facing service on the server, like Nginx or Apache, using the same jail.local file.
Frequently Asked Questions
Does Fail2Ban replace the need for SSH key authentication?
No. Fail2Ban reduces the noise and risk from brute-force attempts, but it's a second layer, not a replacement. Disabling password authentication entirely and relying on SSH keys remains the stronger underlying protection — Fail2Ban is what catches everything that gets past or around that.
Does changing my SSH port help?
It reduces the volume of automated scanning noise your logs pick up, since most bots only probe the default port 22. It's not a real security measure on its own though — a determined attacker running a full port scan will still find a moved SSH port. Treat it as noise reduction, not protection.
Will Fail2Ban ban legitimate users who mistype their password a few times?
It can, if maxretry is set too aggressively low. A maxretry of 5 within a 10-minute findtime is a reasonable starting point that tolerates normal typos while still catching automated attempts, which typically fire far more than 5 attempts in that window.
How do I know if Fail2Ban is actually stopping real attacks, not just installed and idle?
Check sudo fail2ban-client status sshd periodically — the "Total banned" counter climbing over time confirms it's actively catching and blocking attempts, not just sitting there enabled and unused.