How to Install and Configure Fail2ban on Linux: Hands-On Defense Against SSH and Nginx Brute Force

Security tutorial - IT technology blog
Security tutorial - IT technology blog

Background: Bots Attack the Moment Your Server Goes Online

Just spun up a fresh VPS with a public IP? Within 10 to 15 minutes, port-scanning bots from all over the internet will start knocking on your door. Through numerous real-world infrastructure audits, I’ve found that the most common vulnerability is leaving SSH and web server ports wide open without any active defense mechanism in place.

Take a look at your SSH log file (/var/log/auth.log on Ubuntu or /var/log/secure on RHEL/CentOS) or your Nginx access log (/var/log/nginx/access.log). You’ll typically see thousands of failed login attempts every single day. Attackers constantly brute-force common usernames like root, admin, and test, or probe for sensitive paths such as /.env, /wp-login.php, and /phpmyadmin.

Fail2ban was built to solve this exact problem. Its operating mechanism is straightforward:

  • Log monitoring: Continuously parses system log files in real time.
  • Pattern matching: Uses regex filters to catch failed authentication attempts or suspicious requests.
  • IP banning: When an IP exceeds the failure threshold (maxretry) within a specified window (findtime), Fail2ban dynamically updates firewall rules (iptables, nftables, or UFW) to block that IP for a defined duration (bantime).

Installing Fail2ban on Linux

Fail2ban is available in the official package repositories of almost all major Linux distributions. The installation takes less than a minute.

1. On Ubuntu / Debian

sudo apt update
sudo apt install fail2ban -y

2. On CentOS / RHEL / AlmaLinux / Rocky Linux

sudo dnf install epel-release -y
sudo dnf install fail2ban fail2ban-systemd -y

Once installed, enable and start the service so Fail2ban launches automatically on boot:

sudo systemctl enable fail2ban
sudo systemctl start fail2ban
sudo systemctl status fail2ban

Configuring Fail2ban to Protect SSH and Nginx

Keep this golden rule in mind: never edit /etc/fail2ban/jail.conf directly. This file gets overwritten during package upgrades. Instead, copy its contents to jail.local to make safe, persistent customizations.

sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local

1. Configuring Global Defaults

Open /etc/fail2ban/jail.local in your preferred text editor:

sudo nano /etc/fail2ban/jail.local

Locate the [DEFAULT] section and configure these fundamental parameters:

[DEFAULT]
# Whitelist trusted IPs (office IP, home static IP, localhost)
ignoreip = 127.0.0.1/8 ::1 203.0.113.50

# Default ban duration (1h = 1 hour, 1d = 1 day)
bantime  = 1h

# Time window for tracking cumulative failures (10 minutes)
findtime  = 10m

# Maximum retry attempts before an IP gets banned
maxretry = 5

# Incremental ban time: automatically increase penalties for repeat offenders
bantime.increment = true
bantime.factor = 2
bantime.maxtime = 4w

The bantime.increment = true directive is invaluable in production. On a first offense, an IP is banned for 1 hour. If it resumes attacking right after the ban expires, the penalty doubles to 2 hours, then 4 hours, scaling up to a maximum of 4 weeks (4w). This effectively neutralizes persistent brute-force botnets.

2. Protecting SSH (sshd)

Scroll down to the [sshd] section or append the following block to your jail.local file:

[sshd]
enabled = true
port    = ssh
logpath = %(sshd_log)s
backend = systemd
maxretry = 3
findtime = 15m
bantime  = 24h

Note: If you run SSH on a custom port (such as 2222), update the directive to port = 2222 so Fail2ban applies firewall rules to the correct port.

3. Protecting Nginx

On Nginx web servers, three attack vectors are most prevalent: HTTP Basic Auth brute force, sensitive source file enumeration, and aggressive request flooding.

Add these specialized jails to jail.local:

# 1. Block HTTP Basic Auth brute force (password-protected admin/staging areas)
[nginx-http-auth]
enabled = true
port    = http,https
logpath = /var/log/nginx/error.log
maxretry = 3
bantime  = 12h

# 2. Block bots scanning for leaked files (.env, .git, wp-admin, phpmyadmin)
[nginx-botsearch]
enabled  = true
port     = http,https
logpath  = /var/log/nginx/access.log
maxretry = 2
findtime = 10m
bantime  = 48h

# 3. Block clients that exceed rate limits configured in Nginx
[nginx-limit-req]
enabled  = true
port     = http,https
logpath  = /var/log/nginx/error.log
findtime = 5m
maxretry = 5
bantime  = 1h

4. Creating a Custom Filter to Block 404 Scanning Bots

Automated vulnerability scanners often send hundreds of requests per minute hunting for backup files (like backup.zip or db.sql). We can create a custom filter to ban IPs generating an excessive volume of 404 Not Found errors.

Create the filter configuration file at /etc/fail2ban/filter.d/nginx-404-scan.conf:

[Definition]
failregex = ^<HOST> - .* "(GET|POST|HEAD).*".* 404 .*
ignoreregex =

Declare the new jail at the end of /etc/fail2ban/jail.local:

[nginx-404-scan]
enabled  = true
port     = http,https
filter   = nginx-404-scan
logpath  = /var/log/nginx/access.log
findtime = 1m
maxretry = 20
bantime  = 2h

This rule is simple: if an IP triggers 20 404 errors within 1 minute, Fail2ban immediately blocks it for 2 hours.

Restart the service to load all new configurations:

sudo systemctl restart fail2ban

Monitoring and Daily Operations

The fail2ban-client CLI is your primary utility for monitoring and managing active jails.

1. Checking Overall Jail Status

sudo fail2ban-client status

Sample output when jails are running normally:

Status
|- Number of jail:      4
`- Jail list:   nginx-404-scan, nginx-botsearch, nginx-http-auth, sshd

2. Viewing Banned IPs for a Specific Jail

To inspect how many bots the SSH jail has caught:

sudo fail2ban-client status sshd
Status for the jail: sshd
|- Filter
|  |- Currently failed: 2
|  |- Total failed:     45
|  `- File list:        /var/log/auth.log
`- Actions
   |- Currently banned: 3
   |- Total banned:     12
   `- Banned IP list:   198.51.100.23 203.0.113.11 192.0.2.88

3. Troubleshooting: Unbanning Your Own IP

Entering the wrong SSH password multiple times can accidentally lock you out. If you connect via mobile data, a backup network, or your VPS provider’s web console, run the following commands to unban your IP:

# Unban a specific IP in the sshd jail
sudo fail2ban-client set sshd unbanip 203.0.113.50

# Or unban the IP across all active jails
sudo fail2ban-client unban 203.0.113.50

4. Monitoring Logs in Real Time

To watch Fail2ban detect and ban malicious IPs live:

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

Whenever an IP violates a rule, a clear notice is logged:

2026-10-07 10:15:22,418 fail2ban.actions [1452]: NOTICE [sshd] Ban 198.51.100.23
2026-10-07 10:20:05,112 fail2ban.actions [1452]: NOTICE [nginx-botsearch] Ban 192.0.2.14

With just 5 minutes of configuration, you’ve set up a solid automated perimeter defense. Your server can now focus CPU cycles on serving legitimate users instead of wasting resources on thousands of junk requests every day.

Share: