If your Ubuntu server has SSH exposed to the internet, it is being attacked right now. Automated bots scan the entire web around the clock, hammering port 22 with thousands of password guesses against usernames like root and admin. A strong password or SSH key keeps them out, but it does nothing to stop the noise, and the noise itself costs CPU, log space, and your attention.
Fail2Ban fixes this. It watches your log files, spots repeated failed login attempts, and automatically blocks the offending IP address with a firewall rule for a set period. In this guide you will install Fail2Ban on Ubuntu 24.04, configure it to protect SSH, verify it is actually banning attackers, and learn how to extend it to Nginx and other services. Everything here uses real, current commands, tested against the Fail2Ban version shipped with Ubuntu 24.04.
Prerequisites
- An Ubuntu 24.04 or 22.04 server (a VPS, cloud VM, or local machine) with SSH access.
- A non-root user with
sudoprivileges. - UFW installed and enabled as your firewall. If you have not set it up yet, run
sudo ufw allow sshfollowed bysudo ufw enablebefore continuing, or you risk locking yourself out. - Your own public IP address handy, so you can whitelist it and never ban yourself by accident.
Step 1: Install Fail2Ban
Fail2Ban is in Ubuntu's official repositories, so installation is one command. First refresh the package list so you get the current version:
sudo apt update
sudo apt install -y fail2ban
The first command updates the local package index; the second installs the fail2ban package and its Python dependencies. Ubuntu 24.04 ships Fail2Ban 1.0.2, which is current and stable. Confirm the install with:
fail2ban-server --version
This prints the installed version, for example Fail2Ban v1.0.2. If you see a version number, the binaries are in place and ready to configure.
Step 2: Start and enable the service
Installation alone does not start Fail2Ban. Enable it so it launches on every boot, and start it now:
sudo systemctl enable --now fail2ban
The enable flag registers Fail2Ban to start at boot; --now also starts it immediately. Check that it is running:
sudo systemctl status fail2ban
Look for Active: active (running) in green. You can also ask Fail2Ban itself whether it is alive:
sudo fail2ban-client ping
A healthy server answers with Server replied: pong. Out of the box, Fail2Ban ships with only the sshd jail enabled on Ubuntu, but the stock settings are generic. The next steps tune them properly.
Step 3: Create jail.local (never edit jail.conf)
Fail2Ban reads two files: /etc/fail2ban/jail.conf, which ships with the package and gets overwritten on every upgrade, and /etc/fail2ban/jail.local, which is yours and always wins. Never edit jail.conf. Copy it as a starting point:
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
This gives you a complete, commented copy of every available jail and setting. You will only change a handful of values in jail.local, which override the defaults from jail.conf. Edit it with your preferred editor:
sudo nano /etc/fail2ban/jail.local
If you prefer a minimal file instead of a full copy, that works too: Fail2Ban merges jail.local over jail.conf, so a short file containing only the sections you change is enough. A full copy is easier for beginners because every option is visible and documented.
Step 4: Configure the default and SSH jails
Find the [DEFAULT] section near the top of jail.local and set these values:
[DEFAULT]
bantime = 1h
findtime = 10m
maxretry = 5
banaction = ufw
backend = auto
ignoreip = 127.0.0.1/8 ::1 YOUR_SERVER_IP
Here is what each setting does:
bantime = 1h: a banned IP stays blocked for one hour. The stock 10 minutes is too short to deter bots; one hour is a sane production default.findtime = 10mandmaxretry = 5: if an IP fails 5 times within 10 minutes, it gets banned. Real users rarely mistype a password five times in ten minutes; bots do it constantly.banaction = ufw: tells Fail2Ban to block using UFW rules. Ubuntu ships UFW, so this integrates cleanly with the firewall you already manage. Without this, Fail2Ban inserts raw iptables rules that can behave oddly alongside UFW.backend = auto: lets Fail2Ban pick how to read logs. On Ubuntu 24.04 this resolves tosystemd, which reads SSH failures from the journal, the most reliable source.ignoreip: addresses that are never banned. Keep the localhost entries and add your own public IP (and your office or home IP ranges) so you cannot lock yourself out during testing.
Next, scroll to the [sshd] section and make sure it is enabled:
[sshd]
enabled = true
port = ssh
filter = sshd
logpath = %(sshd_log)s
maxretry = 5
The port = ssh line tells Fail2Ban which port to block in the firewall rule. If you run SSH on a custom port, list both: port = ssh,2222. Save the file and exit the editor.
Tip: if you changed the SSH port insshd_config, update theportline here too. Fail2Ban blocks the port you name; naming the wrong one means bans land on port 22 while your real SSH port stays exposed to the attacker.
Step 5: Test the configuration before restarting
Fail2Ban refuses to start with a broken config, and a typo in jail.local can silently disable your SSH protection. Test first:
sudo fail2ban-client -d | head -40
The -d flag dumps the fully merged configuration without starting anything. Scan the output for your [sshd] section and confirm bantime, maxretry, and banaction show the values you set. Then verify the SSH filter actually matches real log lines:
sudo fail2ban-regex /var/log/auth.log /etc/fail2ban/filter.d/sshd.conf
fail2ban-regex runs the jail's regular expressions against your actual auth log and reports how many lines matched as failures. On a server with any internet exposure you should see matches. If it reports zero matches on an exposed server, check that SSH logging is reaching auth.log (or switch the backend to systemd explicitly, covered in the troubleshooting section). Now restart Fail2Ban to apply the new config:
sudo systemctl restart fail2ban
Restart, not reload: Fail2Ban only re-reads jail.local fully on a restart.
Step 6: Check status and see banned IPs
With Fail2Ban running, ask it which jails are active:
sudo fail2ban-client status
You should see output like Jail list: sshd, confirming the SSH jail is up. Drill into it for details:
sudo fail2ban-client status sshd
This shows the filter settings (currently failed attempts, total failures) and the actions section, which lists every currently banned IP under Banned IP list. On a public server, give it a day and this list will fill up, which is exactly the point. You can watch bans happen live in the Fail2Ban log:
sudo tail -f /var/log/fail2ban.log
Each ban appears as a Ban 203.0.113.44 line, and unbans appear as Unban 203.0.113.44 when the bantime expires. Press Ctrl+C to stop watching.
Step 7: Safely simulate a ban (optional but recommended)
Before trusting your protection, prove it works with a harmless test IP from the documentation range, which is never a real attacker:
sudo fail2ban-client set sshd banip 203.0.113.5
This manually bans the test address using the same ufw action Fail2Ban uses for real attacks. Confirm it landed:
sudo fail2ban-client status sshd
sudo ufw status | grep 203.0.113.5
The first command should list 203.0.113.5 under banned IPs; the second shows the UFW rule blocking it. Clean up the test ban:
sudo fail2ban-client set sshd unbanip 203.0.113.5
If you ever ban yourself by accident (it happens to everyone once), this unbanip command is the rescue. Run it from another machine or your hosting provider's web console.
Step 8: Protect more than SSH (optional)
Fail2Ban ships with dozens of ready-made jails. Two worth enabling on most servers:
[recidive]
enabled = true
[nginx-http-auth]
enabled = true
Add these to jail.local. The recidive jail watches Fail2Ban's own log and permanently (well, for bantime = 1w by default) bans IPs that get banned repeatedly, escalating against persistent bots. The nginx-http-auth jail protects password-protected Nginx areas. Restart Fail2Ban after editing:
sudo systemctl restart fail2ban
sudo fail2ban-client status
The jail list should now show sshd, recidive, nginx-http-auth. There are also jails for Apache (apache-auth), WordPress (wordpress-hard), Postfix, and Dovecot; browse jail.conf for the full catalog and enable only what you actually run.
Verify it worked
Run this three-point check:
sudo systemctl is-active fail2banshould printactive.sudo fail2ban-client status sshdshould show the jail with yourmaxretryandbantimevalues, plus a growingCurrently failedcounter on any internet-exposed server.sudo ufw statusshould show Fail2Ban's deny rules near the top of the list, ahead of your allow rules. Fail2Ban inserts its rules at the top so bans take priority.
For the ultimate proof, deliberately fail 6 SSH logins from a second machine or phone (wrong password, not a wrong username flood), then check fail2ban-client status sshd: your test IP should appear in the banned list, and new SSH attempts from it should time out instead of prompting for a password.
Common issues and fixes
1. Fail2Ban runs but never bans anyone
The most common cause on Ubuntu 24.04 is a log backend mismatch: the sshd jail looks for failures in a log file that does not exist because your system logs SSH only to the systemd journal. Fix it by forcing the journal backend in jail.local:
[sshd]
backend = systemd
Restart Fail2Ban and re-run fail2ban-regex against the journal with sudo fail2ban-regex systemd-journal /etc/fail2ban/filter.d/sshd.conf. If matches appear, the backend was the problem.
2. You banned your own IP
It happens when testing from the same network you forgot to whitelist. From any other connection (mobile data, provider console), run:
sudo fail2ban-client set sshd unbanip YOUR_IP
Then add your IP to ignoreip in jail.local and restart Fail2Ban so it never happens again.
3. Bans do not seem to block traffic (UFW conflict)
This happens when UFW is disabled or when Fail2Ban uses the default iptables action while you manage rules in UFW. Confirm UFW is active with sudo ufw status (it must say Status: active), and confirm banaction = ufw is set in the [DEFAULT] section of jail.local. Restart Fail2Ban after changing it.
4. SSH runs on a custom port and attacks still get through
Fail2Ban blocks only the ports listed in the jail's port setting. If SSH listens on 2222, set port = ssh,2222 in the [sshd] section of jail.local and restart. Verify with sudo ufw status that the deny rules reference port 2222.
5. Fail2Ban fails to start after editing jail.local
Almost always a syntax error: a missing section header, a stray space before a setting name, or an unclosed quote. Run sudo fail2ban-client -d; it prints the exact line that breaks parsing. Fix it, then sudo systemctl restart fail2ban. When in doubt, restore from your backup copy of jail.conf and re-apply only the changes from Step 4.
Conclusion
You now have Fail2Ban installed, tuned, and verified: SSH brute-force bots get five guesses per ten minutes, then an hour-long timeout, with repeat offenders escalated by the recidive jail. That single change cuts the vast majority of automated attack noise hitting your server.
Fail2Ban is one layer, not the whole wall. Pair it with SSH key authentication, disabled password logins, and a non-standard SSH port for a genuinely hardened server. If you would rather have an expert set all of this up and keep it monitored, XpertBees offers managed server setup and hardening for Ubuntu servers. Get in touch and we will take it from here.



