# fail2ban for Postfix and Dovecot

> Protect a mail server with fail2ban: postfix, postfix-sasl and dovecot jails, filter modes, systemd journal setup, Dovecot 2.4 logs and mailcow/iRedMail notes.
>
> Source: https://fail2ban.dev/guides/postfix-dovecot/ · Updated 2026-10-25 · Checked against fail2ban 1.1.1
> fail2ban.dev is an independent guide, not the official fail2ban project.

To protect Postfix and Dovecot with fail2ban, enable three stock jails in `/etc/fail2ban/jail.local`: `[postfix-sasl]` for SMTP AUTH password guessing, `[dovecot]` for IMAP/POP3 logins and `[postfix]` for rejected SMTP clients. On Debian 12/13 and Ubuntu 24.04 mail logs live in the systemd journal, so add `backend = systemd`. Check with `sudo fail2ban-client status postfix-sasl`.

```ini
[postfix-sasl]
enabled  = true
backend  = systemd
maxretry = 3

[dovecot]
enabled = true
backend = systemd

[postfix]
enabled = true
backend = systemd
```

## Which mail jails fail2ban ships

All of these are defined in `jail.conf` and are disabled until you enable them:

| Jail | Filter | Catches | Banned ports |
|---|---|---|---|
| `postfix-sasl` | `postfix[mode=auth]` | `SASL LOGIN/PLAIN authentication failed` | smtp, 465, submission, imap, imaps, pop3, pop3s |
| `postfix` | `postfix` (mode `more`) | SMTP rejects: relay denied, unknown users, bad HELO, RBL hits | smtp, 465, submission |
| `postfix-rbl` | `postfix[mode=rbl]` | Clients refused because they're on a DNS blocklist (`maxretry = 1`) | smtp, 465, submission |
| `dovecot` | `dovecot` | Failed IMAP, POP3, ManageSieve and submission logins | pop3, pop3s, imap, imaps, submission, 465, sieve |
| `sieve` | `sieve` | Cyrus `timsieved` login failures (not Dovecot) | smtp, 465, submission |

`postfix-sasl` bans the IMAP and POP3 ports too, because the same password is usually valid for both protocols. An attacker who fails SMTP AUTH shouldn't get a fresh start on IMAP.

The old `filter.d/postfix-sasl.conf` and `postfix-rbl.conf` files no longer exist. They became modes of the single [postfix filter](/guides/postfix-dovecot/#filter-postfix), so `filter = postfix-sasl` in an old guide now fails with a "filter not found" error. Use the jail names above and let `jail.conf` pick the filter.

## Step by step: Postfix and Dovecot on Debian or Ubuntu


1. **Install fail2ban and the journal bindings.** `sudo apt install fail2ban python3-systemd`. Debian 12 and 13 don't install rsyslog, so `/var/log/mail.log` doesn't exist and the journal is the only log source.
2. **Create the jails.** Put the block from the top of this page in `/etc/fail2ban/jail.local`, and set [`ignoreip`](/config/whitelist/) to your office or VPN network so a misconfigured phone doesn't lock out the boss.
3. **Check the config.** `sudo fail2ban-client -t` parses everything and reports errors without touching the running service.
4. **Reload.** `sudo systemctl restart fail2ban`, or `sudo fail2ban-client reload` if it's already running.
5. **Confirm the jails are running.** `sudo fail2ban-client status` should list `dovecot, postfix, postfix-sasl`.

fail2ban 1.1.1's `paths-debian.conf` already sets `postfix_backend = systemd`, so on 1.1.1 the Postfix jails use the journal without being told. The packaged 1.0.2 (Debian 12, Ubuntu 24.04) and 1.1.0 (Debian 13, Ubuntu 26.04) don't, which is why the explicit `backend = systemd` lines are there. They're harmless on 1.1.1.

The filters bring their own journal matches. Postfix reads `_SYSTEMD_UNIT=postfix.service` and `postfix@-.service` (Debian runs the real daemon as `postfix@-`), and Dovecot reads `_SYSTEMD_UNIT=dovecot.service`.

> **Still using rsyslog?**
If `/var/log/mail.log` exists and you'd rather read files, use `backend = auto` instead of `systemd` (on Ubuntu 24.04 you must say so explicitly, because its `defaults-debian.conf` sets `backend = systemd` for every jail). On Debian-family systems `jail.conf` points all mail jails at `/var/log/mail.log`. Older guides suggest `/var/log/mail.warn` for smaller files, which works only if rsyslog writes that file. More in [choosing a backend](/config/backend/).

## The postfix-sasl jail (SMTP AUTH brute force)

This is the jail that matters most on a mail server. Bots try leaked passwords against port 587 and 465, and Postfix logs each failure like this:

```log
```

A stricter setup for a server with few legitimate users:

```ini
[postfix-sasl]
enabled  = true
backend  = systemd
maxretry = 3
findtime = 1h
bantime  = 1d
```

### "fail2ban postfix-sasl not working"

The usual causes, in order:

1. **No log source.** `Have not found any log file for postfix-sasl jail` means `/var/log/mail.log` is missing. Add `backend = systemd` ([full fix](/troubleshooting/no-log-file/)).
2. **The SMTP AUTH failures never reach the log.** Postfix logs them only when SASL is handled by smtpd. If authentication happens on a separate submission proxy, the proxy's logs need their own jail.
3. **Wrong jail name or filter.** `filter = postfix-sasl` points at a file that no longer exists. Use `[postfix-sasl]` from `jail.conf` with no `filter` line.

Test the filter against the journal directly:

```bash
```

The `[postfix]` jail uses `mode = more` by default, which combines `normal` and `rbl`. Set a different mode in the jail to change what it bans:

| Mode | What it matches |
|---|---|
| `normal` | `reject:` lines (relay access denied, unknown user, bad HELO, sender domain not found), `too many errors` |
| `rbl` | `554 5.7.1 Service unavailable; Client host [...] blocked` |
| `more` (default) | `normal` + `rbl` |
| `auth` | SASL authentication failures (used by `postfix-sasl`) |
| `ddos` | `lost connection after CONNECT/EHLO`, postscreen `PREGREET`/`HANGUP`, `COMMAND ... LIMIT`, connection rate limit exceeded |
| `extra` | `auth` + `normal` |
| `errors` | `too many errors after ...` only, for a ban on the first hit |
| `aggressive` | Everything above, plus `hostname ... does not resolve to address` |

```ini
[postfix]
enabled = true
backend = systemd
mode    = aggressive
```

## The postfix-rbl jail

`postfix-rbl` bans a client the first time Postfix rejects it for being on a DNS blocklist (`maxretry = 1`). It only works if Postfix itself checks blocklists, for example with `reject_rbl_client zen.spamhaus.org` in `smtpd_recipient_restrictions`, or with postscreen DNSBL scoring. Without that, the jail never matches anything.

```ini
[postfix-rbl]
enabled = true
backend = systemd
bantime = 1d
```

## The dovecot jail

The [dovecot filter](/guides/postfix-dovecot/#filter-dovecot) catches failed logins on `imap-login`, `pop3-login`, `managesieve-login` and `submission-login`, plus PAM, SQL, LDAP and passwd-file authentication failures from `auth-worker`:

```log
```

The filter has an `aggressive` mode that also bans clients disconnecting without trying to log in (`no auth attempts`, `disconnected before auth was ready`, `disconnected during TLS handshake`). That stops scanners, but the Dovecot authors of the filter warn it can catch misconfigured MTAs and clients:

```ini
[dovecot]
enabled = true
backend = systemd
mode    = aggressive
```

Dovecot 2.4 changed its log lines. A failed IMAP login now looks like this:

```log
```

```bash
  https://raw.githubusercontent.com/fail2ban/fail2ban/1.1.1/config/filter.d/dovecot.conf
sudo fail2ban-regex systemd-journal dovecot
sudo systemctl restart fail2ban
```

## Sieve, ManageSieve and webmail

- **Dovecot ManageSieve** (port 4190) is already covered by the `dovecot` filter through `managesieve-login`. Add `4190` to the jail's ports if clients reach it from outside: `port = pop3,pop3s,imap,imaps,submission,465,sieve,4190`.
- **The `sieve` jail** is for Cyrus `timsieved` (`badlogin: ... authentication failure`), not Dovecot. Its stock `port` line lists SMTP ports; set `port = 4190` if you use it.
- **Roundcube** has its own stock jail, `roundcube-auth`, that reads Roundcube's error log. Webmail logins go through IMAP, so the dovecot jail sees them too, but from the webmail server's IP. Make sure that IP is in `ignoreip` or fail2ban will ban your own webmail.

## Test the mail jails

Run the filters against what's already in the journal before trusting them:

```bash
sudo fail2ban-regex systemd-journal 'postfix[mode=more]'
sudo fail2ban-regex systemd-journal dovecot
```

Then check that the jails count failures:

```bash
sudo fail2ban-client status dovecot
```

## mailcow, Mail-in-a-Box, iRedMail and Zimbra

All-in-one mail stacks usually bring their own banning. Adding a second fail2ban layer on top often fights it.

- **mailcow** doesn't use fail2ban. Its `netfilter-mailcow` container watches the Postfix, Dovecot, SOGo and web UI logs and bans at the firewall itself. Configure ban time, max attempts and the allowlist in the mailcow admin UI under the Fail2ban parameters. When "mailcow fail2ban not working" comes up, look at the netfilter container's logs (`docker compose logs netfilter-mailcow`), not at a host fail2ban.
- **Mail-in-a-Box** installs fail2ban and drops its own jails into `/etc/fail2ban/jail.d/mailinabox.conf`. Put your changes in a `.local` file so the setup script doesn't overwrite them.
- **iRedMail** installs fail2ban with jails for Postfix, Dovecot, Roundcube and SOGo in `/etc/fail2ban/jail.d/`, plus an optional SQL-backed ban list. Edit those files rather than adding duplicate jails.
- **Zimbra** has no stock fail2ban filter. Zimbra logs authentication failures in `/opt/zimbra/log/audit.log` with the client address in an `oip=` field, so a custom filter has to match that field rather than the proxy address. Build it with the [filter generator](/tools/filter-generator/) and test it with `fail2ban-regex` against your own log before enabling it. Zimbra's own account lockout and DoS filter are worth enabling either way.
- **docker-mailserver** includes fail2ban inside the container. Enable it with `ENABLE_FAIL2BAN=1` and manage it with `docker exec <container> fail2ban-client`, not the host's fail2ban.

## postfix, dovecot and sieve filter reference

{{component:filter-ref:postfix.dovecot.sieve}}

## FAQ

### How do I configure fail2ban for Postfix?

Enable the stock `[postfix-sasl]` and `[postfix]` jails in `/etc/fail2ban/jail.local` with `enabled = true`, add `backend = systemd` on Debian 12/13 or Ubuntu 24.04, and restart fail2ban. `postfix-sasl` stops SMTP AUTH password guessing; `postfix` bans clients Postfix rejects. Check with `fail2ban-client status postfix-sasl`.

### Why is fail2ban postfix-sasl not working?

Usually there's no log for it to read: Debian 12/13 have no `/var/log/mail.log`, so set `backend = systemd`. Also check that you didn't set `filter = postfix-sasl`, because that filter file was replaced by `postfix[mode=auth]`. Then test with `fail2ban-regex systemd-journal 'postfix[mode=auth]'`.

### Does fail2ban work with Dovecot 2.4?

Yes, from fail2ban 1.1.1, which added the 2.4 `Login aborted ... (auth failed ...)` format. fail2ban 1.1.0 on Debian 13 misses those lines. Install the 1.1.1 `dovecot.conf` as `/etc/fail2ban/filter.d/dovecot.local` and verify with `fail2ban-regex systemd-journal dovecot`.

### What does the postfix-rbl jail do?

It bans an SMTP client on the first rejection for being on a DNS blocklist, using `filter = postfix[mode=rbl]` and `maxretry = 1`. It only matches if Postfix itself checks blocklists, for example with `reject_rbl_client` or postscreen. Its job is to save DNS lookups and connections from known bad hosts.

### Should I enable fail2ban on mailcow?

No. mailcow ships its own banning container (`netfilter-mailcow`) that reads the mail logs and blocks at the firewall. Configure it in the mailcow admin UI. A host fail2ban can still protect SSH, but it shouldn't duplicate the mail jails.

### Which postfix mode is best for fail2ban?

Keep the default `more` (normal + rbl) for the `postfix` jail and run `postfix-sasl` alongside it. Move to `aggressive` only after checking a week of bans. It adds connection-level and DNS checks that stop more bots but can catch legitimate servers with broken DNS.
