How to Set Up Fail2ban: Protect Your Server from Brute-Force Attacks

If you expose your home server to the internet, automated attacks will find it. Within minutes of opening SSH or a web service, bots start hammering your server with login attempts.

Fail2ban is your automated security guard. It monitors logs for suspicious activity (repeated failed logins, exploit attempts) and automatically bans offending IP addresses via firewall rules.

In this guide, you’ll learn to install and configure Fail2ban to protect SSH, web services, and more.

What is Fail2ban?

Fail2ban is a log-parsing application that:

  1. Monitors service logs (SSH, Nginx, Apache, etc.)
  2. Detects patterns indicating attacks (failed logins, exploit attempts)
  3. Bans malicious IPs by adding firewall rules (iptables/ufw)
  4. Unbans automatically after a timeout period

Example: After 3 failed SSH login attempts in 10 minutes, Fail2ban blocks the IP for 1 hour.

Why You Need It

Without Fail2ban, attackers can:

  • Brute-force weak passwords (trying thousands of combinations)
  • Launch dictionary attacks against SSH
  • Exploit vulnerabilities by flooding services
  • Consume bandwidth and CPU with automated scans

With Fail2ban:

  • Bots get blocked after a few attempts
  • Your logs stay clean
  • Server resources stay free
  • You get email alerts on suspicious activity

Installation

Ubuntu/Debian

sudo apt update
sudo apt install fail2ban -y

CentOS/RHEL/Rocky Linux

sudo yum install epel-release -y
sudo yum install fail2ban -y

Start and Enable

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

You should see active (running).

Configuration Basics

Fail2ban uses two types of config files:

  • .conf files - Defaults (don’t edit directly)
  • .local files - Your overrides (survive updates)

Main Config File

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

This creates a local override that won’t get erased by updates.

Protecting SSH (Essential)

SSH is the #1 target. Let’s protect it first.

Default SSH Jail

Find the [sshd] section in /etc/fail2ban/jail.local:

[sshd]
enabled = true
port = ssh
filter = sshd
logpath = /var/log/auth.log  # Debian/Ubuntu
# logpath = /var/log/secure   # CentOS/RHEL
maxretry = 3
bantime = 1h
findtime = 10m

What this means:

  • enabled = true - SSH protection active
  • port = ssh - Monitor default port 22 (or custom port)
  • maxretry = 3 - Ban after 3 failed attempts
  • bantime = 1h - Keep IP banned for 1 hour
  • findtime = 10m - Count failures within 10-minute window

Custom SSH Port

If you changed SSH to port 2299:

[sshd]
enabled = true
port = 2299
filter = sshd
logpath = /var/log/auth.log
maxretry = 3
bantime = 1h
findtime = 10m

Restart Fail2ban

sudo systemctl restart fail2ban

Test It

From another machine, try logging in with wrong passwords:

ssh user@your-server  # Enter wrong password 3 times

After 3 attempts, you’ll see:

ssh:connecttohostyour-serverport22:Connectionrefused

Check the ban:

sudo fail2ban-client status sshd

Output:

S`t-atF`A`ui-c-slttCTFiCTBfeuoiouoaorrtlnrtnrraesraneleletnlndhtfitbelaslaIyitynPjl:nafebeliadadili:n:s:lntee:sdds::hd03112v0a3r./0l.o1g1/3a.u4t5h.log

Protecting Web Services

Nginx (General Protection)

Create /etc/fail2ban/jail.d/nginx.local:

[nginx-http-auth]
enabled = true
filter = nginx-http-auth
port = http,https
logpath = /var/log/nginx/error.log
maxretry = 5
bantime = 1h
findtime = 10m

[nginx-noscript]
enabled = true
filter = nginx-noscript
port = http,https
logpath = /var/log/nginx/access.log
maxretry = 6
bantime = 30m

[nginx-badbots]
enabled = true
filter = nginx-badbots
port = http,https
logpath = /var/log/nginx/access.log
maxretry = 2
bantime = 24h

[nginx-noproxy]
enabled = true
filter = nginx-noproxy
port = http,https
logpath = /var/log/nginx/access.log
maxretry = 2
bantime = 24h

Filters explained:

  • nginx-http-auth - Failed HTTP basic auth
  • nginx-noscript - Blocked scripts (.php, .asp, etc.)
  • nginx-badbots - Known malicious user agents
  • nginx-noproxy - Proxy exploit attempts

Apache

[apache-auth]
enabled = true
port = http,https
filter = apache-auth
logpath = /var/log/apache2/error.log
maxretry = 5
bantime = 1h

[apache-badbots]
enabled = true
port = http,https
filter = apache-badbots
logpath = /var/log/apache2/access.log
maxretry = 2
bantime = 24h

Restart After Adding Jails

sudo systemctl restart fail2ban

Advanced Configuration

Whitelist Your IP

Never lock yourself out. Add to /etc/fail2ban/jail.local:

[DEFAULT]
ignoreip = 127.0.0.1/8 ::1 192.168.1.0/24 YOUR_PUBLIC_IP

Replace YOUR_PUBLIC_IP with your home/office IP.

Find your public IP:

curl ifconfig.me

Email Alerts

Get notified when IPs are banned.

Install mail utilities:

sudo apt install mailutils -y

Configure in /etc/fail2ban/jail.local:

[DEFAULT]
destemail = [email protected]
sendername = Fail2ban
action = %(action_mwl)s

action_mwl = ban + send email with logs.

Longer Ban Times for Repeat Offenders

Use the recidive jail to catch repeat attackers:

[recidive]
enabled = true
filter = recidive
logpath = /var/log/fail2ban.log
bantime = 1w
findtime = 1d
maxretry = 3

If an IP gets banned 3 times in 1 day, ban it for 1 week.

Permanent Bans

For persistent attackers, ban permanently:

sudo fail2ban-client set sshd banip 203.0.113.45 -1

-1 = permanent ban.

Protecting Docker Services

If you run Docker containers with exposed ports:

Nextcloud

[nextcloud]
enabled = true
filter = nextcloud
port = http,https
logpath = /var/log/nextcloud/nextcloud.log
maxretry = 3
bantime = 1h

Create filter /etc/fail2ban/filter.d/nextcloud.conf:

[Definition]
failregex = .*Login failed: '.*' \(Remote IP: '<HOST>'.*
ignoreregex =

Vaultwarden (Bitwarden)

[vaultwarden]
enabled = true
filter = vaultwarden
port = http,https
logpath = /path/to/vaultwarden/vaultwarden.log
maxretry = 3
bantime = 12h

Filter /etc/fail2ban/filter.d/vaultwarden.conf:

[Definition]
failregex = ^.*Username or password is incorrect\. Try again\. IP: <HOST>\. Username:.*$
ignoreregex =

Monitoring and Management

Check All Jail Status

sudo fail2ban-client status

Output:

S`t-atNJuuasmiblerliosft:jails:shd,ng3inx-http-auth,recidive

Check Specific Jail

sudo fail2ban-client status sshd

View Banned IPs (All Jails)

sudo fail2ban-client banned

Unban an IP

sudo fail2ban-client unban 203.0.113.45

Or unban all:

sudo fail2ban-client unban --all

Check Logs

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

You’ll see entries like:

2025-01-3110:23:45,123fail2ban.actions[12345]:NOTICE[sshd]Ban203.0.113.45

Testing Your Configuration

Test Filter Regex

sudo fail2ban-regex /var/log/auth.log /etc/fail2ban/filter.d/sshd.conf

Shows how many matches the filter found in your logs.

Simulate Attack (Safe)

From a different machine (not your main workstation):

for i in {1..5}; do ssh wronguser@your-server; done

Check if it got banned:

sudo fail2ban-client status sshd

Validate Config

sudo fail2ban-client -t

Shows configuration errors before restarting.

Firewall Integration

Fail2ban works with:

  • iptables (default)
  • ufw (Ubuntu)
  • firewalld (CentOS)
  • nftables

Check Current Bans (iptables)

sudo iptables -L -n | grep "reject-with"

Check Current Bans (ufw)

sudo ufw status numbered

Fail2ban adds rules like:

[10]REJECTfrom203.0.113.45toany

Performance Tips

Use Systemd Backend (Faster)

Modern distros can use systemd journal instead of log files:

[DEFAULT]
backend = systemd

Then specify journal tags:

[sshd]
enabled = true
filter = sshd[mode=aggressive]
journalmatch = _SYSTEMD_UNIT=sshd.service
maxretry = 3

Limit Log Scanning

For high-traffic servers, limit how far back Fail2ban looks:

[DEFAULT]
maxlines = 10000

Common Issues

“No jail defined” Error

Make sure jail is enabled = true and you restarted:

sudo systemctl restart fail2ban

Logs Not Found

Check log paths match your system:

ls -la /var/log/auth.log    # Debian/Ubuntu
ls -la /var/log/secure      # CentOS/RHEL

Accidentally Locked Yourself Out

Prevention: Always whitelist your IP in ignoreip.

Fix: Access via console (KVM/IPMI) or alternate IP, then unban:

sudo fail2ban-client unban YOUR_IP

Or disable Fail2ban temporarily:

sudo systemctl stop fail2ban

Filter Not Matching

Test regex against logs:

sudo fail2ban-regex /var/log/auth.log /etc/fail2ban/filter.d/sshd.conf --print-all-matched

Security Best Practices

  1. Use with SSH Keys - Fail2ban + key auth = nearly unbreakable
  2. Don’t Rely on Fail2ban Alone - It’s a layer, not a complete solution
  3. Monitor Logs - Check weekly for unusual ban patterns
  4. Update Filters - New exploits need new filters
  5. Whitelist Carefully - Don’t whitelist entire subnets unless necessary

Configuration Checklist

  • Fail2ban installed and running
  • SSH jail enabled with correct port
  • Your IP whitelisted in ignoreip
  • Web service jails configured (Nginx/Apache)
  • Email alerts enabled (optional)
  • Recidive jail for repeat offenders
  • Tested with simulated attack
  • Firewall rules verified (iptables -L or ufw status)
  • Logs monitored (/var/log/fail2ban.log)

What to Monitor

Weekly checks:

# Total bans
sudo fail2ban-client status | grep "Jail list"

# Top offending IPs
sudo zgrep "Ban " /var/log/fail2ban.log* | awk '{print $NF}' | sort | uniq -c | sort -rn | head -10

# Current banned IPs
sudo fail2ban-client banned

Beyond Fail2ban

For even stronger protection, combine with:

  • Cloudflare - DDoS protection for web services
  • GeoIP blocking - Block entire countries (if traffic is local-only)
  • Port knocking - Hide SSH behind secret knock sequence
  • VPN-only access - Don’t expose services publicly at all

Final Thoughts

Fail2ban is a must-have for any internet-facing server. It won’t stop sophisticated attacks, but it eliminates 99% of automated bot traffic.

Set it and forget it - Fail2ban quietly protects your server while you focus on building cool self-hosted services.


Next Steps:

Questions? Join our Discord community or drop a comment below.