Compare
fail2ban vs reaction
reaction is a newer, deliberately small daemon that scans command output for repeated patterns and runs actions — fail2ban's idea with fewer moving parts. Here is how the two compare and who should consider switching.
reaction is an open-source daemon by ppom that watches the output of commands (usually journalctl -f or tail -f), counts repeated regex matches, and runs whatever commands you configure — typically a firewall ban and a delayed unban. Pick reaction if you want a lean, explicit config you write yourself and care about CPU use on log-heavy hosts. Pick fail2ban if you want 96 ready-made filters, distribution packages everywhere, a long track record and the largest pool of documentation and community answers.
reaction's author built it as a lighter replacement for fail2ban, and it shows: there is no default rule set to override, no jail.conf/jail.local layering, and every ban and unban is a command you can read in the config. The trade-off is that you write more yourself.
fail2ban vs reaction at a glance#
| Feature | fail2ban | reaction |
|---|---|---|
| Language | Python 3 | Rust (v2, a full rewrite of the earlier Go v1) |
| Detection source | Log files, systemd journal | Any command's stdout (streams), e.g. journalctl -fu sshd.service |
| Detection logic | Filter regexes with <HOST>, maxretry within findtime |
Filter regexes with named patterns (<ip>), retry within retryperiod |
| Ready-made rules | 96 filters shipped upstream | Example filters in the wiki (SSH, Nginx, Traefik, Nextcloud, Proxmox, web crawlers…) |
| Response | Action files (nftables, iptables, firewalld, ufw, pf, Cloudflare, mail…) | Commands you write, plus plugins (nftables, ipset, HTTP ban page) |
| Ban expiry | bantime, bantime.increment, recidive |
An action with after: (e.g. unban after 48h) |
| Scope | Single host | Single host |
| Config style | INI, layered .conf / .local files |
One YAML, JSON or JSONnet file |
| IPv6 | Yes | Yes (type: ip, optional ipv6mask to ban a whole /64) |
| Persistence | SQLite database, bans restored at start | Embedded database in /var/lib/reaction, pending actions replayed at start |
| Packaging | Every major distribution | Release binaries and .deb for x86-64; some distributions and a NixOS module |
| License | GPL-2.0-or-later | AGPL-3.0 |
| Maintenance | Active, 1.1.1 (Aug 2026) | Active, v2.7.0 (24 Oct 2026) |
How reaction works#
A reaction configuration has four building blocks:
- Streams: commands whose output is read line by line, such as
journalctl -n0 -fu sshd.service. - Patterns: named regexes substituted into filters. The special
type: ipmatches IPv4 and IPv6 and supportsignorelists,ignorecidrranges and masks (ipv6mask: 64). - Filters: attached to a stream; a list of regexes plus
retryandretryperiod. When the same pattern value (the IP) matchesretrytimes withinretryperiod, the filter triggers. - Actions: commands run when a filter triggers. An action with
after:runs later, which is how unbans are expressed;oneshot: truemarks alert-style actions that shouldn't replay after a restart.
start and stop command lists set up and tear down firewall chains. A minimal SSH setup in YAML, adapted from the project's example configuration:
patterns:
ip:
type: ip
ignore: [ '127.0.0.1', '::1' ]
ignorecidr: [ '192.168.1.0/24' ]
start:
- [ 'iptables', '-w', '-N', 'reaction' ]
- [ 'iptables', '-w', '-I', 'INPUT', '-j', 'reaction' ]
stop:
- [ 'iptables', '-w', '-D', 'INPUT', '-j', 'reaction' ]
- [ 'iptables', '-w', '-F', 'reaction' ]
- [ 'iptables', '-w', '-X', 'reaction' ]
streams:
ssh:
cmd: [ 'journalctl', '-n0', '-fu', 'ssh.service' ]
filters:
failedlogin:
regex:
- 'Failed password for .* from <ip>'
- 'Invalid user .* from <ip>'
retry: 3
retryperiod: 6h
actions:
ban:
cmd: [ 'iptables', '-w', '-A', 'reaction', '-s', '<ip>', '-j', 'DROP' ]
ipv4only: true
unban:
cmd: [ 'iptables', '-w', '-D', 'reaction', '-s', '<ip>', '-j', 'DROP' ]
after: 48h
ipv4only: trueA real deployment adds the matching ip6tables commands with ipv6only: true, or uses the nftables or ipset plugin instead of raw commands; the project recommends nftables or ipset over plain iptables rules for performance. The service unit's name (ssh.service vs sshd.service) depends on your distribution. Runtime commands include reaction show (current matches and pending actions), reaction flush, reaction test-regex, reaction test-filter (new in 2.7.0) and reaction test-config.
How fail2ban compares#
The equivalent fail2ban jail is shorter because the filter, the firewall action and the unban already exist:
[sshd]
enabled = true
backend = systemd
maxretry = 3
findtime = 6h
bantime = 48h
ignoreip = 127.0.0.1/8 ::1 192.168.1.0/24The upstream sshd filter covers far more message variants than two hand-written regexes (pre-auth disconnects, sshd-session, PAM failures, key-exchange garbage in aggressive mode), and the ban action handles IPv4 and IPv6 in one set. The cost is fail2ban's layered configuration model, which how fail2ban works explains.
Concept by concept:
| fail2ban | reaction |
|---|---|
| jail | filter (with an after: unban action) |
logpath / journalmatch |
stream cmd |
failregex with <HOST> |
regex with <ip> |
maxretry / findtime |
retry / retryperiod |
bantime |
after: on the unban action |
ignoreip |
pattern ignore / ignorecidr |
fail2ban-regex |
reaction test-regex / test-filter |
fail2ban-client status <jail> |
reaction show |
Performance claims#
reaction's README says it is much faster than fail2ban, and the author's blog reported lower CPU and memory use on their own server after switching. Those are the project's own measurements, not independent benchmarks, and we haven't measured it. What is structurally true: reaction is a compiled Rust binary with no interpreter, while fail2ban's cost grows with the number of log lines and regexes it evaluates. If fail2ban is using noticeable CPU on your server, try the fixes in fail2ban high CPU first — journal matching and trimming unused jails often solve it.
When reaction is the better choice#
- You want every ban and unban spelled out in one file, with no defaults hidden in distribution overrides.
- You react to things that aren't logins — restarting a service on an error, alerting on a message — since reaction is a general "pattern in output → command" engine.
- Your host processes very large log volumes and CPU matters.
- You like JSONnet and want to generate configuration with variables and functions.
When fail2ban is the better choice#
- You protect many different services and want filters that already handle their log quirks.
- You want packages from your distribution's repositories and security updates through the normal channel.
- You need integrations that already exist as fail2ban actions: Cloudflare, AbuseIPDB, firewalld rich rules, email reports.
- You'd rather rely on a project with two decades of history and a wide user base. reaction's own README notes it has not had an external security audit yet.
Running both, and migrating#
Both can run on the same host as long as they don't manage the same service — give each its own firewall chain or set. To migrate a jail, translate each failregex into a reaction regex (replace <HOST> with <ip>, and drop fail2ban's date and prefix handling, which reaction doesn't do the same way), map maxretry/findtime/bantime as in the table above, then test with reaction test-filter against real log lines before disabling the fail2ban jail. When writing regexes, the custom filter guide and the jail options reference help you understand what each fail2ban setting was doing.
FAQ#
What is reaction?#
reaction is a daemon that scans the output of commands such as journalctl -f for repeated patterns and runs configured actions, most often banning IPs in the firewall and unbanning them later. It is written in Rust, licensed under the AGPL-3.0, developed by ppom on Framagit, and configured in YAML, JSON or JSONnet.
Is reaction better than fail2ban?#
It is lighter and simpler to reason about, which suits people who want to write their own rules and care about CPU use. fail2ban has far more ready-made filters and actions, distribution packages everywhere and a much larger community. For most users protecting common services, fail2ban is less work.
Is reaction stable enough for production?#
The project considers its core mostly finished and released v2.7.0 in October 2026, after a series of 2.x releases funded in part by NLnet. Version 2.7.0 included breaking changes, so read the changelog before upgrading. The README notes that it hasn't had an external security audit.
Can I convert fail2ban filters to reaction?#
Yes, by hand. Copy each failregex, replace <HOST> with <ip>, remove anything that depends on fail2ban's prefix and date handling, and set retry, retryperiod and an after: unban to match maxretry, findtime and bantime. Test with reaction test-filter against real log lines.
Spotted something wrong or out of date? Tell us — every page is checked against the current fail2ban release.