Get started

How fail2ban works

fail2ban is a log watcher with a trigger: it reads your logs, counts failures per IP address, and runs a firewall command when an address crosses the line. Here's every moving part, from the socket to the SQLite database.

Updated 9 min readChecked against fail2ban 1.1.1

fail2ban is an open-source intrusion prevention tool for Linux that watches log files (or the systemd journal) for repeated authentication failures and bans the offending IP addresses by adding firewall rules. Each jail pairs a filter (regular expressions that recognize a failure) with an action (the commands that ban and unban). When one IP fails maxretry times within findtime, fail2ban bans it for bantime.

text
log file / journal ──► filter (failregex) ──► failure counter ──► action (nftables, iptables, firewalld…)
     auth.log            "Failed password        per IP, within         actionban  → block 203.0.113.42
                          for root from <HOST>"  findtime               actionunban → after bantime

fail2ban was written by Cyril Jaquier in 2004 and is now maintained by Serg G. Brester, Yaroslav Halchenko and contributors. It's written in Python 3, licensed GPL-2.0-or-later, and developed on GitHub. The current release is 1.1.1.

What is fail2ban used for?#

Anything that writes a recognizable line to a log when someone fails to authenticate or misbehaves:

  • SSH brute force — the classic use, and the only jail some distributions enable by default.
  • Web logins and scanners — nginx/Apache basic auth, WordPress, Nextcloud, bots probing for /wp-login.php or .env.
  • Mail servers — Postfix SASL, Dovecot IMAP/POP3 logins.
  • Anything else with logs — VPNs, VoIP (Asterisk), databases, Proxmox, Vaultwarden. Upstream ships 96 filters, and you can write your own.

It turns endless password guessing into a few attempts per attacker IP, keeps logs readable, and cuts the CPU spent answering bots. It doesn't fix weak passwords — upstream's own README says to use key-based or two-factor authentication if you really want to protect a service.

fail2ban-server and fail2ban-client#

fail2ban runs as two programs:

Program Role
fail2ban-server Long-running daemon, started by the fail2ban systemd service. Runs as root, reads logs, keeps counters, executes ban actions.
fail2ban-client Command-line tool. Reads the config files, sends them to the server at start/reload, and sends runtime commands (status, set sshd unbanip …).

They talk through a Unix socket, /var/run/fail2ban/fail2ban.sock (set in fail2ban.conf), readable by root only. That's why every client command needs sudo, and why "Failed to access socket path" means the server isn't running — see could not find server.

output
$ sudo fail2ban-client ping
Server replied: pong

An important consequence: the server only knows what the client sent it. Editing a file in /etc/fail2ban changes nothing until you run sudo fail2ban-client reload (or restart the service). The other way round, runtime changes such as set sshd bantime 86400 are lost on restart unless you also write them to jail.local. The command reference lists every client command.

Inside the server, each jail gets its own threads: one that reads the log source and one that runs actions and expires bans. A separate observer thread handles database work and incremental ban times.

What is a fail2ban jail?#

A jail is one unit of protection: this log, these failure patterns, this response. It's a section in jail.conf/jail.local:

/etc/fail2ban/jail.local
[sshd]
enabled  = true
port     = ssh
filter   = sshd
logpath  = /var/log/auth.log
backend  = auto
maxretry = 5
findtime = 10m
bantime  = 1h
action   = %(action_)s
Option Meaning
enabled Jails are off unless set to true (stock default: false)
filter Which file in filter.d/ defines failures (defaults to the jail's name)
logpath / backend Where to read: files, or the systemd journal
port / protocol What the ban blocks (stock default: all ports, tcp)
maxretry, findtime, bantime The ban policy (stock: 5 failures, 10 minutes, 10 minutes)
action / banaction What to do on ban
ignoreip Addresses never banned

Upstream's jail.conf defines about a hundred jails, all disabled. You enable the ones you need in jail.local, which overrides jail.conf. Configuration is read in this order, with later files winning: jail.conf → jail.d/*.conf → jail.local → jail.d/*.local. Full option list: jail options reference.

Filters: failregex, prefregex and datepattern#

A filter is a file in /etc/fail2ban/filter.d/ with a [Definition] section:

  • failregex — one or more Python regular expressions. A line that matches is a failure. The special tag <HOST> captures the IP address (or hostname); <ADDR> captures an IP only.
  • ignoreregex — lines matching this are skipped even if they match failregex.
  • prefregex — an optional common prefix matched first. Only the <F-CONTENT> part is then tested against each failregex, which keeps complex filters such as sshd fast.
  • datepattern — how to find the timestamp. Usually unset: fail2ban auto-detects dozens of common date formats. Lines without a usable timestamp are ignored.
  • journalmatch — for the systemd backend, which journal entries to read (for sshd: _SYSTEMD_UNIT=sshd.service + _COMM=sshd + _COMM=sshd-session).

For example, the stock sshd filter (SSH guide) recognizes the line OpenSSH writes for a bad password:

log
Oct 25 18:36:10 server sshd-session[4120]: Failed password for root from 203.0.113.42 port 52814 ssh2

Each match is logged by fail2ban as Found:

log
2026-10-25 18:36:10,238 fail2ban.filter         [1997]: INFO    [sshd] Found 203.0.113.42 - 2026-10-25 18:36:10

Filters accept parameters too: filter = sshd[mode=aggressive] switches the sshd filter to a stricter rule set. Test any filter against a real log with fail2ban-regex or the browser-based regex tester.

Actions: banaction, actionban and actionunban#

An action is a file in /etc/fail2ban/action.d/ whose [Definition] holds shell commands:

Command Runs when
actionstart The jail starts (e.g. create an nftables set or iptables chain)
actionban An IP is banned (add it to the set/chain)
actionunban The ban expires or you unban manually
actionstop The jail stops (remove the set/chain)
actioncheck Before a ban, to verify the chain or set still exists

Tags like <ip>, <name>, <port> and <failures> are replaced at run time. Some actions are Python (.py) instead of shell.

In jail.conf, the jail's action defaults to %(action_)s, which expands to the configured banaction with the jail's ports and protocol. So you normally just pick a banaction — nftables, iptables-multiport, firewallcmd-rich-rules, ufw, pf, cloudflare… — and fail2ban fills in the rest. Since 1.1.1 the default banaction comes from the distribution's paths-*.conf (nftables on Debian). Shortcuts like action_mw and action_mwl add an email with whois data and log lines. See ban actions and the action library.

Because actions run as root, fail2ban can do more than firewall bans: report to AbuseIPDB, push a Cloudflare rule, call a webhook.

Backends: how fail2ban reads logs#

The jail's backend decides how new log lines arrive:

Backend How it works Use when
auto (default) Tries pyinotify, then polling. Since 1.1.1 it can switch to systemd when no log file exists and the filter has a journalmatch Most file-based jails
pyinotify Kernel inotify events on file changes Files, with python3-pyinotify installed
polling Checks files for changes on a timer Files, no extra libraries
systemd Reads the systemd journal via python-systemd, filtered by journalmatch; logpath is ignored sshd and other services on systems without rsyslog

This is the source of the most common install problem: Debian 12, Fedora, Arch and Raspberry Pi OS Bookworm don't write /var/log/auth.log or /var/log/secure, so a file-based sshd jail has nothing to read. Details: backends and the systemd journal.

The ban lifecycle: findtime, maxretry and bantime#

  1. Match. A log line matches the filter. fail2ban extracts the IP and the line's timestamp. Lines older than findtime are ignored.
  2. Ignore check. If the IP matches ignoreip, the server's own addresses (ignoreself = true by default) or an ignorecommand, it's dropped here.
  3. Count. The failure is added to a per-IP fail ticket, fail2ban's internal record for that address. Failures older than findtime drop out of the count. A filter can count another value than the IP with an <F-ID> group (see custom filters).
  4. Ban. When the ticket reaches maxretry failures within findtime, it becomes a ban ticket holding the ban time, ban count and matched lines (maxmatches, which actions read as <matches>). The action's actionban runs and the ban is logged:
log
2026-10-25 18:36:11,027 fail2ban.actions        [1997]: NOTICE  [sshd] Ban 203.0.113.42
  1. Unban. After bantime the actions thread runs actionunban:
log
2026-10-25 19:36:11,031 fail2ban.actions        [1997]: NOTICE  [sshd] Unban 203.0.113.42

With the stock values (maxretry = 5, findtime = 10m, bantime = 10m), an IP needs five failures in ten minutes to get a ten-minute ban. A negative bantime (-1) makes bans permanent. With bantime.increment = true, each new ban of the same IP lasts longer (by default ×2 each time, optionally capped by bantime.maxtime). See bantime, findtime and maxretry and the bantime calculator.

The ban database and restarts#

fail2ban stores bans in SQLite at /var/lib/fail2ban/fail2ban.sqlite3 (dbfile in fail2ban.conf). On start, it re-applies bans that haven't expired yet — the log shows Restore Ban for each — so restarting the service doesn't free everyone. The database also remembers previous bans for incremental ban times and records how far each log file was read, so fail2ban doesn't rescan old lines after a restart.

Entries older than dbpurgeage (default 1d) are deleted. If you use bans longer than a day, raise dbpurgeage so long bans survive a restart. See fail2ban.conf settings.

Recidive: banning repeat offenders#

The recidive jail is fail2ban watching itself. Its filter reads fail2ban's own log (/var/log/fail2ban.log) for Ban lines from other jails. With the stock settings, an IP banned 5 times within a day (findtime = 1d) gets banned for a week (bantime = 1w) on all ports (banaction = %(banaction_allports)s). It's a simple way to deal with bots that come back after every short ban. See permanent bans and recidive.

Is fail2ban a firewall? IDS or IPS?#

fail2ban isn't a firewall. It doesn't filter packets itself. It decides whom to block and tells your firewall — nftables, iptables, firewalld, UFW, pf, or a remote one such as Cloudflare — to do it. Without a working firewall back end, fail2ban logs bans that block nothing.

It's a host-based intrusion prevention system (IPS) in the simple, reactive sense: it detects abuse from logs and acts on it automatically. It's not a full IDS — it doesn't inspect packets, doesn't know about vulnerabilities, and can only react to what the application already logged. It can't stop a single successful exploit, a slow attack from thousands of IPs that each stay under maxretry, or traffic that never produces a log line. Treat it as one layer next to key-based auth, updates and a default-deny firewall — the best practices page puts it in context. Short answers to these and other yes/no questions are in the fail2ban FAQ.

FAQ#

What is fail2ban?#

fail2ban is a free, open-source intrusion prevention tool for Linux and other Unix systems. It scans log files and the systemd journal for repeated failed logins or other abuse and temporarily bans the offending IP addresses through the firewall. It's written in Python and licensed under the GPL.

How does fail2ban work?#

fail2ban-server reads logs through jails. Each jail's filter uses regular expressions to recognize failures and extract the IP address. When an IP reaches maxretry failures within findtime, the jail's action adds a firewall rule that blocks it for bantime, then removes the rule when the ban expires.

What is a jail in fail2ban?#

A jail is a configuration section that ties together a log source, a filter (what counts as a failure), ban thresholds (maxretry, findtime, bantime) and an action (how to ban). For example, the sshd jail watches SSH logins. Jails are defined in jail.conf and enabled in jail.local.

What is the difference between fail2ban-server and fail2ban-client?#

fail2ban-server is the background daemon that reads logs and runs bans. fail2ban-client is the command-line tool that starts, configures and queries the server over a Unix socket. You always interact through the client, for example sudo fail2ban-client status sshd.

Spotted something wrong or out of date? Tell us — every page is checked against the current fail2ban release.