How to Install and Configure fail2ban
Install fail2ban, configure the SSH jail, tune ban times, write custom filters, and verify bans are working — on Debian, Fedora, and Arch.
Before you start
- ▸Root or sudo access on the target system
- ▸A running SSH daemon (sshd) if following the SSH jail steps
- ▸One of nftables, firewalld, ufw, or iptables active and functioning
- ▸Basic familiarity with editing files in a terminal
fail2ban watches your log files and temporarily bans IP addresses that show signs of brute-force or abuse. It does this by parsing logs with regex-based filters, then feeding matching IPs into a firewall action — by default a temporary ban. It won't replace a proper firewall or key-based SSH auth, but it's a solid second layer that cuts noise and real attacks alike. This guide installs fail2ban, wires up the SSH jail, tunes ban times, and shows you how to write a custom filter from scratch.
Installation
Debian / Ubuntu
sudo apt update && sudo apt install fail2ban -y
Fedora / RHEL / Rocky
sudo dnf install epel-release -y # RHEL/Rocky only
sudo dnf install fail2ban -y
Arch Linux
sudo pacman -S fail2ban
After installation, enable and start the service:
sudo systemctl enable --now fail2ban
Understanding the Configuration Layout
fail2ban splits configuration into two layers. Files ending in .conf are the upstream defaults — never edit them directly, because package updates will overwrite your changes. Files ending in .local override the defaults and survive updates.
/etc/fail2ban/fail2ban.conf— daemon-level settings (log level, socket path)/etc/fail2ban/jail.conf— all jail defaults and bundled jail definitions/etc/fail2ban/jail.local— your overrides; create this file/etc/fail2ban/filter.d/— filter definitions (regex patterns)/etc/fail2ban/action.d/— ban/unban action scripts
Create your working override file now:
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
Alternatively, start with a minimal jail.local that only contains what you want to change — cleaner and less likely to conflict with package updates.
Configuring Global Defaults
Open /etc/fail2ban/jail.local and locate or add the [DEFAULT] section. The key tunables:
sudo nano /etc/fail2ban/jail.local
[DEFAULT]
# Hosts to never ban — your own IPs, monitoring servers
ignoreip = 127.0.0.1/8 ::1 203.0.113.50
# How long a ban lasts (seconds, or use time suffixes: 10m, 1h, 1d)
bantime = 1h
# Window in which findtime failures trigger a ban
findtime = 10m
# Number of failures before a ban
maxretry = 5
# Backend: systemd is preferred on modern distros; auto falls back correctly
backend = systemd
bantime accepts a multiplier suffix in fail2ban 0.11+: set it to -1 for a permanent ban (use with caution). The bantime.increment feature can also escalate repeat offenders automatically — see jail.conf for details.
The SSH Jail
The bundled sshd jail covers OpenSSH. With backend = systemd, it reads directly from the journal rather than a log file — no path configuration needed.
[sshd]
enabled = true
port = ssh
filter = sshd
backend = systemd
maxretry = 3
bantime = 2h
findtime = 5m
If you run SSH on a non-standard port, change port to match:
port = 2222
If your distro still uses flat log files (/var/log/auth.log on older Debian, /var/log/secure on RHEL), set backend and logpath explicitly:
[sshd]
enabled = true
port = ssh
filter = sshd
backend = auto
logpath = /var/log/auth.log
maxretry = 3
Choosing a Firewall Action
fail2ban needs to know which firewall to use. The default action varies by distro. Check what's active on your system and set accordingly in [DEFAULT]:
- nftables (recommended on modern systems):
banaction = nftables-multiport - firewalld (Fedora/RHEL default):
banaction = firewallcmd-rich-rules - ufw (Ubuntu):
banaction = ufw - iptables (legacy fallback):
banaction = iptables-multiport
[DEFAULT]
banaction = nftables-multiport
banaction_allports = nftables-allports
Reload after any change:
sudo systemctl reload fail2ban
Writing a Custom Filter
Suppose you're running a web app that logs failed logins like this in /var/log/myapp/auth.log:
2024-06-01 14:22:03 LOGIN FAILED user=admin src=198.51.100.77
2024-06-01 14:22:07 LOGIN FAILED user=root src=198.51.100.77
Create a new filter file:
sudo nano /etc/fail2ban/filter.d/myapp-auth.conf
[Definition]
failregex = ^%(__prefix_line)s LOGIN FAILED user=\S+ src=$
ignoreregex =
<HOST> is fail2ban's placeholder for an IPv4 or IPv6 address. %(__prefix_line)s matches the timestamp/hostname prefix automatically. Test your filter against real log data before enabling it:
sudo fail2ban-regex /var/log/myapp/auth.log /etc/fail2ban/filter.d/myapp-auth.conf
The output will report how many lines matched. A realistic result looks like:
Lines: 120 lines, 0 ignored, 14 matched, 106 missed
Once you're satisfied, add the jail in jail.local:
[myapp-auth]
enabled = true
port = http,https
filter = myapp-auth
logpath = /var/log/myapp/auth.log
maxretry = 5
bantime = 30m
sudo systemctl reload fail2ban
Verifying Your Setup
Check which jails are active:
sudo fail2ban-client status
Inspect a specific jail:
sudo fail2ban-client status sshd
Sample output (yours will differ):
Status for the jail: sshd
|- Filter
| |- Currently failed: 2
| |- Total failed: 47
| `- Journal matches: _SYSTEMD_UNIT=sshd.service + _COMM=sshd
`- Actions
|- Currently banned: 1
|- Total banned: 8
`- Banned IP list: 198.51.100.77
To manually unban an IP:
sudo fail2ban-client set sshd unbanip 198.51.100.77
To manually ban one for testing:
sudo fail2ban-client set sshd banip 203.0.113.1
Troubleshooting
Jail shows as inactive after reload
Check for syntax errors in your config:
sudo fail2ban-client -t
IPs are matched but never banned
Confirm the action is working by checking the service log:
sudo journalctl -u fail2ban -n 50 --no-pager
A mismatch between the configured banaction and the active firewall (e.g., specifying nftables-multiport when only iptables is available) will silently fail to ban. Run sudo nft list ruleset or sudo iptables -L to confirm which is in use.
You locked yourself out
If you're on a VPS and lose SSH access, use the provider's console to unban yourself:
sudo fail2ban-client set sshd unbanip YOUR_IP
Then add your static IP to ignoreip in [DEFAULT] and reload. This is exactly why ignoreip should be set before you go live.
fail2ban-regex shows zero matches
The regex anchoring or timestamp format may be off. Use --print-all-matched to debug which lines are being evaluated, and compare carefully against a real log line. Remove %(__prefix_line)s temporarily to isolate the issue.
Frequently asked questions
- Will fail2ban interfere with legitimate users who mistype their password?
- Only if they exceed maxretry within the findtime window. Setting maxretry to 5 and findtime to 10 minutes is generous enough for normal use. Always add your own IPs to ignoreip.
- Does fail2ban protect against distributed brute-force attacks from many IPs?
- Not effectively. fail2ban bans individual IPs; a botnet spreading attempts across thousands of addresses will stay under the threshold. Use it alongside strong passwords or — better — key-only SSH authentication.
- What's the difference between bantime = 1h and bantime.increment = true?
- A fixed bantime releases the IP after one hour every time. bantime.increment multiplies the ban duration for repeat offenders — a recidivist gets banned for progressively longer periods, eventually permanently.
- Can I use fail2ban with nftables on systems that still have iptables installed?
- Yes, but you must explicitly set banaction = nftables-multiport in jail.local; the auto-detection may prefer iptables if it's present. Verify with nft list ruleset after a test ban.
- How do I see the full history of bans, not just currently active ones?
- fail2ban-client status <jail> shows total banned counts. For full history including timestamps, query the journal: sudo journalctl -u fail2ban | grep 'Ban'.
Related guides
Manage Secrets with Ansible Vault
Encrypt Ansible secrets with AES-256 using ansible-vault: encrypt files and inline vars, automate with password files, and isolate group-level secrets with vault IDs.
AppArmor Explained
Learn how AppArmor profiles work, how to switch between enforce and complain mode, create new profiles, and diagnose access denials on Ubuntu, Debian, and Arch.
Apply CIS Benchmarks with OpenSCAP
Use OpenSCAP and scap-security-guide to evaluate, report on, and remediate Linux systems against CIS Benchmarks — covering install, eval, and automation.
How to Audit a Linux System with auditd
Set up auditd on Linux to track file access, syscalls, and privilege use. Covers persistent rules, file watches, ausearch, and aureport across major distros.