Troubleshoot
Securityfail2banUbuntuDebianSSHHardening

How to Install and Configure fail2ban on Ubuntu & Debian

Install fail2ban to automatically block brute-force attacks on SSH, web servers, and other services. Covers installation, configuration, custom jails, and monitoring.

May 11, 2026·5 min read

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:

  • .conf files — defaults shipped with the package (do not edit these)
  • .local files — 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

CommandPurpose
fail2ban-client statusList all active jails
fail2ban-client status sshdShow SSH jail stats & banned IPs
fail2ban-client set sshd banip IPBan an IP manually
fail2ban-client set sshd unbanip IPUnban an IP
systemctl restart fail2banApply config changes
fail2ban-regex LOG FILTERTest a filter against log file
How to Install and Configure fail2ban on Ubuntu & Debian | VMHeaven Troubleshoot