Fail2Ban Setup
Every Linux server connected to the internet receives constant automated brute-force attempts against SSH and other services. Fail2ban watches the logs and bans repeat offenders, turning hundreds of daily probes into a non-issue.
How fail2ban works
1. A bot tries to log in via SSH 5 times, all failures
2. fail2ban's filter regex matches "Failed password" in /var/log/auth.log
3. When 5 matches occur within 10 minutes (findtime), the threshold is reached
4. fail2ban runs the ban action: adds a firewall rule dropping the offending IP
5. After bantime (e.g., 1 hour), the rule is removed automatically
The configuration is organised into jails: each jail defines a log file, filter pattern, and action for one service.
Installation
# Debian/Ubuntu
sudo apt install fail2ban
# Fedora/RHEL
sudo dnf install fail2ban
# Arch Linux
sudo pacman -S fail2ban
# Enable and start
sudo systemctl enable --now fail2ban
# Check status
sudo systemctl status fail2ban
sudo fail2ban-client status
Basic configuration
Never edit /etc/fail2ban/jail.conf — it gets overwritten on package updates. Create /etc/fail2ban/jail.local for all your settings:
sudo nano /etc/fail2ban/jail.local
[DEFAULT]
# Whitelist: these IPs are never banned
ignoreip = 127.0.0.1/8 ::1
# How long to ban an offending IP (in seconds, or use suffix: 1h, 1d)
bantime = 1h
# Window in which failures are counted (10 minutes)
findtime = 10m
# Number of failures before a ban
maxretry = 5
# Email notification (optional)
# destemail = admin@example.com
# sendername = Fail2Ban
# mta = sendmail
# Backend for detecting log changes
backend = auto
# Firewall action to use for banning
# Use 'nftables-multiport' if you are using nftables directly
# Use 'ufw' if you manage your firewall with ufw
banaction = iptables-multiport
Enabling the SSH jail
The SSH jail is the most important one. It is pre-configured — you just need to enable it:
# /etc/fail2ban/jail.local
[sshd]
enabled = true
port = ssh # or the port number if you changed it, e.g. 2222
filter = sshd
logpath = /var/log/auth.log # Debian/Ubuntu
# logpath = /var/log/secure # Fedora/RHEL
# logpath = %(syslog_authpriv)s # auto-detect (works on both)
maxretry = 3
bantime = 24h
findtime = 1h
# After editing, reload fail2ban
sudo fail2ban-client reload
# Check that the sshd jail is active
sudo fail2ban-client status sshd
Checking jail status
# List all active jails
sudo fail2ban-client status
# Status of a specific jail
sudo fail2ban-client status sshd
# Output shows:
# - Filter: how many log lines matched
# - Actions: current number of banned IPs
# - Banned IP list: the actual IPs currently banned
Banning and unbanning manually
# Manually ban an IP in a specific jail
sudo fail2ban-client set sshd banip 203.0.113.5
# Unban an IP
sudo fail2ban-client set sshd unbanip 203.0.113.5
# Check if an IP is banned
sudo fail2ban-client get sshd banned | grep 203.0.113.5
# Unban all IPs in a jail
sudo fail2ban-client set sshd unbanip $(sudo fail2ban-client get sshd banned | tr -d '[]' | tr ',' ' ')
Adding jails for other services
nginx
# /etc/fail2ban/jail.local
[nginx-http-auth]
enabled = true
filter = nginx-http-auth
port = http,https
logpath = /var/log/nginx/error.log
maxretry = 5
[nginx-limit-req]
enabled = true
filter = nginx-limit-req
port = http,https
logpath = /var/log/nginx/error.log
maxretry = 10
findtime = 1m
bantime = 10m
[nginx-botsearch]
enabled = true
filter = nginx-botsearch
port = http,https
logpath = /var/log/nginx/access.log
maxretry = 2
Postfix (mail)
[postfix]
enabled = true
port = smtp,465,submission
filter = postfix
logpath = /var/log/mail.log
maxretry = 5
[postfix-sasl]
enabled = true
port = smtp,465,submission,imap,imaps,pop3,pop3s
filter = postfix-sasl
logpath = /var/log/mail.log
maxretry = 5
WordPress login
[wordpress]
enabled = true
filter = wordpress
port = http,https
logpath = /var/log/nginx/access.log
maxretry = 5
findtime = 10m
bantime = 1h
Create the filter file /etc/fail2ban/filter.d/wordpress.conf:
[Definition]
failregex = ^<HOST> .* "POST .*wp-login\.php
ignoreregex =
Writing a custom filter
Filters are regex patterns that match failure lines in log files. They live in /etc/fail2ban/filter.d/.
# Look at an existing filter for reference
cat /etc/fail2ban/filter.d/sshd.conf
# /etc/fail2ban/filter.d/myapp.conf
[INCLUDES]
before = common.conf
[Definition]
# <HOST> is a placeholder that fail2ban replaces with the IP regex
failregex = ^.*authentication failure.*from <HOST>
^.*Invalid login from <HOST>
ignoreregex =
# Test your filter against actual log lines
sudo fail2ban-regex /var/log/myapp.log /etc/fail2ban/filter.d/myapp.conf
# Test with a built-in filter
sudo fail2ban-regex /var/log/auth.log /etc/fail2ban/filter.d/sshd.conf
Aggressive settings for high-traffic servers
For a server under heavy attack, you may want longer bans and persistent storage:
# /etc/fail2ban/jail.local
[DEFAULT]
# Ban for 1 week on first offence
bantime = 1w
findtime = 10m
maxretry = 3
# Progressive banning (ban longer for repeat offenders)
# Requires fail2ban >= 0.11
bantime.increment = true
bantime.factor = 24
bantime.maxtime = 5w
bantime.formula = ban.Time * (1<<(ban.Count if ban.Count<20 else 20)) * banFactor
# Persist bans across fail2ban restarts
dbfile = /var/lib/fail2ban/fail2ban.sqlite3
dbpurgeage = 30d
View ban history and logs
# fail2ban log
sudo tail -f /var/log/fail2ban.log
sudo journalctl -u fail2ban -f
# Recent bans
sudo grep "Ban " /var/log/fail2ban.log | tail -20
# Recent unbans
sudo grep "Unban " /var/log/fail2ban.log | tail -20
# Count bans per jail over last day
sudo grep "Ban " /var/log/fail2ban.log | \
grep "$(date -d '1 day ago' '+%Y-%m-%d')" | \
awk '{print $6}' | sort | uniq -c | sort -rn
# Most attacked IPs
sudo grep "Ban " /var/log/fail2ban.log | \
awk '{print $NF}' | sort | uniq -c | sort -rn | head -20
Using nftables as the backend
On systems where nftables is the primary firewall:
# /etc/fail2ban/jail.local
[DEFAULT]
banaction = nftables-multiport
banaction_allports = nftables-allports
# Verify bans are appearing in nftables
sudo nft list ruleset | grep fail2ban
# Or with iptables-nft
sudo iptables -L f2b-sshd -n --line-numbers
Frequently Asked Questions
What does fail2ban do?
Fail2ban is a daemon that monitors log files for patterns indicating brute-force attacks or other repeated failures, then temporarily blocks the offending IP addresses using firewall rules (iptables, nftables, or ufw). For example, if an IP tries to log in via SSH 5 times and fails within 10 minutes, fail2ban adds a firewall rule that drops all traffic from that IP for a configurable ban duration. When the ban expires, the rule is removed. Fail2ban does not replace a firewall or strong authentication — it complements them by reducing the noise and risk from automated attacks.
What is a fail2ban jail?
A jail is a fail2ban configuration unit that ties together a log file to watch, a filter (a regex pattern that matches failure events in the log), and an action (what to do when the threshold is reached). Each service you want to protect gets its own jail. Fail2ban ships with jails for common services including SSH, Apache, nginx, postfix, dovecot, and others. Jails are configured in /etc/fail2ban/jail.local (your custom settings) and activated with the enabled = true directive.
What is the difference between jail.conf and jail.local?
jail.conf is the default configuration file that ships with fail2ban and is overwritten when the package updates. jail.local is your custom override file: any setting in jail.local takes precedence over the same setting in jail.conf. You should never edit jail.conf directly. Instead, create /etc/fail2ban/jail.local with only the settings you want to change. The same pattern applies to individual jail configuration files in /etc/fail2ban/jail.d/ — put your overrides there, not in the .conf files under /etc/fail2ban/.
How do I unban an IP that fail2ban has blocked?
Use the fail2ban-client command: sudo fail2ban-client set sshd unbanip 1.2.3.4. This immediately removes the ban for that IP from the specified jail. If you are not sure which jail banned the IP, you can check all jails with sudo fail2ban-client status (lists active jails) and then sudo fail2ban-client status sshd (shows currently banned IPs for the sshd jail). You can also whitelist your own IP permanently by adding it to the ignoreip setting in jail.local so it is never banned.
How do I whitelist my IP address in fail2ban?
Add your IP (or subnet) to the ignoreip setting in /etc/fail2ban/jail.local. Under the [DEFAULT] section, set ignoreip = 127.0.0.1/8 ::1 203.0.113.5 (replace the last address with your IP). Multiple addresses are space-separated. You can also add CIDR subnets: ignoreip = 127.0.0.1/8 ::1 192.168.1.0/24. After editing, reload fail2ban: sudo fail2ban-client reload. IPs in ignoreip are never banned, even if they exceed the failure threshold.
Does fail2ban work with nftables instead of iptables?
Yes. Fail2ban supports nftables as a backend. In /etc/fail2ban/jail.local, set banaction = nftables-multiport under [DEFAULT] to use nftables for banning. On modern distributions where iptables is actually the nftables shim (iptables-nft), the default iptables banaction also works correctly. If you use ufw, set banaction = ufw to have fail2ban interact with ufw instead. The banaction setting controls which firewall command fail2ban uses to create and remove bans.