DEV Community

Cover image for Fail2ban vs CrowdSec: Homelab IPS Battle
Sergej Voronko
Sergej Voronko

Posted on Originally published at linuxcore.dev

Fail2ban vs CrowdSec: Homelab IPS Battle

When evaluating fail2ban vs crowdsec for a homelab IPS, the choice replaces a local regex log scanner with a distributed consensus engine. While Fail2ban parses text logs and directly appends blocking rules to your firewall, CrowdSec separates the workload into a log parser, a Local API, and distinct remediation components (commonly known as CrowdSec bouncers). For a homelab administrator, the decision comes down to whether the immediate simplicity of a single Python script outweighs the performance and shared threat intelligence of a decoupled architecture.

How Fail2ban Processes Logs and Iptables Rules

Fail2ban is the default intrusion prevention system for most self-hosted servers. The service is a Python daemon that runs regular expressions against system log files. When an IP address matches a failure condition too many times within a specific findtime window, Fail2ban triggers an action. That action usually involves appending an IP block to an iptables or nftables chain on the local machine.

The implementation requires zero external dependencies. You define a filter file containing a regex, link it to a jail in jail.local, specify a ban duration, and start the service.

The limitations appear under load. Regular expression evaluation is notoriously CPU-intensive. If a homelab exposes a public application that suffers an aggressive brute-force attack or a rapid vulnerability scan, the sudden influx of log entries forces the Python daemon to constantly evaluate complex regex rules against a fast-moving stream of text. Server CPU usage frequently spikes during these events.

Relying on vanilla iptables rules to drop connections from thousands of IP addresses forces the Linux kernel to perform linear searches. Unless Fail2ban is specifically configured to use an ipset action, a large ban list severely degrades packet forwarding performance. A large list of banned IPs means every valid incoming packet must be checked against those sequential firewall rules before reaching its destination.

Fail2ban also stores its active bans in a local SQLite database located at /var/lib/fail2ban/fail2ban.sqlite3. If the dbpurgeage parameter is not tuned correctly, this database grows massive over time and slows down service restarts.

CrowdSec Architecture and Bouncer Design

CrowdSec is written in Go and fundamentally changes the intrusion prevention model. Instead of one monolithic script handling parsing, state management, and blocking, the architecture is split into discrete services.

The core agent uses YAML configurations and Grok patterns to parse logs. Grok patterns execute much faster than raw regex evaluations in Python, reducing CPU overhead during high-volume attacks.

When the agent detects malicious behavior based on defined scenarios, it does not touch the host firewall directly. Instead, it registers a decision with the Local API (LAPI), which stores the event in a local SQLite or PostgreSQL database.

Remediation components (the CrowdSec bouncers) query this LAPI. The bouncer is responsible for enforcing the ban. If you install the host firewall bouncer, it creates an ipset or nftables set and adds the malicious IP address to it for fast lookups. Because the components communicate over an HTTP API, you can run the parsing agent on an internal web server and run the bouncer on your perimeter router.

This architecture includes a community threat intelligence feature. The LAPI periodically sends anonymous data about banned IPs to a Central API. In return, your homelab receives a curated blocklist of IPs that have attacked other CrowdSec users. Your server drops packets from known malicious actors before they ever attempt a connection.

Deploying CrowdSec via Docker Compose

Deploying Fail2ban in Docker often requires elevated privileges and messy host file mounts. CrowdSec is designed with containers in mind. The agent runs as a standard unprivileged container, and you configure your reverse proxy to share its log directory.

Below is a minimal Docker Compose snippet based on the current official CrowdSec documentation. The configuration mounts the host log directory, exposes the Local API on port 8080, and installs common collections.

services:
  crowdsec:
    image: crowdsecurity/crowdsec:latest
    container_name: crowdsec
    environment:
      - COLLECTIONS=crowdsecurity/linux crowdsecurity/traefik crowdsecurity/http-cve
      - GID=1000
    volumes:
      - crowdsec-db:/var/lib/crowdsec/data/
      - crowdsec-config:/etc/crowdsec/
      - /var/log/auth.log:/var/log/auth.log:ro
      - /path/to/proxy/logs:/var/log/proxy:ro
    ports:
      - "127.0.0.1:8080:8080"
    restart: unless-stopped

volumes:
  crowdsec-db:
  crowdsec-config:
Enter fullscreen mode Exit fullscreen mode

The COLLECTIONS environment variable automatically downloads parsers and scenarios from the CrowdSec Hub. A collection is a bundled set of parsing rules and detection thresholds for a specific service. The Local API binds to 127.0.0.1 so that host-level bouncers can query it securely.

Connecting CrowdSec Bouncers to the LAPI

Running the CrowdSec agent alone only populates a database with decisions; it does not block anything. You must install a bouncer to apply the remediations.

Bouncers come in two primary categories: Infrastructure-level (Layer 3/4) and Application-level (Layer 7).
The infrastructure bouncer (crowdsec-firewall-bouncer) runs directly on the host operating system and issues kernel-level drops.
The application bouncers integrate directly into reverse proxies like Traefik, Caddy, or Nginx. Instead of dropping the packet at the kernel, a Layer 7 bouncer can return a 403 Forbidden HTTP error or serve a CAPTCHA challenge to suspicious IPs.

To connect a bouncer, you must generate an API token using the cscli command line tool inside the CrowdSec container:

docker exec crowdsec cscli bouncers add homelab-firewall
Enter fullscreen mode Exit fullscreen mode

The command outputs an alphanumeric token. You paste this token into the bouncer's configuration file and restart the bouncer service.

Homelab Gotchas: WAL Mode and Docker Networking

The distributed design introduces management complexity. Moving from Fail2ban to CrowdSec requires learning a new taxonomy of agents, scenarios, parsers, and bouncers. You will rely heavily on the cscli tool to inspect decisions and manage collections.

The SQLite database lock issue is a documented gotcha for high-traffic homelabs. By default, the CrowdSec container initializes a standard SQLite database. When multiple logs are parsed rapidly and the bouncer pulls updates simultaneously, the database can throw a "database is locked" error in the container logs. The fix requires manually adding use_wal: true to the db_config section of /etc/crowdsec/config.yaml to enable Write-Ahead Logging, followed by a container restart.

Docker networking creates another frequent issue with bouncers. If you run a bouncer in a separate Docker container on a custom bridge network, it communicates with the LAPI via a dynamic container IP address. If the bouncer container updates or restarts, Docker assigns it a new internal IP. The CrowdSec LAPI sees a new connection and leaves the old bouncer registered. Over time, stale bouncer registrations accumulate. Administrators must implement static container IPs or prune inactive bouncers periodically using the cscli bouncers prune command.

Conversely, Fail2ban requires almost no maintenance once the jail is dialed in. If you have a single virtual private server or a single home server running SSH, Fail2ban is highly effective. You install the package, enable the sshd jail, and the configuration survives system reboots and package updates without demanding attention.

When to Stick With Fail2ban vs CrowdSec

A homelab running a simple WireGuard endpoint and SSH does not require CrowdSec. Fail2ban provides immediate protection with minimal setup. The CPU penalty of regex parsing is irrelevant when your public attack surface is restricted to a single port and traffic volume is low.

Homelabs hosting multiple public-facing web applications behind a reverse proxy gain significant advantages from CrowdSec. The Go-based log parsing handles heavy traffic without spiking processor usage. The community threat intelligence preemptively drops known malicious scanners, reducing the sheer volume of traffic hitting your applications. Most importantly, the bouncers allow you to decouple detection from enforcement, parsing proxy logs safely inside an isolated container while executing the ban via the host network stack.

What's Next

FAQ

What is the main difference between Fail2ban and CrowdSec?

Fail2ban uses local regex log parsing to update iptables directly. CrowdSec uses a decoupled architecture with Go-based grok parsing and a distributed threat intelligence network.

Can Fail2ban and CrowdSec run together?

Running both on the same logs creates duplicate firewall rules and race conditions. Pick one intrusion prevention system per service.

Do CrowdSec bouncers cost money?

The core CrowdSec engine and standard bouncers are free and open-source. The project limits API pulls for enterprise volume, but standard homelab usage remains free.


Originally published at linuxcore.dev.

Top comments (0)