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.

Updated 7 min readChecked against fail2ban 1.1.1

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: ip matches IPv4 and IPv6 and supports ignore lists, ignorecidr ranges and masks (ipv6mask: 64).
  • Filters: attached to a stream; a list of regexes plus retry and retryperiod. When the same pattern value (the IP) matches retry times within retryperiod, the filter triggers.
  • Actions: commands run when a filter triggers. An action with after: runs later, which is how unbans are expressed; oneshot: true marks 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:

/etc/reaction.yml
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: true

A 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:

/etc/fail2ban/jail.local
[sshd]
enabled  = true
backend  = systemd
maxretry = 3
findtime = 6h
bantime  = 48h
ignoreip = 127.0.0.1/8 ::1 192.168.1.0/24

The 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.