A fail2ban replacement for Linux

Single binary.
Durable protection.

Predictable behavior under hostile input.

Reads your logs, detects repeated failures, and bans offending IPs. A familiar job, with a smaller runtime and explicit resource limits.

sshd / ban eventExample
/var/log/auth.log3 matches
  1. 12:04:01
    Failed password for root
    from 203.0.113.7
  2. 12:04:03
    Failed password for root
    from 203.0.113.7
  3. 12:04:05
    Failed password for root
    from 203.0.113.7
Address banned203.0.113.7 for 1 hour
3 / 3
Confirmed protection · examplesshd · 203.0.113.7
1h ban · kernel readback confirmed
Daemon + administration
1 binary
Native persistence
SQLite
Built-in service filters
15
Linux release targets
5
Latest · v0.4.8

One shared pool of 65,536 tracked addresses, configured ban lists with a visible origin on every ban, and a stop that leaves installed rules in the kernel.

Read the v0.4.8 changes

Design

Familiar operation.
Fewer moving parts.

The same log-to-ban model, with explicit boundaries around the work your server does.

A smaller runtime to maintain

One static executable combines the daemon, administration and migration tools. No Python runtime or separate database server is required.

Static musl build

Durable state with explicit limits

Source progress, retry state and protection ownership survive restart in SQLite. Bounded admission retains critical state when resources run short.

Memory model

Parsing without a regex engine

Built-in filters compile into specialised parsers. A single pass extracts the address, with bounds checks and no allocations in the hot path.

Parser architecture

Enforcement through the kernel

Typed intent persists before dispatch and is confirmed by kernel readback. Choose nftables via netlink, or iptables and ipset.

Netlink integration

Migration

Bring your existing jails.

Import your fail2ban configuration, review the result, then switch when you’re ready.

  1. Get the binary

    Use the latest v0.4.8 release for a new install or upgrade. v0.4.7 is the minimum supported release and the only upgrade source; a v0.4.6 state upgrades through v0.4.7 first, and earlier versions are retired. Follow the installation and upgrade instructions.

    Download v0.4.8
  2. Import and review

    Inspect supported jails and project configuration into TOML. Resolve unsupported filters and actions before enabling protection.

  3. Verify, then switch

    Compare decisions in log-only mode before changing enforcement. The guide covers cutover and rollback.

    Follow the migration guide
Inspect configuration

# After installing fail2zig

sudo fail2zig migrate inspect --source-dir /etc/fail2ban

# Review scope and blockers before planning cutover

What doesn’t transfer

Regexes and shell actions need alternatives.
Schema-4 migration carries owners and history—not partial matches.

Check compatibility
Linux builds

x86_64, ARM64, ARMv7, MIPS & MIPSel. Selected live checks cover Debian 13 x86_64; other targets have cross-build, static-inspection and QEMU smoke evidence.

Verify a build

Release qualification

Evidence. With the limits in view.

v0.4.8 shares one tracking pool across all jails and confirms nftables installs by the kernel’s acknowledgement. v0.4.7 brought idle CPU at 2,000 bans under 3 % of one vCPU. Earlier releases added storage recovery, the qualified PortSentry recipe and read-only firewall visibility.

Selected live qualification

Debian 13x86_64

File and journal input, all three firewall backends, service permissions and storage recovery. Other architectures have static-build and QEMU smoke evidence.

Read the qualification scope

Protection through restart

SQLiteembedded

Durable checkpoints, retries, owners and confirmed history. Resource exhaustion pauses admission instead of silently discarding critical protection state.

Understand the resource limits

Qualification boundarySelected live checks cover Debian 13 x86_64, including file and journal input and all three firewall backends. The PortSentry recipe has separate IPv4/IPv6 connect and stealth checks. Capacity figures are measurements on one lab host (8 vCPU, 16 GiB): a cold restart with 65,536 installed bans reached readiness in 373 s and idled at 12 % of one core. The other release targets have cross-build, static-inspection and QEMU command evidence. See the qualification guide for the full scope.

Comparison

Different tools, different tradeoffs.

Compare runtime, deployment, and firewall support. Your existing configuration is part of that decision.

Scroll horizontally to compare all columns.

Intrusion prevention tools: language, deployment, and firewall backends
Tool Language Deployment Firewall backends
fail2zig Zig Static musl binary nftables / iptables / ipset
fail2ban Python Package + runtime iptables / nftables
SSHGuard C Daemon + firewall backend pf / iptables / nftables
CrowdSec Go Engine + remediation component iptables / nftables

Selected deployment characteristics. See fail2ban, SSHGuard and CrowdSec for full support details.

FAQ

Before you switch.

Is this a drop-in replacement?

No. Inspect supported configuration and action scope before switching. The schema-4 migration workflow can transfer admitted owners, deadlines and history, but not partial matches or journal cursors. Arbitrary fail2ban regexes and actions are not executed.

Read the compatibility details
Can hostile log lines crash or exhaust the daemon?

That’s the threat model we designed to. Parsing is bounds-checked, log lines have a bounded input contract, and native processing has explicit resource limits. Storage failures pause affected ingestion while retaining protection. These controls reduce risk; the threat model documents the remaining limits.

Read the threat model
Why Zig? Why not Rust?

Zig gives us explicit allocator control, comptime code generation for built-in filters, and straightforward cross-compilation to musl targets. Rust would work. Zig fits the problem.

Does it run in minimal containers?

Static linkage avoids shared runtime libraries, but deployment still needs readable logs, writable persistent state and the selected backend’s capabilities. Journal input needs journalctl; iptables/ipset need host tools. Effects stay in the current network namespace; custom namespace selectors are unsupported.

Is this a SIEM or a WAF?

Neither. It reads logs and acts on repeated failures. It does not aggregate security events across hosts, correlate incidents, or inspect HTTP bodies.

Open source · AGPL-3.0

Take a closer look.

Explore the features, find your services, or plan your migration.