Configuration
fail2ban backends and the systemd journal
The backend decides where a jail reads its log lines from: a log file, or the systemd journal. Get it wrong and the jail starts but never sees a single failure.
The backend option tells a fail2ban jail how to read logs. auto (the default) watches the files in logpath with pyinotify, or polling if pyinotify isn't installed. systemd reads the systemd journal instead, using the filter's journalmatch and ignoring logpath. Use backend = systemd for services that log only to the journal, such as sshd on Debian 12 and later, which has no /var/log/auth.log. It needs the python3-systemd package.
[sshd]
enabled = true
backend = systemd ; read sshd messages from the journal
[nginx-http-auth]
enabled = true
backend = auto ; read /var/log/nginx/*error.logThe four backends#
| Backend | Reads | Needs | Use it for |
|---|---|---|---|
auto |
Log files (logpath) |
nothing | The default. Tries pyinotify, then polling. Since 1.1.1 it can switch to systemd when no log file exists (see below). |
pyinotify |
Log files | python3-pyinotify |
Event-driven file watching on Linux; lowest latency and CPU for busy logs. |
polling |
Log files | nothing | Checks files for changes every couple of seconds. Works everywhere, including network filesystems and containers where inotify misbehaves. |
systemd |
The systemd journal | python3-systemd |
Services that log to journald only. logpath is ignored. |
If the backend you ask for can't load, fail2ban falls back down the list: pyinotify falls back to polling with a warning. systemd has nothing to fall back to, so a missing python3-systemd stops the jail:
ERROR NOK: ("Failed to initialize any backend for Jail 'sshd'",)When to use the systemd backend#
Use backend = systemd when the service's messages exist only in the journal:
- Debian 12 and later: no rsyslog by default, so no
/var/log/auth.log. The sshd jail fails with "Have not found any log file for sshd jail" until you switch it tosystemd. - Fedora, RHEL 9+, Arch, openSUSE:
paths-*.confalready setssshd_backend = systemd(and for postfix, dovecot and FTP servers), so those jails read the journal out of the box. - Services you run as systemd units that log to stdout: many Go, Node and Python apps.
Keep file backends for services that write their own log files: nginx, Apache, Nextcloud, Vaultwarden, mail servers configured to log to files. They don't show those lines in the journal.
You can mix both freely; backend is per jail. The trap is setting backend = systemd in [DEFAULT]: every file-based jail then ignores its logpath and silently sees nothing. Ubuntu 24.04's jail.d/defaults-debian.conf does exactly that, so on Ubuntu 24.04 set backend = auto on (or above) your file-based jails.
Install python3-systemd#
sudo apt install python3-systemdsudo dnf install python3-systemdsudo pacman -S python-systemdThen reload and check that the jail lists its journal matches instead of files:
$ sudo fail2ban-client status sshd
Status for the jail: sshd
|- Filter
| |- Currently failed: 2
| |- Total failed: 6
| `- File list: /var/log/auth.log
`- Actions
|- Currently banned: 1
|- Total banned: 1
`- Banned IP list: 203.0.113.42That's the output with a file backend. With backend = systemd, the File list: line is replaced by Journal matches: _SYSTEMD_UNIT=sshd.service + _COMM=sshd + _COMM=sshd-session.
journalmatch: which journal entries a jail reads#
A systemd jail doesn't read the whole journal. It reads only entries matching journalmatch, which comes from the filter and can be overridden in the jail:
journalmatch = _SYSTEMD_UNIT=sshd.service + _COMM=sshd + _COMM=sshd-sessionThe syntax is the same as journalctl matches:
FIELD=valuematches entries with that field value.- Several terms separated by spaces must all match (AND), except that repeating the same field means either value.
+separates alternatives (OR).
So the line above means "unit sshd.service, OR process name sshd, OR process name sshd-session". On Debian and Ubuntu the unit is called ssh.service, but the _COMM=sshd alternative still catches its messages. Debian 13 and Ubuntu 26.04 packages set journalmatch = _SYSTEMD_UNIT=ssh.service + _COMM=sshd for the sshd jail.
Check what a match returns before you use it, with the same expression in journalctl:
journalctl _SYSTEMD_UNIT=ssh.service + _COMM=sshd -n 20 --no-pagerOverriding it in a jail, for example for an app unit:
[myapp]
enabled = true
filter = myapp
backend = systemd
journalmatch = _SYSTEMD_UNIT=myapp.service
port = http,httpsFind the right field values with journalctl -u myapp -o verbose -n 1, which prints every field of the last entry (_SYSTEMD_UNIT, _COMM, SYSLOG_IDENTIFIER, CONTAINER_NAME…).
systemd backend parameters#
Pass options in brackets after systemd:
| Parameter | Meaning |
|---|---|
journalflags |
Which journals to open. 1 = local machine only (LOCAL_ONLY), 4 = system journal only (SYSTEM_ONLY, skips user-session journals) |
journalpath |
Read a journal directory instead of the default, e.g. a container's or remote machine's journal |
journalfiles |
Read specific journal files (comma-separated, globs allowed) |
namespace |
Read a journald namespace |
rotated |
1.1.1+: also open rotated journal files. Default off |
journalflags, journalpath, journalfiles and namespace are mutually exclusive.
backend = systemd[journalflags=4]
backend = systemd[journalpath=/var/log/journal/remote]
backend = systemd[rotated=on]The rotated option (1.1.1)#
Since 1.1.1, fail2ban ignores rotated journal files (*@*.journal) by default and opens only the active ones. Older versions opened every journal file per jail, which on servers with many jails and a large journal caused "Too many open files" errors and high memory use. The trade-off: after a journal rotation, fail2ban can't look back further than the rotation, which only matters if your findtime is longer than the time between rotations. Set rotated=on if you need that.
Automatic switch to systemd (1.1.1+)#
Since 1.1.1 a jail with backend = auto switches to the journal on its own when:
- no file matches its
logpath, - the jail or its filter has a
journalmatch, and systemd_if_nologsisn't set tofalse(it defaults totrue).
That's why a fresh Debian 13 sshd jail works without /var/log/auth.log. Jails whose filter has no journalmatch (most web application filters) still fail with "Have not found any log file". For those, either fix the logpath or add skip_if_nologs = true so fail2ban starts the other jails and skips this one with a warning. More on the no log file error.
Reading logs from Docker containers#
Two ways to feed container logs to a jail on the host:
- journald log driver: run the container with
--log-driver=journald(or"log-driver": "journald"in/etc/docker/daemon.json). Each entry gets aCONTAINER_NAMEfield:
[vaultwarden]
enabled = true
backend = systemd
journalmatch = CONTAINER_NAME=vaultwarden- Log file on a volume: let the app write its log to a mounted directory (for example
./data/vaultwarden.log) and pointlogpathat the host path withbackend = autoorpolling. Polling is the safest choice for files on bind mounts.
Remember that bans on the host's INPUT chain don't stop traffic to published container ports; see the Docker guide.
Test a filter against the journal#
fail2ban-regex reads the journal too. Use the filter name or file and systemd-journal as the log source:
# test the sshd filter against the journal, using the filter's journalmatch
sudo fail2ban-regex systemd-journal sshd
# your own match, including rotated journal files
sudo fail2ban-regex -m "_SYSTEMD_UNIT=myapp.service" "systemd-journal[rotated=on]" /etc/fail2ban/filter.d/myapp.confAll options are on the fail2ban-regex page.
fail2ban's own log in the journal#
journalctl -u fail2ban shows fail2ban's own messages (start-up errors, bans) whenever it runs as a systemd service. Where fail2ban writes its log is a separate setting, logtarget, in fail2ban.conf. If you set it to the journal, the recidive jail must use backend = systemd too.
FAQ#
What backend should fail2ban use?#
Leave backend = auto for services that write log files (nginx, Apache, most web apps) and use backend = systemd for services that log only to the journal, such as sshd on Debian 12+, Fedora and RHEL. The backend is per jail, so mix them as needed; avoid setting systemd in [DEFAULT].
How do I make fail2ban read the systemd journal?#
Install python3-systemd, set backend = systemd in the jail and remove any reliance on logpath. The jail reads journal entries matching the filter's journalmatch, for example _SYSTEMD_UNIT=sshd.service + _COMM=sshd. Check with sudo fail2ban-client status <jail>, which then shows Journal matches.
What is journalmatch in fail2ban?#
A systemd journal filter, written like journalctl matches, that selects which journal entries a jail reads. Stock filters define it (sshd uses _SYSTEMD_UNIT=sshd.service + _COMM=sshd + _COMM=sshd-session); you can override it per jail. It's only used with the systemd backend.
Why does the systemd backend find no failures?#
Usually the journalmatch doesn't match the unit name (for example ssh.service vs sshd.service), the service writes to a file instead of the journal, or fail2ban can't read the journal files. Test with sudo fail2ban-regex systemd-journal <filter> and compare with journalctl using the same match.
What is the difference between pyinotify and polling?#
pyinotify uses Linux inotify events and reacts as soon as a log file changes; polling checks the files for changes every couple of seconds. Both read the same files. auto uses pyinotify when the Python module is installed and polling otherwise. Prefer polling for files on network or container bind mounts.
Spotted something wrong or out of date? Tell us — every page is checked against the current fail2ban release.