How to Install and Configure fail2ban on Ubuntu & Debian
fail2ban monitors your log files and automatically bans IP addresses that show signs of brute-force behavior — too many failed login attempts, malformed requests, or other suspicious patterns. It is one of the most effective and lightweight ways to protect a Linux VPS from automated attacks.
What Does fail2ban Do?
fail2ban reads log files (e.g. /var/log/auth.log for SSH) and applies temporary firewall bans (via iptables or nftables) to IPs that exceed a configurable threshold. For example:
- An IP fails SSH login 5 times in 10 minutes → banned for 1 hour
- An IP hits an HTTP 404 page 50 times in 60 seconds → banned for 24 hours
Bans are temporary by default and lift automatically — or you can make them permanent.
Installation
apt update
apt install fail2ban -y
Start and enable the service:
systemctl enable fail2ban
systemctl start fail2ban
systemctl status fail2ban
Basic Configuration
fail2ban uses two types of config files:
.conffiles — defaults shipped with the package (do not edit these).localfiles — your overrides (create these)
Always create .local files. This way package updates never overwrite your config.
Create the Main Config Override
cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
nano /etc/fail2ban/jail.local
Find the [DEFAULT] section at the top and set these values:
[DEFAULT]
# Ban duration in seconds (3600 = 1 hour, 86400 = 1 day, -1 = permanent)
bantime = 3600
# Window to count failures (600 = 10 minutes)
findtime = 600
# Number of failures before ban
maxretry = 5
# Your own IPs — never ban these
ignoreip = 127.0.0.1/8 ::1 YOUR_HOME_IP
Replace YOUR_HOME_IP with your actual IP so you can never accidentally ban yourself.
Enabling the SSH Jail
The SSH jail is usually already enabled. Verify in jail.local:
[sshd]
enabled = true
port = ssh
logpath = %(sshd_log)s
backend = %(sshd_backend)s
maxretry = 3
bantime = 86400
If your SSH runs on a non-default port, specify it:
[sshd]
enabled = true
port = 2222
logpath = %(sshd_log)s
maxretry = 3
bantime = 86400
Restart fail2ban to apply:
systemctl restart fail2ban
Monitoring fail2ban
Check Active Jails and Ban Status
fail2ban-client status
Output:
Status
|- Number of jail: 1
`- Jail list: sshd
Check a Specific Jail
fail2ban-client status sshd
Output:
Status for the jail: sshd
|- Filter
| |- Currently failed: 2
| |- Total failed: 147
| `- File list: /var/log/auth.log
`- Actions
|- Currently banned: 3
|- Total banned: 28
`- Banned IP list: 185.220.101.5 103.244.19.17 45.33.82.201
View the Fail2ban Log
tail -f /var/log/fail2ban.log
You will see lines like:
2026-05-11 14:23:01 INFO [sshd] Ban 185.220.101.5
2026-05-11 15:23:01 INFO [sshd] Unban 185.220.101.5
Manually Banning and Unbanning IPs
Ban an IP Immediately
fail2ban-client set sshd banip 1.2.3.4
Unban an IP
fail2ban-client set sshd unbanip 1.2.3.4
Permanently Unban (in case you banned yourself)
If you locked yourself out, use the VNC/KVM console:
fail2ban-client set sshd unbanip YOUR_IP
# or flush all bans:
fail2ban-client set sshd action iptables-multiport actionflush
Custom Jails
Protect Nginx from HTTP Brute Force
Create /etc/fail2ban/jail.d/nginx.local:
[nginx-http-auth]
enabled = true
port = http,https
logpath = /var/log/nginx/error.log
maxretry = 5
bantime = 3600
[nginx-botsearch]
enabled = true
port = http,https
logpath = /var/log/nginx/access.log
maxretry = 20
findtime = 60
bantime = 86400
Protect phpMyAdmin
Create /etc/fail2ban/filter.d/phpmyadmin.conf:
[Definition]
failregex = ^<HOST> -.*"(GET|POST).*phpMyAdmin.*" (403|404)
ignoreregex =
Then in /etc/fail2ban/jail.d/phpmyadmin.local:
[phpmyadmin]
enabled = true
port = http,https
filter = phpmyadmin
logpath = /var/log/nginx/access.log
maxretry = 5
bantime = 86400
Restart fail2ban:
systemctl restart fail2ban
fail2ban-client status
Testing Your Configuration
Test a filter against a real log file before enabling it in production:
fail2ban-regex /var/log/auth.log /etc/fail2ban/filter.d/sshd.conf
Output shows how many lines matched the filter — useful for verifying your regex before applying it.
Aggressive Settings for Public-Facing Servers
For high-traffic servers that see constant attack traffic, use stricter defaults:
[DEFAULT]
bantime = 86400 ; 24 hours
findtime = 300 ; 5 minutes
maxretry = 3 ; 3 strikes
[sshd]
enabled = true
maxretry = 2
bantime = 604800 ; 7 days for SSH brute force
Persistent Bans (Ban Survives Restart)
By default, bans are stored in memory and cleared on restart. To persist them:
In jail.local under [DEFAULT]:
dbfile = /var/lib/fail2ban/fail2ban.sqlite3
dbpurgeage = 86400
fail2ban will restore active bans from the database on startup.
fail2ban + UFW
If you use UFW, configure fail2ban to use UFW as the ban action. In jail.local:
[DEFAULT]
banaction = ufw
Bans will appear as UFW rules:
ufw status | grep DENY
Summary
| Command | Purpose |
|---|---|
fail2ban-client status | List all active jails |
fail2ban-client status sshd | Show SSH jail stats & banned IPs |
fail2ban-client set sshd banip IP | Ban an IP manually |
fail2ban-client set sshd unbanip IP | Unban an IP |
systemctl restart fail2ban | Apply config changes |
fail2ban-regex LOG FILTER | Test a filter against log file |