TL;DR: Fail2ban monitors log files for failed login attempts and automatically bans IP addresses that show malicious behavior. After 3-5 failed SSH attempts, the attacker's IP gets blocked by the firewall. This guide covers installation, SSH protection, custom jails for web services, and monitoring banned IPs.
What Is Fail2ban?
Fail2ban is an intrusion prevention framework that:
- Monitors log files for failed authentication attempts
- Detects patterns indicating brute force attacks
- Automatically updates firewall rules to block attackers
- Unbans IPs after a configurable time period
Why you need it: Any server exposed to the internet will receive constant automated login attempts. Check your SSH logs, you'll see hundreds of failed attempts from IPs around the world. Fail2ban turns reactive security into proactive defense.
How It Works
- Fail2ban watches log files (e.g.,
/var/log/auth.log) - When it sees repeated failures from one IP, it triggers a "jail"
- The jail adds a firewall rule blocking that IP
- After the ban time expires, the IP is automatically unblocked
Installation
Debian/Ubuntu
sudo apt update
sudo apt install fail2ban -y
CentOS/RHEL/Fedora
sudo dnf install epel-release -y
sudo dnf install fail2ban -y
Start and Enable Service
sudo systemctl enable fail2ban
sudo systemctl start fail2ban
sudo systemctl status fail2ban
Configuration Basics
Fail2ban configuration lives in /etc/fail2ban/:
jail.conf, Default configuration (don't edit directly)jail.local, Your custom overrides (create this)jail.d/, Directory for individual jail configsfilter.d/, Regex patterns for detecting attacksaction.d/, What to do when attacks are detected
Important: Never edit jail.conf directly, updates will overwrite your changes. Always use jail.local or files in jail.d/.
Create jail.local
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
sudo nano /etc/fail2ban/jail.local
Key Configuration Options
| Option | Default | Description |
|---|---|---|
bantime | 10m | How long to ban an IP |
findtime | 10m | Time window for counting failures |
maxretry | 5 | Failures before ban |
ignoreip | 127.0.0.1/8 | IPs to never ban |
Protecting SSH
SSH protection is typically enabled by default. Verify and customize:
Create SSH Jail Configuration
sudo nano /etc/fail2ban/jail.d/sshd.local
Add:
[sshd]
enabled = true
port = ssh
filter = sshd
logpath = /var/log/auth.log
backend = systemd
# Ban after 3 failed attempts within 10 minutes
maxretry = 3
findtime = 10m
# Ban for 1 hour
bantime = 1h
# Never ban these IPs (your home IP, etc.)
ignoreip = 127.0.0.1/8 ::1 192.168.1.0/24 Note: Replace 192.168.1.0/24 with your local network range to avoid accidentally banning yourself.
Apply Changes
sudo systemctl restart fail2ban
Verify SSH Jail Is Active
sudo fail2ban-client status sshd
Expected output:
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: Aggressive Protection Settings
For servers that don't need to be lenient:
[sshd]
enabled = true
port = ssh
filter = sshd
logpath = /var/log/auth.log
backend = systemd
# Very strict: 2 failures = ban
maxretry = 2
findtime = 10m
# Ban for 24 hours
bantime = 24h
# Increase ban time for repeat offenders
bantime.increment = true
bantime.multipliers = 1 5 30 60 1440
# 1st ban: 24h, 2nd: 5 days, 3rd: 30 days, 4th: 60 days, 5th+: permanent
ignoreip = 127.0.0.1/8 ::1 192.168.1.0/24 Custom Jails for Other Services
Nextcloud
Create filter:
sudo nano /etc/fail2ban/filter.d/nextcloud.conf
Add:
[Definition]
failregex = ^{"reqId":".*","level":2,"time":".*","remoteAddr":"<HOST>","user":".*","app":"core","method":".*","url":".*","message":"Login failed:.*$
^{"reqId":".*","level":2,"time":".*","remoteAddr":"<HOST>","user":".*","app":"core","method":".*","url":".*","message":"Trusted domain error.*$
datepattern = ,"time":"%%Y-%%m-%%dT%%H:%%M:%%S Create jail:
sudo nano /etc/fail2ban/jail.d/nextcloud.local
Add:
[nextcloud]
enabled = true
port = 80,443
protocol = tcp
filter = nextcloud
logpath = /path/to/nextcloud/data/nextcloud.log
maxretry = 3
bantime = 1h
findtime = 10m Nginx (Basic Auth)
sudo nano /etc/fail2ban/jail.d/nginx-auth.local
Add:
[nginx-http-auth]
enabled = true
port = http,https
filter = nginx-http-auth
logpath = /var/log/nginx/error.log
maxretry = 3
bantime = 1h
findtime = 10m Apache
[apache-auth]
enabled = true
port = http,https
filter = apache-auth
logpath = /var/log/apache2/error.log
maxretry = 3
bantime = 1h
findtime = 10m Monitoring and Management
View All Jail Status
sudo fail2ban-client status
View Specific Jail Details
sudo fail2ban-client status sshd
View Banned IPs
# All jails
sudo fail2ban-client banned
# Specific jail
sudo fail2ban-client status sshd | grep "Banned IP"
Manually Ban an IP
sudo fail2ban-client set sshd banip 123.123.123.123
Manually Unban an IP
sudo fail2ban-client set sshd unbanip 123.123.123.123
View Fail2ban Logs
sudo tail -f /var/log/fail2ban.log
Test a Filter (Debug)
# Test if filter catches log entries
sudo fail2ban-regex /var/log/auth.log /etc/fail2ban/filter.d/sshd.conf
Email Alerts (Optional)
Get notified when IPs are banned:
sudo nano /etc/fail2ban/jail.local
Add to [DEFAULT] section:
[DEFAULT]
destemail = [email protected]
sender = [email protected]
mta = sendmail
action = %(action_mwl)s Note: Requires a working mail server (sendmail, postfix, or msmtp).
Fail2ban with Docker
Docker containers don't log to the host's log files by default. Options:
Option 1: Forward Docker Logs
Configure Docker to log to syslog:
# In /etc/docker/daemon.json
{
"log-driver": "syslog",
"log-opts": {
"syslog-address": "unix:///dev/log"
}
} Option 2: Mount Log Files
In docker-compose.yml:
volumes:
- /var/log/myapp:/var/log/myapp Then point Fail2ban to /var/log/myapp/.
Option 3: Fail2ban in Docker
Run Fail2ban itself as a container with access to other container logs.
Best Practices
- Always whitelist your IP: Add your home/office IP to
ignoreipto avoid locking yourself out - Use key-based SSH: Fail2ban is a safety net, not a replacement for proper authentication
- Change default SSH port: Reduces noise from automated scans
- Start with lenient settings: Adjust as you understand normal traffic patterns
- Monitor regularly: Check banned IPs and logs weekly
- Keep Fail2ban updated: Security software needs updates too
- Test before production: Verify jails work as expected
- Have a backup access method: Console access in case you lock yourself out
Troubleshooting
Fail2ban Won't Start
# Check for config errors
sudo fail2ban-client -t
# View detailed errors
sudo journalctl -u fail2ban
IPs Not Being Banned
- Verify the log file path is correct
- Test filter with
fail2ban-regex - Check jail is enabled:
fail2ban-client status - Verify log timestamps match Fail2ban's expected format
Accidentally Banned Yourself
If you have console access:
sudo fail2ban-client set sshd unbanip YOUR_IP
Or stop Fail2ban temporarily:
sudo systemctl stop fail2ban
# Fix the issue
sudo systemctl start fail2ban
The Bottom Line
Fail2ban is essential for any internet-exposed server. It's lightweight, effective, and requires minimal maintenance once configured. The combination of Fail2ban with SSH key authentication and a non-standard port makes brute force attacks practically impossible.
Start with SSH protection. Once you're comfortable, add jails for other services. Monitor the logs periodically to understand attack patterns, you'll be surprised how many automated attacks target even small home servers.