Blocking Attacks with Fail2ban
The moment your server is connected to the internet, automated bots start trying to log in. They guess usernames and passwords over and over, hoping to get lucky. This is called a brute-force attack (an attack that tries thousands of password combinations very quickly). Fail2ban is a small, free tool that watches your log files, notices these repeated failed attempts, and automatically blocks the attacking IP address using your firewall. It is one of the highest-value security tools you can install, and it takes about five minutes to set up.
What Fail2ban actually does
Fail2ban works in a simple loop. It reads a log file (a text file where a program records what it is doing, like every login attempt), looks for lines that match a pattern of failure, and counts how many times a single IP address fails. If an IP fails too many times within a set window, Fail2ban tells your firewall to drop (silently reject) all traffic from that IP for a while.
The pieces you configure are:
| Term | Meaning |
|---|---|
| Jail | A rule set for one service (e.g. SSH or Nginx). |
| filter | The pattern that defines what a “failure” looks like in the log. |
| maxretry | How many failures are allowed before a ban. |
| findtime | The time window (in seconds) those failures must happen in. |
| bantime | How long (in seconds) the IP stays banned. |
| action | What to do on a ban — usually add a firewall rule. |
When to use this: any internet-facing server running SSH (the protocol you use to log in remotely), a web server, or a mail server. When NOT to: it is not a replacement for strong passwords, SSH keys, or a firewall — it is an extra layer on top of them.
Installing Fail2ban on Ubuntu
Fail2ban is in the standard Ubuntu repositories, so installation is one command. This works on Ubuntu 22.04 and 24.04 LTS.
sudo apt update
sudo apt install fail2ban -y
The service starts and is enabled (set to launch on boot) automatically. Confirm it is running:
sudo systemctl status fail2ban
Output:
● fail2ban.service - Fail2Ban Service
Loaded: loaded (/usr/lib/systemd/system/fail2ban.service; enabled; preset: enabled)
Active: active (running) since Mon 2026-06-15 09:12:44 UTC; 8s ago
Main PID: 4821 (fail2ban-server)
Tasks: 5 (limit: 4915)
Memory: 12.4M
active (running) and enabled mean it is working and will survive a reboot.
Configuring jails the right way
Fail2ban ships with a default config at /etc/fail2ban/jail.conf. Never edit that file — package updates overwrite it. Instead, create a jail.local file. Fail2ban reads .local files last, so they override the defaults safely.
Create the file:
sudo nano /etc/fail2ban/jail.local
Paste this configuration. The [DEFAULT] section sets global values; each named section is a jail.
[DEFAULT]
# Never ban your own office/home IP. Replace with your real IP.
ignoreip = 127.0.0.1/8 ::1 203.0.113.25
# A failed IP is banned for 1 hour.
bantime = 3600
# Count failures inside a 10-minute window.
findtime = 600
# Ban after 5 failures.
maxretry = 5
# Use ufw to apply bans (matches Ubuntu's default firewall).
banaction = ufw
[sshd]
enabled = true
port = 22
Always put your own IP in
ignoreip. If you fat-finger your password a few times, Fail2ban will happily ban you out of your own server. Theignoreiplist protects you.
Use a longer
bantimefor repeat offenders. Settingbantime = -1bans an IP permanently, which is useful for an IP that keeps coming back after its ban expires.
Adding an Nginx jail
If you run Nginx (a popular web server and reverse proxy — a server that sits in front of your app and forwards requests to it), Fail2ban can ban IPs that flood your site with bad requests or probe for login pages. Add these jails to the same jail.local:
[nginx-http-auth]
enabled = true
port = http,https
logpath = /var/log/nginx/error.log
[nginx-bad-request]
enabled = true
port = http,https
logpath = /var/log/nginx/access.log
maxretry = 10
nginx-http-auth watches for failed HTTP password prompts (the browser pop-up login box). logpath tells Fail2ban which log to read — on Ubuntu, Nginx logs live in /var/log/nginx/.
Apply any config change by reloading the service:
sudo systemctl reload fail2ban
Checking status and bans
Fail2ban comes with a command-line client, fail2ban-client. Use it to see which jails are active.
sudo fail2ban-client status
Output:
Status
|- Number of jail: 3
`- Jail list: nginx-bad-request, nginx-http-auth, sshd
To see who is currently banned in the SSH jail:
sudo fail2ban-client status sshd
Output:
Status for the jail: sshd
|- Filter
| |- Currently failed: 2
| |- Total failed: 1843
| `- File list: /var/log/auth.log
`- Actions
|- Currently banned: 4
|- Total banned: 197
`- Banned IP list: 185.243.96.114 92.118.39.21 45.143.200.18 162.142.125.9
This server has banned 197 IPs since startup, with 4 currently blocked. The Banned IP list shows the brute-forcers being kept out right now.
Banning and unbanning by hand
If you banned yourself or a legitimate user, unban their IP:
sudo fail2ban-client set sshd unbanip 203.0.113.99
You can also manually ban a known-bad IP:
sudo fail2ban-client set sshd banip 198.51.100.7
A real SSH brute-force example
On a fresh internet-facing server, watch the live SSH log to see attacks in real time:
sudo tail -f /var/log/auth.log
Output:
Jun 15 09:31:02 web01 sshd[5120]: Failed password for invalid user admin from 185.243.96.114 port 51234 ssh2
Jun 15 09:31:04 web01 sshd[5120]: Failed password for invalid user admin from 185.243.96.114 port 51240 ssh2
Jun 15 09:31:07 web01 sshd[5120]: Failed password for root from 185.243.96.114 port 51252 ssh2
Jun 15 09:31:09 web01 fail2ban.actions [4821]: NOTICE [sshd] Ban 185.243.96.114
After five failures inside the 10-minute window, Fail2ban printed Ban 185.243.96.114 and added a firewall rule. That attacker can no longer reach the server for the next hour. You can confirm the firewall rule exists:
sudo ufw status | grep 185.243.96.114
Output:
Anywhere DENY 185.243.96.114
Best Practices
- Edit
jail.local, neverjail.conf, so package updates cannot wipe your settings. - Always add your own static IP to
ignoreipto avoid locking yourself out. - Tighten the SSH jail (lower
maxretry, longerbantime) since SSH is the most-attacked service. - Combine Fail2ban with key-based SSH login and a firewall — Fail2ban is a layer, not a cure-all.
- Use
bantime.increment = truein[DEFAULT]so repeat offenders get progressively longer bans. - Run
sudo fail2ban-client status sshdregularly (or alert on it) to confirm bans are happening. - Make sure
banactionmatches your actual firewall (ufwon Ubuntu,nftablesoriptablesotherwise).