Repeated failed SSH logins usually mean password spraying, bot scans, or stale automation hitting a public endpoint. Automatically banning the addresses that keep failing reduces noise in the logs and forces repeated guesses off the server before they can continue cycling through credentials.
On current Ubuntu and Debian systems, fail2ban watches authentication failures that match the packaged sshd jail and adds temporary firewall bans when one address crosses the configured retry threshold. The normal workflow is to leave the packaged jail files alone and add only a local override for the retry limit, detection window, and ban duration.
Automatic bans can also catch legitimate administrators when several people share one public address behind NAT, a VPN concentrator, or a bastion host. Keep the current administrative session open while testing, store local changes in /etc/fail2ban/jail.d/ or /etc/fail2ban/jail.local/ instead of editing the packaged /etc/fail2ban/jail.conf/, and confirm the ban from a second host before relying on it in production.
Related: How to manage failed login attempts in SSH
Related: How to show failed SSH attempts
$ whoami user
Do not close the current administrative session until a second host confirms that the new ban settings work as expected.
$ sudo apt install --assume-yes fail2ban Reading package lists... Building dependency tree... Reading state information... fail2ban is already the newest version. 0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.
On current Ubuntu packages, /etc/fail2ban/jail.d/defaults-debian.conf keeps the packaged sshd jail enabled by default.
$ sudoedit /etc/fail2ban/jail.d/sshd.local
[sshd] enabled = true maxretry = 5 findtime = 10m bantime = 1h
Place the same block in /etc/fail2ban/jail.local if that is the override file your system already uses. Do not edit /etc/fail2ban/jail.conf directly because package upgrades can replace it.
If several administrators share one public source address, shorten the ban or exempt that trusted address or subnet before using longer bans.
$ sudo fail2ban-client --test OK: configuration test is successful
A successful test confirms that the local jail file parses cleanly and that the daemon can load the updated sshd jail.
$ sudo systemctl enable --now fail2ban Synchronizing state of fail2ban.service with SysV service script with /usr/lib/systemd/systemd-sysv-install. Executing: /usr/lib/systemd/systemd-sysv-install enable fail2ban $ sudo systemctl is-enabled fail2ban enabled $ sudo systemctl is-active fail2ban active
The package may already start fail2ban automatically, but the command above makes the boot-time state explicit and confirms the service is active now.
$ sudo fail2ban-client reload OK
Use sudo systemctl restart fail2ban instead if the daemon is not responding to a normal reload.
$ sudo fail2ban-client status sshd Status for the jail: sshd |- Filter | |- Currently failed: 0 | |- Total failed: 0 | `- Journal matches: _SYSTEMD_UNIT=sshd.service + _COMM=sshd `- Actions |- Currently banned: 0 |- Total banned: 0 `- Banned IP list:
The current Ubuntu package resolves the packaged sshd jail to the systemd backend with the nftables ban action, so an explicit /var/log/auth.log path is not required on that default setup.
$ sudo fail2ban-client get sshd maxretry 5 $ sudo fail2ban-client get sshd findtime 600 $ sudo fail2ban-client get sshd bantime 3600
findtime and bantime are reported in seconds even when the configuration file uses abbreviations such as 10m or 1h.
$ sudo tail --lines 6 /var/log/fail2ban.log 2026-04-14 03:57:07,646 fail2ban.filter [84]: INFO maxRetry: 5 2026-04-14 03:57:07,646 fail2ban.actions [84]: INFO banTime: 3600 2026-04-14 03:58:12,306 fail2ban.filter [84]: INFO [sshd] Found 198.51.100.24 - 2026-04-14 03:58:12 2026-04-14 03:58:16,023 fail2ban.filter [84]: INFO [sshd] Found 198.51.100.24 - 2026-04-14 03:58:15 2026-04-14 03:58:19,772 fail2ban.filter [84]: INFO [sshd] Found 198.51.100.24 - 2026-04-14 03:58:19 2026-04-14 03:58:29,028 fail2ban.actions [84]: NOTICE [sshd] Ban 198.51.100.24
The address shown above is a documentation placeholder. Your server log will show the real remote address that crossed the retry threshold.
$ sudo fail2ban-client status sshd Status for the jail: sshd |- Filter | |- Currently failed: 0 | |- Total failed: 5 | `- Journal matches: _SYSTEMD_UNIT=sshd.service + _COMM=sshd `- Actions |- Currently banned: 1 |- Total banned: 1 `- Banned IP list: 198.51.100.24
Generate the test failures from a different host or a temporary source address, not from the only SSH session you have left open to the server.