Published 2026-09-08 · Updated 2026-10-22
Fail2ban Setup Guide: Automated Intrusion Prevention
Fail2ban is a log-scanning intrusion prevention tool that watches services like SSH and web servers for repeated failed login attempts, then automatically firewalls the offending IP address for a set period. Configured correctly with sensible jails, filters and ban timing, it turns hours of manual log review into an automated defense that runs continuously in the background.
In this guide
How fail2ban actually works Installing fail2ban and the jail.local pattern Core settings: bantime, findtime, maxretry Setting ignoreip so you don't lock yourself out Configuring common jails: sshd, nginx, recidive How filters and actions work Checking status, unbanning, and logs Common misconfigurations to avoid Frequently asked questionsQuick win (10 minutes)
- Run
sudo apt install fail2ban(Debian/Ubuntu) orsudo dnf install fail2ban(RHEL/Alma/Rocky) if it is not already installed. - Copy
/etc/fail2ban/jail.confto/etc/fail2ban/jail.localso your changes survive package updates. - Add your own static IP address to
ignoreipin the[DEFAULT]section before enabling anything. - Enable the
sshdjail withmaxretry = 4andbantime = 1h, thensudo systemctl restart fail2ban. - Confirm it is running with
sudo fail2ban-client status sshd.
How fail2ban actually works
Fail2ban reads log files, matches failed login attempts against regular expression filters, and triggers a firewall ban when a defined threshold of failures occurs within a defined time window. It does not inspect network traffic directly; it relies entirely on the log output of the service it is protecting, which is why filter accuracy matters so much.
Each protected service is defined as a "jail" in fail2ban's configuration. A jail ties together three things: a log path to watch, a filter (a set of regex patterns that recognize a failed attempt in that log format), and an action (what to do when the failure threshold is crossed, typically inserting a firewall rule via iptables, nftables, or ufw). When fail2ban-client status shows a jail as active, it means the daemon is tailing that log file in near real time and evaluating every new line against the filter's patterns.
Because it works at the log layer, fail2ban is service-agnostic. Any daemon that writes a distinguishable line to a log on authentication failure, whether that is sshd, a web server's auth module, a mail server, or a custom application, can be protected by writing or reusing a filter for it. The project ships with dozens of filters out of the box for exactly this reason, according to the official fail2ban GitHub repository.
Installing fail2ban and the jail.local pattern
Install fail2ban from your distribution's package manager, then create a jail.local file rather than editing jail.conf directly, since package upgrades overwrite jail.conf and would silently discard your settings.
# Debian / Ubuntu
sudo apt update && sudo apt install fail2ban
# RHEL / AlmaLinux / Rocky Linux
sudo dnf install epel-release
sudo dnf install fail2ban
# Copy the shipped defaults into a file fail2ban treats as an override
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
Fail2ban reads jail.conf first and then applies jail.local on top of it, so anything you set in jail.local overrides the shipped default without touching the original file. This is the pattern documented across fail2ban's own configuration files, including the shipped jail.conf on GitHub, which explicitly instructs administrators to use jail.local for local changes. The same split applies to individual filter and action files if you ever need to customize those (filter.d/*.local, action.d/*.local).
After any change to jail.local, apply it with sudo systemctl restart fail2ban and verify the daemon started cleanly with sudo systemctl status fail2ban. A syntax error in jail.local will prevent the service from starting, so always check status after a restart rather than assuming it worked.
Core settings: bantime, findtime, maxretry
Bantime sets how long an IP stays blocked, findtime sets the rolling window fail2ban looks back over, and maxretry sets how many failures within that window trigger a ban. These three values, set in the [DEFAULT] section of jail.local and optionally overridden per jail, control how aggressive or lenient fail2ban is.
The default values shipped by fail2ban are a bantime of 10 minutes (600 seconds), a findtime of 10 minutes, and a maxretry of 5 failures. Time values can be written as plain seconds or using shorthand like 10m, 1h, or 1d, which fail2ban parses natively. For internet-facing SSH, most administrators tighten these considerably beyond the defaults, since 10 minutes does little to slow down an automated scanner that will simply retry after the ban expires.
[DEFAULT]
# how long an IP stays banned
bantime = 1h
# the rolling window used to count failures
findtime = 10m
# failures allowed within findtime before a ban
maxretry = 4
# ban time grows the more often an IP re-offends
bantime.increment = true
bantime.factor = 2
bantime.maxtime = 1w
The bantime.increment option is worth enabling: it multiplies the ban duration each time the same IP re-offends after its previous ban expires, so a scanner that keeps coming back gets banned for progressively longer instead of the same fixed window every time. A negative bantime value means a permanent ban, which is more appropriate for the recidive jail (covered below) than for a first-line service jail, since a temporary ban already defeats most automated brute-force tools without permanently burning an IP address that may later be reassigned to a legitimate user.
Setting ignoreip so you don't lock yourself out
Ignoreip is a whitelist of addresses and CIDR ranges that fail2ban will never ban, and it should always include your own management IP before you enable any jail. Set it in the [DEFAULT] section of jail.local so it applies to every jail automatically.
[DEFAULT]
ignoreip = 127.0.0.1/8 ::1 203.0.113.42/32
According to fail2ban's own configuration documentation, ignoreip accepts a space-separated list of IP addresses, CIDR masks, or DNS hostnames, and any connection from a matching address is exempt from all ban actions regardless of how many failures it triggers. If you manage the server from a fixed office or home IP, add it here. If your IP changes, consider a VPN or bastion host with a stable address instead of leaving ignoreip open to a wide range, which would create a gap an attacker could exploit if they ever obtained that IP range.
It is worth testing this before you rely on it: SSH in from a new terminal window while deliberately failing authentication a few times from an address you control, and confirm your primary session is never dropped from the whitelisted IP. Getting locked out of a remote VPS because ignoreip was misconfigured is a common and entirely avoidable mistake, and recovering usually requires out-of-band console access through your hosting provider.
Configuring common jails: sshd, nginx, recidive
The jails you actually need depend on which services are exposed, but sshd, a web-auth jail, and the recidive jail cover the most common attack surface on a typical VPS running SSH and a web server. Each jail is a bracketed section in jail.local naming the service it protects.
sshd jail
SSH is the most consistently attacked service on any internet-facing Linux box, since automated scanners probe port 22 continuously looking for weak credentials. A tightened sshd jail is the single highest-value fail2ban configuration you can make.
[sshd]
enabled = true
port = ssh
filter = sshd
backend = systemd
logpath = %(sshd_log)s
maxretry = 4
findtime = 10m
bantime = 1h
On systemd-based distributions, set backend to systemd so fail2ban reads directly from the journal rather than a log file path, which is more reliable on distributions that route sshd logging through journald by default. See our SSH hardening best practices guide for the complementary changes, like disabling password authentication entirely, that reduce how much load even reaches this jail.
nginx-http-auth jail
If nginx serves anything behind HTTP basic authentication, or you are fronting an admin panel, enable the matching jail so credential-guessing against that login is caught the same way SSH brute-forcing is.
[nginx-http-auth]
enabled = true
port = http,https
filter = nginx-http-auth
logpath = /var/log/nginx/error.log
maxretry = 5
findtime = 10m
bantime = 30m
For nginx deployments fronting login forms or CMS admin pages rather than HTTP basic auth, you generally need a custom or third-party filter tuned to that application's specific log format, since fail2ban can only match patterns it has a filter for.
recidive jail (repeat offenders)
The recidive jail watches fail2ban's own log rather than a service log, and bans IPs that have already been banned by other jails multiple times, escalating the response for persistent attackers.
[recidive]
enabled = true
filter = recidive
logpath = /var/log/fail2ban.log
banaction = %(banaction_allports)s
bantime = 1w
findtime = 1d
maxretry = 3
This layered pattern, short bans for a first offense escalating to a week-long ban after three separate bans in 24 hours, keeps legitimate users who mistype a password unaffected while making sustained brute-forcing impractical. Note the banaction_allports action, which blocks the offending IP on every port rather than just the one it was originally caught on, since a repeat offender has already shown clear malicious intent.
Not sure which jails your server actually needs?
A free, read-only server check reviews what's exposed on your VPS and flags gaps before an attacker finds them.
Get your free server check Talk to a security engineerHow filters and actions work
A filter is a set of regular expressions fail2ban matches against new log lines to recognize a failed authentication attempt, and an action defines the firewall command run when a ban or unban is triggered. Both live under /etc/fail2ban/filter.d/ and /etc/fail2ban/action.d/ respectively, and jails reference them by name.
You rarely need to write a filter from scratch for common services, since fail2ban ships filters for sshd, several web server auth patterns, mail servers, and more. Where you do need custom filters is for application-specific logs, such as a custom login form that writes its own failure message. In that case, copy an existing filter as a starting point and test your regex against real log lines using fail2ban-regex before enabling the jail, which lets you validate a filter against a live or sample log file without risking a bad ban rule going into production.
# test a filter against the real log before trusting it
sudo fail2ban-regex /var/log/nginx/error.log /etc/fail2ban/filter.d/nginx-http-auth.conf
The default ban action for most jails inserts a firewall rule (via iptables, nftables, or ufw, depending on what fail2ban detects on the host) that drops or rejects traffic from the banned IP. Because the action is pluggable, it is also possible to write a custom action that calls a cloud firewall API instead of or in addition to the local firewall, which matters if your provider's network-level firewall sits in front of the host and a local iptables rule alone would not stop the traffic from reaching it.
Checking status, unbanning, and logs
Use fail2ban-client to check what is running and to manually unban an IP, and check /var/log/fail2ban.log for a full history of every ban and unban event. These are the commands you will reach for most often once fail2ban is live.
# list all active jails
sudo fail2ban-client status
# check a specific jail's ban count and currently banned IPs
sudo fail2ban-client status sshd
# manually unban an IP (useful if you locked out a legitimate user)
sudo fail2ban-client set sshd unbanip 203.0.113.77
# tail the log to watch bans happen in real time
sudo tail -f /var/log/fail2ban.log
It is worth checking fail2ban-client status weekly, or wiring it into your monitoring, since a jail that silently fails to start (for example because a log path changed after a service update) leaves that service unprotected without any obvious symptom. If you already run centralized log monitoring, see our server monitoring and log analysis guide for pulling fail2ban's own log into that pipeline so bans show up alongside other security events rather than sitting in an isolated file.
Common misconfigurations to avoid
The most common fail2ban mistakes are leaving ignoreip empty, using logpath values that don't match the distribution's actual log location, and treating fail2ban as sufficient on its own rather than one layer in a broader hardening plan. Each of these quietly defeats the tool without any error message.
- Wrong logpath or backend: On systemd distributions where sshd logs to the journal instead of a flat file, a jail configured with a file-based logpath and no journal backend will simply never see any log lines, and the jail will report as enabled but never ban anything.
- Not restarting after config changes: fail2ban does not hot-reload jail.local automatically; a restart or reload is required for changes to take effect, and skipping this step is a frequent source of "why isn't this working" confusion.
- Setting maxretry too high: A maxretry of 10 or more on an internet-facing SSH jail gives automated tools ten free attempts before any consequence, which is enough for many credential lists to succeed against a weak password.
- Forgetting the recidive jail: Without it, an attacker can simply wait out each individual ban and keep retrying indefinitely at the same, unescalated rate.
- Treating fail2ban as a complete solution: It reduces brute-force noise, but it does not patch vulnerabilities, does not stop a targeted attack using a single correct credential, and does not replace the fundamentals covered in our VPS security checklist and our SSH hardening guide.
If fail2ban is deployed but a breach still happens through another vector entirely, such as an unpatched application vulnerability, having a plan already in place matters more than any single tool. Our server breach incident response guide walks through what to do in the first hours after a compromise is discovered.
Troubleshooting: if fail2ban runs but bans nothing, follow Fail2ban not banning IPs; for the wider SSH picture see how to stop SSH brute-force attacks.
Frequently asked questions
What does fail2ban actually protect against?
Fail2ban protects against automated brute-force and credential-stuffing attacks by watching log files for repeated authentication failures and temporarily blocking the offending IP address at the firewall level. It does not protect against a single well-guessed password, distributed attacks spread across many IPs, or vulnerabilities in the application itself.
Does fail2ban replace strong SSH hardening?
No. Fail2ban is a detection and response layer, not a substitute for key-based authentication, disabling root login, or changing the default SSH port. It works best alongside those measures, not instead of them.
What is the difference between bantime, findtime and maxretry?
Findtime is the rolling time window fail2ban looks back over. Maxretry is the number of failures allowed within that window before a ban is triggered. Bantime is how long the resulting ban lasts.
Can fail2ban lock me out of my own server?
Yes, if your own IP is not in the ignoreip list and you fail authentication enough times, fail2ban will ban you like anyone else. Adding your static IP or management network to ignoreip before enabling jails prevents this.
Should bans be permanent or temporary?
Most jails should use a temporary bantime measured in minutes to hours, escalating with the recidive jail for repeat offenders. Permanent bans are usually reserved for the recidive jail or for confirmed malicious IPs.
Does fail2ban work with cloud provider firewalls or only iptables?
Fail2ban's default ban actions use iptables, nftables, or ufw on the local host, but it can also be configured with custom action scripts calling a cloud provider's firewall API. For most single-server VPS setups, the local firewall action is sufficient.
How do I know if fail2ban is actually blocking anything?
Run fail2ban-client status to list active jails, and fail2ban-client status sshd to see the current ban count and banned IP list. The log at /var/log/fail2ban.log also records every ban and unban event.
Is fail2ban enough on its own to secure a VPS?
No single tool is enough on its own. Fail2ban should sit alongside SSH hardening, a properly configured firewall, timely patching, and log monitoring as part of a layered approach, not as a standalone defense.
Where is the fail2ban log file?
Fail2ban writes its own activity, every ban and unban, to /var/log/fail2ban.log by default. This is separate from the service logs (such as sshd's or nginx's) that fail2ban reads from to decide when to ban; tail -f /var/log/fail2ban.log is the quickest way to confirm a jail is acting.
Not sure where your server stands?
Get a free, read-only security check, or talk to a security engineer about managed protection.
Get your free server check Talk to a security engineer