# How fail2ban works

> What fail2ban is and how it works: server and client, jails, filters with failregex, ban actions, backends, findtime/maxretry/bantime, the ban database and recidive.
>
> Source: https://fail2ban.dev/how-it-works/ · Updated 2026-10-25 · Checked against fail2ban 1.1.1
> fail2ban.dev is an independent guide, not the official fail2ban project.

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
     auth.log            "Failed password        per IP, within         actionban  → block 203.0.113.42
                          for root from <HOST>"  findtime               actionunban → after bantime
```

## 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](/filters/custom/).

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](/troubleshooting/could-not-find-server/).

```text
$ sudo fail2ban-client ping
Server replied: pong
```

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`:

```ini
[sshd]
enabled  = true
port     = ssh
filter   = sshd
logpath  = /var/log/auth.log
backend  = auto
maxretry = 5
findtime = 10m
bantime  = 1h
action   = %(action_)s
```
|---|---|
| `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](/config/jail-options/).

## 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](/guides/ssh/)) recognizes the line OpenSSH writes for a bad password:

```log
```

```log
```

## 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](/config/banaction/) and the [action library](/actions/).

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](/config/backend/).

## 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](/filters/custom/)).
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
```

```log
```

## 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](/config/fail2ban-conf/).

## 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](/config/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](/best-practices/) page puts it in context. Short answers to these and other yes/no questions are in the [fail2ban FAQ](/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`.
