SSD Nodes Learn Hosting plans →
How to do am Matt ConnorBy Matt Connor

Install Fail2ban Ubuntu 24.04 Stop SSH Bots

For Ubuntu 24.04, plain apt install already bans SSH brute force. Check fail2ban-client status sshd, then fix am if Total failed stays at 0.

Wetín Fail2ban actually dey do

Fail2ban na daemon wey dey read logs. E dey monitor your SSH authentication messages. After e see several failed attempts from one address inside short time, e go run firewall command wey block that address for some time. Na the whole idea be that. Na about thirty lines of config for one file. For Ubuntu 24.04, installation na one apt command, and e go protect you before you edit anything.

Make you understand wetín e be and wetín e no be. Fail2ban no dey authenticate anybody. E no dey encrypt anything. E no dey stop one determined login attempt either. E only dey stop repeated attempts from the same source. Na noise filter and rate limiter e be, no be lock. E job na to stop the constant background scanning of port 22 from wasting your CPU, your bandwidth, and your log space. E also dey slow attacker wey must come from one address at a time.

Wetin Fail2ban no replace

Fail2ban na the third layer, e no be the first. If your server still dey accept SSH passwords, botnet wey spread across thousands of addresses fit continue to guess, because each address dey below your ban threshold and e never trigger am. The real defence against this na key-only authentication, wey make password guessing impossible no matter how many attempts anybody make. Fail2ban on top of key-only auth dey do two useful things: e remove brute-force noise from your logs, and e evict scanners early so dem stop hammering the port. Treat am as defence in depth. E dey behind key authentication and behind firewall; e no dey in front of dem.

Prerequisites, and the Ubuntu 24.04 reality

You need one VPS wey dey run Ubuntu 24.04 with root or sudo access, and SSH must dey work, preferably with key authentication. Fail2ban dey frugal: e dey use a few tens of megabytes of RAM, and you no need tune any limit.

Now the part wey every old guide dey get wrong. For years, the standard advice na "install Fail2ban, then add backend = systemd, because Ubuntu don stop to dey write /var/log/auth.log." That advice only dey work for some images. One stock Ubuntu 24.04 server or cloud image still dey install rsyslog, wey e default rules dey write auth,authpriv.* to /var/log/auth.log, so for most VPSes, SSH failures dey land for that file and also for the systemd journal. Minimal images (the Ubuntu 24.04 minimal cloud image dey ship without rsyslog) and most containers dey log SSH only to the journal, and for there, the text file truly no dey. Either way, for Ubuntu 24.04, the Fail2ban package don already account for am. The package dey drop /etc/fail2ban/jail.d/defaults-debian.conf, and that file, no be the upstream defaults, na wetin your server actually dey run:

[DEFAULT]
banaction = nftables
banaction_allports = nftables[type=allports]
backend = systemd

[sshd]
enabled = true

Read that one well, because e dey settle two questions before you touch anything. backend = systemd mean say the SSH jail dey read the journal, so e no matter whether your image dey write auth.log or not. banaction = nftables mean say dem dey enforce bans through nftables, wey be the firewall wey Ubuntu 24.04 actually dey use, instead of the old iptables. And [sshd] enabled = true mean say the jail dey on from the first boot. The result: one stock apt install fail2ban for Ubuntu 24.04 dey ban SSH brute-force out of the box. Most of your work na to confirm that, tune the policy, and make sure you no lock yourself out.

The old auth.log trap still dey bite for three situations, and e good make you recognize dem: you install Fail2ban with pip instead of apt, so defaults-debian.conf no dey; you dey inside one unprivileged container wey no get systemd journal to read; or you follow one old tutorial and paste backend = auto inside your own jail.local, wey come override the default wey dey work. The first and third one only dey break SSH banning for image wey no get rsyslog, like minimal image, because where rsyslog dey run, the file backend dey find /var/log/auth.log and e dey work. The failure-modes section dey show exactly how each one dey look.

Step 1: Install am confirm say e don dey ban already

sudo apt update
sudo apt install -y fail2ban

Ubuntu 24.04 release Fail2ban 1.0.2, and the package dey pull in python3-systemd as hard dependency, so journal backend get everything wey e need. The service enable and start by itself:

sudo systemctl status fail2ban

You want active (running). Then check the jail wey don already dey do im work:

sudo fail2ban-client status sshd

For public VPS wey don dey reachable even for some minutes, you go often see failures wey dem don count and addresses wey dem don ban, because internet scans port 22 continuously. Na this dey prove say stock config dey work. From here, you dey refine am, no be say you dey build am from zero.

Step 2: Edit jail.local, no be jail.conf

Fail2ban dey keep im upstream defaults inside /etc/fail2ban/jail.conf. No edit dat file. If package upgrade happen, e fit replace am, as di header for di file sef warn you, and all your edits go loss. Fail2ban dey read files for one fixed order: jail.conf first, den jail.d/*.conf follow alphabetical order, den jail.local, den jail.d/*.local follow alphabetical order, and di last value wey e see na im go win. So jail.local go override di packaged defaults-debian.conf, but any *.local file wey you drop inside jail.d/ go dey read after am and override am again; make you keep all your overrides for one place so you go know which value dey active. Di .local file na your own, and package upgrades no dey ever touch am. Di same rule apply to filters, where one *.local file go override di one wey dem ship as filter.d/*.conf.

So, you go write one small jail.local wey go override only di few settings wey concern you, and you go leave both jail.conf and di packaged jail.d/defaults-debian.conf as dem be, make dem serve as reference.

Step 3: Write /etc/fail2ban/jail.local

sudo nano /etc/fail2ban/jail.local

Put this one inside, change the address for the ignoreip line to your own public IP:

[DEFAULT]
# Ubuntu 24.04 already sets these two in jail.d/defaults-debian.conf.
# Pinning them here documents the dependency and survives if that
# file is ever removed or changed by an upgrade.
backend   = systemd
banaction = nftables

# Ban for one hour ...
bantime  = 1h
# ... if an address fails ...
maxretry = 5
# ... 5 times within 10 minutes.
findtime = 10m

# Never ban these. PUT YOUR OWN IP HERE.
ignoreip = 127.0.0.1/8 ::1 10.0.0.24

# Longer bans for repeat offenders: 1h, 2h, 4h ... up to a week.
bantime.increment = true
bantime.maxtime   = 1w

[sshd]
enabled = true

Every line get im place for here:

  • bantime, findtime, maxretry na the policy. The shipped default bantime na only ten minutes; one hour dey more reasonable as minimum. If one address fail five times within ten minutes, e go earn ban. Real people fit type password wrong once or twice; five failures within ten minutes mean say na script.
  • ignoreip na your safety belt. Put the public address wey you dey connect from here, so Fail2ban no go ever lock you out from your own server. If your home connection dey change IP, na reason to prefer the VPN approach for the end, no be reason to skip this line.
  • bantime.increment = true make every repeat ban longer pass the last one: one hour, then two, then four, reach bantime.maxtime. Addresses wey keep coming back go progressively get locked out.

Find the address to whitelist from the machine wey you dey SSH from, no be from the server:

curl -s ifconfig.me

You fit generate a jail.local wey match your ports and ban policy here, then paste am inside the file:

ToolFail2ban jail generator

Step 4: Restart and verify say e dey read the journal

sudo fail2ban-client -t
sudo systemctl restart fail2ban
sudo fail2ban-client status sshd

Di -t go first run config test, so if you make mistake for jail.local, e go fail sharp-sharp instead of make the service just die. If the jail status dey correct, e go look like this:

Status for the jail: sshd
|- Filter
|  |- Currently failed: 0
|  |- Total failed:     14
|  `- Journal matches:  _SYSTEMD_UNIT=sshd.service + _COMM=sshd
`- Actions
   |- Currently banned: 1
   |- Total banned:     3
   `- Banned IP list:   203.0.113.66

The number wey show say Fail2ban dey truly read your logins na Total failed. If the number pass zero, or e dey increase as you deliberately fail login from another machine, e mean say e dey read the journal and you don finish. If the number still dey 0 even after you fail login plenty times, and you sure say you no dey test from the address wey dey ignoreip, check the failure modes wey dey below.

Make you notice say the Journal matches line still mention sshd.service. For Ubuntu, the SSH unit na actually ssh.service, but the filter wey dem ship still match _COMM=sshd, and OpenSSH for 24.04 dey log failure from process wey dem call sshd, so the match dey work. That detail only matter if you dey use newer OpenSSH (9.8 or later, where the per-connection worker na sshd-session); the failure modes don cover that case.

Step 5: Watch as ban happen, or force one to test

Real bans go happen by demsef within minutes for any public VPS. To watch one, tail the log:

sudo tail -f /var/log/fail2ban.log

Ban go look like this:

2026-07-15 10:31:40,502 fail2ban.filter  [812]: INFO    [sshd] Found 203.0.113.66 - 2026-07-15 10:31:40
2026-07-15 10:31:44,118 fail2ban.actions [812]: NOTICE  [sshd] Ban 203.0.113.66

To prove say the machinery dey work from start to finish without wait, ban one documentation address by hand, but no ban your own. 203.0.113.66 dey inside TEST-NET-3, one block wey dem reserve for documentation, so e no fit be real host or private address for your provider network:

sudo fail2ban-client set sshd banip 203.0.113.66

E go print 1, and the address go show under Banned IP list for fail2ban-client status sshd. Now confirm say the block truly dey for the firewall. For Ubuntu 24.04, na nftables be the one, no be iptables:

sudo nft list table inet f2b-table

You go see set wey dem name addr-set-sshd wey hold 203.0.113.66, and one chain f2b-chain wey dey reject any source wey dey inside that set. If fail2ban-client talk say address dey banned but nothing show for nft list, your ban action no match your firewall, check the nftables/iptables note for the failure modes.

Step 6: Unban yourself, and recover if you dey locked out

If you ban one address wey you no suppose ban, even if na your own, remove am like this:

sudo fail2ban-client set sshd unbanip 203.0.113.66

E go return 1 if e work. To clear every ban for all jails:

sudo fail2ban-client unban --all

No rely on SSH session wey you don already open to save you: the nftables ban go reject every packet from the banned address to port 22, even connections wey don already establish, so the session go freeze immediately the ban land. If you ban yourself and you no get ignoreip entry, you go dey locked out until the ban expire. Recover am through your provider web console (VNC or serial), wey no dey pass through SSH, and either wait make bantime finish or run the unban command for inside there.

Step 7: Make bans dey persist and increase

Fail2ban dey keep active bans for small SQLite database at /var/lib/fail2ban/fail2ban.sqlite3, so dem go survive service restart or reboot; you no go lose dem. The bantime.increment lines wey you don add turn every repeat offender into bigger problem for demself, as the ban time dey roughly double from one hour reach about one week.

For system-wide "three strikes" policy on top this one, Fail2ban ship with recidive jail wey dey monitor its own /var/log/fail2ban.log and give long bans to any address wey dem don ban repeatedly across all jails. Since your [DEFAULT] now dey use systemd backend, pin this jail back to the log file wey e dey designed to read:

[recidive]
enabled  = true
backend  = auto
logpath  = /var/log/fail2ban.log
bantime  = 1w
findtime = 1d
maxretry = 5

backend = auto with the explicit logpath make recidive continue reading the plain fail2ban.log, na there the Ban lines wey e dey count dey appear. The systemd default wey you set globally for this jail go point am to the journal, but those lines no dey there.

8: Amala am na key-only SSH, VPN dey even better

Fail2ban dey useful only when you combine am with key authentication. For one drop-in file inside /etc/ssh/sshd_config.d/, like /etc/ssh/sshd_config.d/00-hardening.conf, set:

PasswordAuthentication no
KbdInteractiveAuthentication no

Then sudo systemctl restart ssh. Once password authentication off, brute force no fit succeed at all. Fail2ban then dey reduce log noise and remove scanners early. Better pass that one na to keep SSH completely away from public internet: put SSH behind self-hosted WireGuard VPN and firewall port 22 so e only answer through the tunnel. Nobody fit brute-force port wey dem no fit reach, and Fail2ban go become backup protection instead of the first line.

Fail2ban no be only for SSH. Any service wey dey log failed logins fit get jail, whether na mail server, nginx site, or self-hosted Vaultwarden password manager wey you no wan leave open to credential stuffing. Once web app dey behind nginx site with a Let's Encrypt certificate, point Fail2ban filter to the access log the same way SSH jail dey point to the journal.

Failure modes, wit di exact strings wey you go see

"Have not found any log file for sshd jail", and Fail2ban no go start. Dis na di old auth.log wahala, and for Ubuntu 24.04 you go only see am where /var/log/auth.log no dey (image wey no get rsyslog, like minimal image or container) and di journal no dey read: pip install wey no get defaults-debian.conf, container wey no get journal, or one stray backend = auto wey you paste inside jail.local. For file backend wey no get /var/log/auth.log, di sshd jail no fit find im log and di whole daemon go stop. fail2ban.log go show:

ERROR   Failed during configuration: Have not found any log file for sshd jail

Because dat error dey fatal, di service no go ever start, and fail2ban-client status go come report di downstream symptom:

ERROR  Failed to access socket path: /var/run/fail2ban/fail2ban.sock. Is fail2ban running?

Dat "socket path" line no mean say Fail2ban don spoil, e mean say e no start because one jail no fit find im log. To set backend = systemd inside [DEFAULT], wey di Ubuntu package don already do for you, go fix both messages at once.

Jail dey active but Total failed no dey move. Di daemon dey run and di journal dey read, but real failures dey pile up for journalctl -u ssh while di counter still dey 0. First, check di obvious: you dey test from address wey dey inside ignoreip, so your own failures no go count by design. If dat one no be di reason, you dey use OpenSSH build where di per-connection worker na sshd-session (9.8 and later), wey im journal _COMM be sshd-session, no be sshd, so di default match no go see am. Make you widen di match for di [sshd] block:

[sshd]
enabled      = true
backend      = systemd
journalmatch = _SYSTEMD_UNIT=ssh.service + _COMM=sshd + _COMM=sshd-session

Restart, fail login on purpose from address wey no dey inside ignoreip, and confirm say Total failed don finally dey climb.

You ban yourself: Connection refused. You forget to put your own address inside ignoreip, you test some bad logins, and now:

ssh: connect to host 10.0.0.10 port 22: Connection refused

Di refusal, no be silent timeout, na di nftables action default reject verdict wey dey do im work on you. Fix am like for Step 6: unban from session wey dey different, unbanned address, or from di provider console, because session wey you already open from di banned address go freeze too. Den add your address to ignoreip so e no go happen again.

Fail2ban talk say address dey banned, but e still fit connect. Di counter for status sshd dey rise, but di address still dey reach port 22. Dis na ban-action-versus-firewall mismatch, and for Ubuntu 24.04 e almost always mean say you override di working banaction = nftables wit banaction = iptables-multiport wey you copy from old guide, for box wey no get iptables layer. fail2ban.log go show:

fail2ban.actions [812]: ERROR  Failed to execute ban jail 'sshd' action 'iptables-multiport'

Delete dat override and make di packaged nftables action stand, or, if you dey manage your firewall wit ufw and you want make bans show for dere, set banaction = ufw inside [DEFAULT]. Restart and confirm say di rule don show wit sudo nft list ruleset | grep f2b.

Fail2ban no go start after you edit jail.local. Typo, stray heading or bad time value go make di service refuse to start. Tell Fail2ban make e check di config before e run:

sudo fail2ban-client -t

E go call di file and di jail wey get di problem, for example Errors in jail 'sshd'. Skipping..., so you go fit fix di source instead of to dey guess.

FAQ

Fail2ban wey come wit Ubuntu 24.04 dey ban SSH attacks?

Yes. The package dey ship /etc/fail2ban/jail.d/defaults-debian.conf, wey dey enable the sshd jail, e dey set backend = systemd so e go fit read the systemd journal whether your image dey write /var/log/auth.log or not, and e dey set banaction = nftables so dem go enforce bans through the real firewall of Ubuntu. One plain apt install fail2ban dey protect SSH from the moment you first boot the system. Confirm am wit sudo fail2ban-client status sshd and check for one Total failed wey no be zero.

Why Fail2ban no dey ban anything for my server?

Check these three common reasons one by one. You fit dey test from one address wey dey inside ignoreip, wey dem don exempt by design. You fit don override the default wey dey work by pasting backend = auto inside jail.local from one old guide, wey dey break SSH banning for image wey no get rsyslog and so no get auth.log. Or you fit dey inside container wey no get systemd journal to read at all. Check Total failed inside fail2ban-client status sshd: if the number no dey increase while journalctl -u ssh dey show real failures, the jail dey read the wrong place.

How I go fit unban my own IP address?

Run sudo fail2ban-client set sshd unbanip YOUR.IP.HERE, wey go return 1 if e work, or run sudo fail2ban-client unban --all to clear every ban. If you no fit access SSH again, use the web or VNC console wey your provider give you to run the same command, because the ban dey reject every packet from your address to port 22, so even session wey you don open before go stop to work. Then add your address to ignoreip so e no go happen again.

Wetin be the difference between jail.conf and jail.local?

jail.conf dey hold the default settings from Fail2ban upstream and package upgrade fit replace am, so any edit wey you do there dey at risk. The Debian/Ubuntu package dey layer im own settings on top through jail.d/defaults-debian.conf. Your changes belong in jail.local, wey the system dey read after both and e dey override dem, plus upgrade no dey touch am. Only one *.local file inside jail.d/ dey get read later pass that one, so make you no leave any stray file there. Make you leave jail.conf as reference wey you no go edit.

Fail2ban dey replace key-based SSH authentication?

No. Fail2ban dey limit the rate of repeated failures from one address; e no dey do anything against slow, distributed guessing where each address dey stay under the threshold. Key-only authentication (PasswordAuthentication no) dey make password guessing impossible entirely, and Fail2ban go come reduce log noise and comot scanners early. Run both, and as much as possible, make you keep SSH comot from the public internet entirely.