Do you want to see Fail2Ban in action? We have it running on this website. Check out how many bans have been executed in the last few days—click here!

What is Fail2Ban?

Fail2Ban is an open-source intrusion prevention tool that protects servers from brute-force attacks and unauthorized access. It monitors log files (e.g., SSH, Nginx) for suspicious activity and bans offending IP addresses using firewall rules.


How Fail2Ban Works

  1. Monitor Logs: Scans service-specific logs (e.g., /var/log/auth.log for SSH).
  2. Detect Patterns: Uses regex to identify repeated failed login attempts.
  3. Ban IPs: Blocks IPs exceeding predefined failure thresholds via firewall rules.
  4. Unban (If you want) After Time: Automatically unblocks IPs after a configurable period.

Why It’s Effective:

  • Lightweight, flexible, and automated.
  • Customizable rules (called "jails") for specific services.

Installation Guide

Debian/Ubuntu:

sudo apt update && sudo apt install fail2ban

RHEL/CentOS:

sudo dnf install fail2ban  # or sudo yum install fail2ban

Basic Configuration

Before using it, we need to understand some basic concepts of Fail2Ban: Jails, Filters, and Actions.

Jails

A jail is a configuration that defines how Fail2Ban should monitor and protect a specific service (e.g., SSH, Nginx, Apache). Each jail specifies:

  • Which log file to monitor.
  • How many failed attempts are allowed (maxretry).
  • The time window for counting failures (findtime).
  • How long to ban an IP (bantime).
  • The action to take (e.g., block via firewall).

Jails link together filters (regex patterns to detect attacks) and actions (what to do when an attack is detected)

you can create your own jails or use some just configured ones:

/etc/fail2ban/jail.conf        # Base configuration (do NOT edit directly)
/etc/fail2ban/jail.local       # Custom overrides (safe to edit)
/etc/fail2ban/jail.d/          # Additional jail configs (e.g., *.conf files)

Do not edit /etc/fail2ban/jail.conf directly, as it may be overwritten during updates. Instead, use /etc/fail2ban/jail.local or create custom jail configurations inside /etc/fail2ban/jail.d/ for persistent settings

Let's check a custom jail that we could create in jail.local

[my-nginx-404]
enabled   = true
filter    = my-nginx-404
logpath   = /var/log/nginx/access.log   
# Path to Nginx access log
maxretry  = 5                           
# 5 errors = ban
findtime  = 5m
bantime   = 24h
action    = iptables-block send_notification
Parameter Description
enabled Activates the jail. Set to true to enable it.
port Specifies the ports to block (http = 80, https = 443).
filter The filter file (nginx-404) used to detect malicious activity.
logpath Path to the Nginx access log (/var/log/nginx/access.log).
maxretry Number of failed attempts (404/403 errors) allowed before banning.
findtime Time window (5 minutes) to count failures.
bantime Duration to ban an IP (24 hours).
action What to do when banning (default: block via firewall). (Note: you can execute more than one action)

Filters

A filter is a set of regex patterns that Fail2Ban uses to identify malicious activity in log files. Filters parse logs to detect patterns like:

  • Failed SSH login attempts.
  • Unauthorized access to a web app (e.g., Nginx 403 errors).

Filters determine what constitutes an attack. Fail2Ban compares log entries against these regex rules to trigger bans.

Default filters are located in:

/etc/fail2ban/filter.d/        # Contains all filter definitions (*.conf files)

Example of a filter that catches all the 404 errors from nginx log file:

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

Actions

Actions define what Fail2Ban does when the something triggers the jail configuration. Common actions include:

  • Adding a firewall rule to block an IP.
  • Sending an email alert.
  • Logging the ban.

The most used action is to enforce security policies (e.g., blocking IPs via iptables or firewalld).

Default actions:

/etc/fail2ban/action.d/        # Contains action definitions (*.conf files)
  • Example actions:
    • action.d/iptables-allports.conf (block IP via iptables).
    • action.d/sendmail.conf (send email alerts).
# /etc/fail2ban/action.d/iptables-allports.conf
[Definition]
actionban = iptables -I fail2ban-<name> 1 -s <ip> -j DROP

This action blocks an IP using iptables.

In Every Action file you could have 4 different components:

  • actionban: is executed when the condition to do the ban is met (the action could be to block traffic from the IP that is doing something bad )
  • actionunban: is executed when the condition to do the unban is met (the action could be to remove the firewall configuration that blocked the Ip on the ban)
  • actionstart: is executed when the jail starts (for example, here you can do some action to configure your firewall, if needed)
  • actionstop: is executed when the jail stop (for example, you could send a notification about the fact that the jail is not operating anymore)

Note: there is also a component called actioncheck that is executed when the jail starts, and before each ban. If you need, you can use actioncheck to be sure that you can execute the action in the action ban; for example, you could have to check a database connection or other stuff.

To summarize:

Concept Role File Location
Jails Define monitoring rules /etc/fail2ban/jail.local or jail.d/
Filters Detect attacks via regex /etc/fail2ban/filter.d/
Actions Enforce bans/notifications /etc/fail2ban/action.d/

Let's Try

Now that we have installed Fail2Ban and we know a little bit about how to configure it let's try it, we will do a basic configuration to protect the ssh. First of all we have to create a local configuration for our jail

sudo nano /etc/fail2ban/jail.local
[sshd]
enabled = true
maxretry = 3      
# Allow 3 failed attempts
findtime = 10m    
# Within 10 minutes
bantime = 1h      
# Ban for 1 hour
logpath = /var/log/auth.log  
# Path to SSH logs (usually /var/log/auth.log)
filter = sshd 
#we use the filter that we just have 
action = iptables-allports[name=sshd] 
#we use an action that will block the ip of the "attacker" on every port

For the Filter and the Action, we will use some that ship with fail2ban; if we want, we can also write them by ourselves, but for our purpose, it is not needed.

Starting

After saving the jail we can try to start:

sudo systemctl start fail2ban # start fail2ban
sudo systemctl status fail2ban # check if fail2ban is running

If, checking the status, you see something like:

Active: failed (Result: exit-code) 

You can check the details of the error with:

sudo journalctl -u fail2ban

Testing

If we have fail2ban running correctly we can check the active jails and if there are some active bans in the jails that we have:

sudo fail2ban-client status         # List active jails
sudo fail2ban-client status sshd    # View banned IPs for SSH

This will be the output of the command:

debian@mojalab:/home/debian$ sudo fail2ban-client status         # List active jails
Status
|- Number of jail:      1
`- Jail list:   sshd

debian@mojalab:/home/debian$ sudo fail2ban-client status sshd    # View banned IPs for SSH
Status for the jail: sshd

|- Filter
|  |- Currently failed: 0
|  |- Total failed:     0
|  `- File list:        /var/log/auth.log
`- Actions
   |- Currently banned: 0
   |- Total banned:     0
   `- Banned IP list:

you could also check the log of fail2ban, usually you can find it in /var/log/fail2ban, with tail -f you could observe the log file ,for example, while doing a test

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

In the following example I made more than 3 login attempts with the wrong credential and then I got banned



2025-02-02 00:35:24,825 fail2ban.filter         [298523]: INFO    [sshd] Found 45.80.184.201 - 2025-02-02 00:35:24
2025-02-02 00:35:31,582 fail2ban.filter         [298523]: INFO    [sshd] Found 45.80.184.201 - 2025-02-02 00:35:31
2025-02-02 00:35:32,050 fail2ban.filter         [298523]: INFO    [sshd] Found 45.80.184.201 - 2025-02-02 00:35:32
2025-02-02 00:35:32,132 fail2ban.actions        [298523]: NOTICE  [sshd] Ban 45.80.184.201

checking again I got:

debian@mojalab:/home/debian$ sudo fail2ban-client status sshd    # View banned IPs for SSH
Status for the jail: sshd
|- Filter
|  |- Currently failed: 0
|  |- Total failed:     6
|  `- File list:        /var/log/auth.log
`- Actions
   |- Currently banned: 1
   |- Total banned:     1
   `- Banned IP list:   45.80.184.201

Unban an IP

To manually unban an IP address from a specific jail (e.g., sshd ), use the following command:

sudo fail2ban-client set <jail-name> unbanip <IP-address>

In my example it will be

debian@kasper:~$ sudo fail2ban-client set sshd unbanip 45.80.184.201
1

As you see it will return 1 if the unban was successful 0 otherwise

Whitelisting

If needed (for example you have a lot of users that connect to your server using the same Ip Address and you don't want to block everyone for just one user that keeps trying to login with the wrong credentials) you could put an Ip in Whitelist, this Ip will be excluded from the ban process.

To add the ip to the whitelist just edit the jail.local file:

sudo nano /etc/fail2ban/jail.local

and add:

[DEFAULT]
ignoreip = 45.80.184.201

and then restart and check if everything is ok:

debian@mojalab:~$ sudo systemctl restart fail2ban
debian@mojalab:~$ sudo systemctl status fail2ban
● fail2ban.service - Fail2Ban Service
     Loaded: loaded (/lib/systemd/system/fail2ban.service; enabled; vendor preset: enabled)
     Active: active (running) since Sun 2025-02-02 01:06:10 UTC; 5s ag

now even you receive a lot of failed login attempt from this IP it will not be banned.

After whitelisting if you check the log you will see something like:

2025-02-02 01:08:45,622 fail2ban.filter         [298645]: INFO    [sshd] Ignore 45.80.184.201 by ip
2025-02-02 01:08:48,327 fail2ban.filter         [298645]: INFO    [sshd] Ignore 45.80.184.201 by ip
2025-02-02 01:08:50,019 fail2ban.filter         [298645]: INFO    [sshd] Ignore 45.80.184.201 by ip
2025-02-02 01:08:50,696 fail2ban.filter         [298645]: INFO    [sshd] Ignore 45.80.184.201 by ip
2025-02-02 01:08:50,854 fail2ban.filter         [298645]: INFO    [sshd] Ignore 45.80.184.201 by ip

Final Notes

Fail2Ban is a powerful first line of defense against brute-force attacks. However, it should be part of a multi-layered security approach rather than the sole protection mechanism. Keep your server updated, minimize exposed ports, implement strong authentication policies, and always have a backup plan.

Disclaimer: At MojaLab, we aim to provide accurate and useful content, but hey, we’re human (well, mostly)! If you spot an error, have questions, or think something could be improved, feel free to reach out—we’d love to hear from you. Use the tutorials and tips here with care, and always test in a safe environment. Happy learning!

No AI was mistreated in the making of this tutorial—every LLM was used with the respect it deserves.