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 sudo privileges.
  • UFW installed and enabled as your firewall. If you have not set it up yet, run sudo ufw allow ssh followed by sudo ufw enable before 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 = 10m and maxretry = 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 to systemd, 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 in sshd_config, update the port line 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:

  1. sudo systemctl is-active fail2ban should print active.
  2. sudo fail2ban-client status sshd should show the jail with your maxretry and bantime values, plus a growing Currently failed counter on any internet-exposed server.
  3. sudo ufw status should 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.