fail2ban Not Matching Journal Entries from Unprivileged Users
When configuring fail2ban with the systemd journal backend, you may find that entries logged by services running as non-root users are silently ignored—even when journalctl shows them clearly and your regex matches when tested against a log file. I spent 3 hours on this before finding journalflags.
The Symptom
Your filter works when tested against a file:
journalctl SYSLOG_IDENTIFIER=myapp -n 10 > /tmp/test.log
fail2ban-regex /tmp/test.log /etc/fail2ban/filter.d/myapp.conf
# Failregex: 7 total
But fails when tested against the journal directly:
fail2ban-regex systemd-journal /etc/fail2ban/filter.d/myapp.conf
# Failregex: 0 total
And the live jail shows zero matches despite new log entries appearing.
The Cause
By default, fail2ban’s systemd backend only reads journal entries from system services (effectively those running as root or with _UID=0). Entries from unprivileged users—including web servers like Apache running as www-data, PHP-FPM pools, or any service with User= in its unit file—are filtered out.
You can verify this by comparing the _UID field in your journal entries:
journalctl SYSLOG_IDENTIFIER=myapp -o verbose -n 2
If you see _UID=1002 (or any non-zero UID) on the entries that aren’t being matched, this is your problem.
The Solution
Add journalflags=1 to the backend specification in your jail configuration:
[myjail]
enabled = true
backend = systemd[journalflags=1]
filter = myfilter
maxretry = 3
bantime = 10m
This flag tells fail2ban to include all journal entries, not just those from system-level processes.
Testing the Fix
To verify the fix works before reloading the jail:
sudo fail2ban-regex 'systemd-journal[journalflags=1]' /etc/fail2ban/filter.d/myfilter.conf
Note the quotes around the systemd-journal argument—the shell will interpret the brackets otherwise.
Then reload your jail:
sudo fail2ban-client reload myjail
Complete Example
A working jail for a custom syslog-based filter where the application runs as an unprivileged user:
# /etc/fail2ban/jail.d/myapp.conf
[myapp]
enabled = true
backend = systemd[journalflags=1]
filter = myapp
maxretry = 3
findtime = 10m
bantime = 1h
action = iptables-multiport[name=myapp, port="http,https"]
# /etc/fail2ban/filter.d/myapp.conf
[Definition]
journalmatch = SYSLOG_IDENTIFIER=myapp SYSLOG_FACILITY=16
failregex = Suspicious access from: <HOST>
ignoreregex =
Why This Happens
The systemd journal Python bindings accept flags that control which journal files are opened. Without journalflags=1, fail2ban uses a default that restricts access to system journals only. This is arguably a reasonable security default, but it’s poorly documented and causes silent failures that are difficult to debug.
References
- fail2ban issue #2653 — php-fpm unprivileged user
- fail2ban issue #2696 — services running as unprivileged users
- fail2ban discussion #3944 — podman/user journals