# fail2ban best practices and recommended settings

> fail2ban best practices: recommended bantime, findtime and maxretry, ignoreip, incremental bans, recidive, systemd backend, nftables, permissions, updates.
>
> Source: https://fail2ban.dev/best-practices/ · Updated 2026-10-25 · Checked against fail2ban 1.1.1
> fail2ban.dev is an independent guide, not the official fail2ban project.

fail2ban best practices in short: use SSH keys first and treat fail2ban as an extra layer, whitelist your own IPs with `ignoreip`, use `bantime = 1h`, `findtime = 10m`, `maxretry = 5` with `bantime.increment = true`, enable the `recidive` jail, read logs through the systemd backend, ban with nftables, test every filter with `fail2ban-regex`, keep `/etc/fail2ban` root-owned, and run a current release (1.1.1).

## The checklist

| # | Practice | Why |
|---|---|---|
| 1 | Key-based SSH, no password login | fail2ban limits guessing; keys make guessing pointless |
| 2 | Put your own IPs in `ignoreip` | Avoid locking yourself out |
| 3 | Config only in `.local` files | Upgrades don't overwrite your settings |
| 4 | `bantime = 1h`, `findtime = 10m`, `maxretry = 5` | Sensible baseline instead of 10-minute bans |
| 5 | `bantime.increment = true` | Repeat offenders get longer bans automatically |
| 6 | Enable `recidive` | Week-long, all-ports bans for persistent bots |
| 7 | `backend = systemd` where logs are in the journal | Works on Debian 12+, Fedora, Arch; no missing-file errors |
| 8 | `banaction = nftables` (or your firewall's action) | One set per jail, scales to many bans |
| 9 | Test filters and config before reloading | Silent "never bans" failures are common |
| 10 | Monitor bans and fail2ban's own log | Know it's working |
| 11 | Protect web apps and use the real client IP | SSH is rarely the only login |
| 12 | Keep fail2ban updated | Filter fixes and security fixes |
| 13 | Root-owned config, no sudo for fail2ban-client | Write access to actions = root access |
| 14 | Back up `/etc/fail2ban` | Rebuild a server in minutes |

## 1. Fix authentication first

fail2ban's own README is blunt about it: it reduces the rate of failed logins but can't eliminate the risk of weak authentication. Before you tune any jail:

- Use SSH keys and set `PasswordAuthentication no` and `PermitRootLogin prohibit-password` (or `no`) in `sshd_config`.
- Use two-factor authentication on web panels that support it.
- Expose only what must be public; put admin interfaces behind a VPN.

With password login disabled, the sshd jail still earns its keep: it cuts log noise and stops bots from burning CPU on key exchanges. The [SSH guide](/guides/ssh/) covers both sides.

## 2. Whitelist your own addresses with ignoreip

```ini
[DEFAULT]
ignoreip = 127.0.0.1/8 ::1 192.168.1.0/24 203.0.113.10
```

## 3. Never edit jail.conf

Put every change in `/etc/fail2ban/jail.local` or `jail.d/*.local`. fail2ban reads `jail.conf` → `jail.d/*.conf` → `jail.local` → `jail.d/*.local`, and later files win. Package upgrades replace `.conf` files but leave `.local` files alone. Copying the whole of `jail.conf` to `jail.local` is a common mistake: you freeze old defaults and miss upstream fixes. Only copy the lines you change — see the [jail.local example](/config/jail-local/).

## 4. Recommended bantime, findtime and maxretry

The stock values (`bantime = 10m`, `findtime = 10m`, `maxretry = 5`) are deliberately cautious. A good baseline for internet-facing servers:

| Setting | Stock | Recommended | Notes |
|---|---|---|---|
| `bantime` | `10m` | `1h` | Starting value when increments are on |
| `findtime` | `10m` | `10m`–`1h` | Longer windows catch slow guessers |
| `maxretry` | `5` | `5` (SSH with keys: `3`) | Lower values ban humans with typos |
| `bantime.increment` | off | `true` | Doubles each repeat ban |
| `bantime.maxtime` | none | `1w`–`4w` | Caps the growth |

Avoid permanent bans (`bantime = -1`) as a default. Addresses get reassigned, firewall sets grow forever, and a single mistake locks out a legitimate user for good. Incremental bans with a cap get you most of the benefit. Use the [bantime calculator](/tools/bantime-calculator/) to see what a policy does over time, and [bantime, findtime and maxretry](/config/bantime-findtime-maxretry/) for the trade-offs.

## 5–6. Incremental bans and recidive

```ini
[DEFAULT]
bantime  = 1h
findtime = 10m
maxretry = 5
bantime.increment = true
bantime.maxtime = 2w
bantime.overalljails = true

[recidive]
enabled = true
```
- **`recidive`** watches fail2ban's own log and bans an IP banned 5 times in a day for a week, on all ports. See [recidive](/config/recidive/).

Both depend on history, so keep it long enough. Raise the database purge age above your longest ban:

```ini
[DEFAULT]
dbpurgeage = 30d
```

## 7. Use the systemd backend where logs live in the journal

On Debian 12+, Ubuntu 24.04+, Fedora, Arch, Raspberry Pi OS and Proxmox VE 8+, SSH failures exist only in the journal. Set `backend = systemd` for those jails (Debian 13 and Ubuntu do this for sshd already). Keep file-based jails such as nginx on `backend = auto` — a systemd backend ignores `logpath`. See [backends and the systemd journal](/config/backend/).

## 8. Ban with nftables (or your firewall's native action)

Match the ban action to the firewall you actually run:

| Firewall | `banaction` |
|---|---|
| nftables (Debian, Ubuntu 24.04+, Arch) | `nftables` |
| firewalld (RHEL family, Fedora) | `firewallcmd-rich-rules` (default from `fail2ban-firewalld`) |
| UFW, if you want bans in `ufw status` | `ufw` |
| Legacy iptables | `iptables-multiport` / `iptables-ipset` |

The [nftables action](/actions/#action-nftables) keeps each jail's bans in one set, so thousands of bans cost one rule, not thousands. Set `banaction_allports` to the matching all-ports variant for recidive. More in [ban actions](/config/banaction/).

## 9. Test filters and config before you rely on them

The most common fail2ban failure is silent: the jail starts, shows no errors, and never matches anything. Test before reloading:

```bash
sudo fail2ban-regex /var/log/nginx/error.log nginx-http-auth
sudo fail2ban-regex systemd-journal sshd    # journal-based jails
sudo fail2ban-client reload
```

## 10. Monitor fail2ban

```bash
sudo fail2ban-client banned
sudo grep -E 'NOTICE|WARNING|ERROR' /var/log/fail2ban.log | tail -50
```

- A jail whose "Total failed" never grows — it isn't reading anything.
- `ERROR` lines from actions — bans are logged but not applied.
- Your own IPs in the ban list.

For dashboards and alerts, export metrics to Prometheus ([monitoring guide](/guides/monitoring/)), or analyze an existing log with the [log analyzer](/tools/log-analyzer/). Email on every ban (`action_mwl`) gets noisy on busy servers; reserve it for low-traffic jails.

## 11. Protect web apps — with the real client IP

Bots try SSH, but they also try `/wp-login.php`, `/.env`, basic-auth prompts and app logins. Enable jails for what you expose: `nginx-http-auth`, `nginx-botsearch`, `nginx-limit-req`, `apache-auth`, plus app filters such as WordPress, Nextcloud or Vaultwarden (see [guides](/guides/)).

Behind a reverse proxy, CDN or tunnel, make sure the application logs the **client's** address, not the proxy's. Otherwise fail2ban bans your own proxy or Cloudflare — [here is the fix](/troubleshooting/reverse-proxy-ip/). For sites behind Cloudflare, also ban at the edge with the [Cloudflare guide](/guides/cloudflare/).

## 12. Keep fail2ban updated

Filters change as software changes its log formats, and fail2ban has had security fixes in filters. Release 1.1.1, for example:

- matches the `sshd-session` process name that OpenSSH 9.8+ uses;
- fixes catastrophic regex backtracking (ReDoS) in the dovecot and domino-smtp filters (GHSA-33wh-ccjc-p397).

Install your distribution's updates, check `fail2ban-client --version`, and read [versions and changelog](/versions/) and [security advisories](/security/) when upgrading. If your distribution ships an old release and you need a fix, override just the affected filter in `filter.d/<name>.local` instead of replacing the whole package.

## 13. Lock down permissions: action.d is root code

`fail2ban-server` runs as root and executes the commands in `action.d` (and the `ignorecommand`) as root. Anyone who can write to `/etc/fail2ban` — or who can talk to fail2ban's socket — can run commands as root. This is a well-known privilege-escalation path when a non-root user or group has write access to `action.d`.

```bash
sudo find /etc/fail2ban \( ! -user root -o -perm /022 \) -ls
```

- **Don't give users `sudo fail2ban-client`.** The client can redefine an action's `actionban` command at runtime, so it's equivalent to full root.
- Keep the socket at its default (`/var/run/fail2ban/fail2ban.sock`, mode 0600, root).
- Review third-party filters and actions before dropping them into `/etc/fail2ban`, just like any script you run as root.

## 14. Back up and version your configuration

Everything that makes your setup yours is in `/etc/fail2ban/*.local`, `jail.d/`, and custom files in `filter.d/` and `action.d/`:

```bash
```

## Don't rely on fail2ban alone

fail2ban is reactive: it only acts after an application has logged a failure. It doesn't help against:

- a single successful login with a stolen password or key;
- exploits that don't produce a failed-login log line;
- distributed attacks where thousands of IPs each stay below `maxretry`;
- volumetric DDoS — the traffic still reaches your network.

Pair it with a default-deny firewall, key-based authentication, prompt OS updates and, for web apps, a WAF or rate limiting. If you need shared block lists across many servers, compare [fail2ban alternatives](/alternatives/) such as CrowdSec.

## FAQ

### What are the recommended fail2ban settings?

A good baseline is `bantime = 1h`, `findtime = 10m`, `maxretry = 5`, `bantime.increment = true` with `bantime.maxtime = 1w` or more, your own IPs in `ignoreip`, the recidive jail enabled, `backend = systemd` for journal-based services and an nftables or firewalld ban action. Put it all in `jail.local`.

### Should I set fail2ban bans to permanent?

Usually not. Permanent bans (`bantime = -1`) grow your firewall sets forever and can lock out reassigned or shared IPs for good. Incremental bans (`bantime.increment = true` with a `bantime.maxtime` of a week or more) plus the recidive jail block persistent attackers almost as well, and still let mistakes expire.

### Is fail2ban a security risk?

It runs as root and executes shell commands from `action.d`, so writable config files or sudo access to `fail2ban-client` amount to root access. Keep `/etc/fail2ban` root-owned and not group- or world-writable, don't delegate `fail2ban-client` via sudo, and keep fail2ban updated for filter security fixes.
