Skip to content
Go back

CrowdSec Collections & Bouncers: fail2ban for 2026

By SumGuy 9 min read
CrowdSec Collections & Bouncers: fail2ban for 2026

Your Server Is Smarter Than fail2ban

fail2ban is one of those tools every self-hoster installs once, forgets about, and assumes is handling things. And honestly? It does handle things — badly scanning SSH brute-forcers who hammer port 22 from a single IP, sure. But here’s the problem: fail2ban is completely blind. It only knows about threats that have already hit your logs. Some IP scanning servers in Vietnam right now? fail2ban has no idea. It’ll learn, eventually, when that scanner reaches you. Probably at 2 AM.

CrowdSec changes the model. Instead of every server learning independently from its own log wounds, CrowdSec shares threat intelligence across its entire user base. When a scanner hits someone else’s Nginx instance in Germany, your server knows about it within minutes. You’re not defending alone anymore.

Here’s what that means in practice: a fresh CrowdSec install blocks millions of IPs before a single packet hits your app. Not because you configured it perfectly — because 400,000 other CrowdSec instances already did the work for you.


How CrowdSec Actually Works

Three moving parts:

Agent — Reads your logs, runs them through scenario rules (called collections), and makes ban decisions locally. Runs as a service on your host.

Collections — Curated sets of parsers + scenarios. crowdsecurity/nginx teaches the agent what an Nginx log looks like and what a brute-force pattern looks like in that format. There are collections for sshd, traefik, caddy, http-cve exploits, WordPress, and more. You install the ones that match your stack.

Bouncers — The enforcement layer. The agent makes a decision (ban this IP). A bouncer acts on that decision at whatever layer you choose: iptables/nftables, Caddy middleware, Traefik middleware, Cloudflare WAF, HAProxy, etc. The agent and bouncer are deliberately separate — you can ban at the firewall level or at the application level, depending on what makes sense.

The central API (LAPI) glues it together locally and also connects to CrowdSec’s cloud-based blocklist. The community blocklist is free and updated in near-real-time.


Installing the Agent

The official repos make this straightforward on Debian/Ubuntu:

Terminal window
curl -s https://packagecloud.io/install/repositories/crowdsec/crowdsec/script.deb.sh | sudo bash
sudo apt install crowdsec

For RPM-based distros:

Terminal window
curl -s https://packagecloud.io/install/repositories/crowdsec/crowdsec/script.rpm.sh | sudo bash
sudo dnf install crowdsec

After install, the agent starts automatically and reads /etc/crowdsec/acquis.yaml to know which log files to watch.

Check that it’s running:

Terminal window
sudo systemctl status crowdsec
sudo cscli version

Picking Your Collections

Collections are installed with cscli. List what’s already on your system and what’s available:

Terminal window
sudo cscli collections list
sudo cscli hub list -t collections

For a typical self-hosted stack running public web services, start here:

Terminal window
sudo cscli collections install crowdsecurity/nginx
sudo cscli collections install crowdsecurity/sshd
sudo cscli collections install crowdsecurity/http-cve
sudo cscli collections install crowdsecurity/traefik # if you use traefik
sudo cscli collections install crowdsecurity/caddy # if you use caddy

crowdsecurity/http-cve is the one you don’t want to skip. It watches for known CVE exploitation attempts — WordPress plugin scanners, Log4Shell probes, path traversal, shellshock — and blocks them regardless of which web server is fronting your app.

After installing collections, reload:

Terminal window
sudo systemctl reload crowdsec

Telling CrowdSec Where Your Logs Are

The acquis.yaml file tells the agent what to read. The default covers syslog and journald, but you’ll want to add your specific log paths:

acquis.yaml
# /etc/crowdsec/acquis.yaml
# SSH via journald (works without a log file)
- source: journald
journalctl_filter:
- "_SYSTEMD_UNIT=ssh.service"
labels:
type: syslog
# Nginx access logs
- filenames:
- /var/log/nginx/access.log
- /var/log/nginx/error.log
labels:
type: nginx
# Traefik via Docker
- source: docker
container_name:
- traefik
labels:
type: traefik
# Caddy (if logging to file)
- filenames:
- /var/log/caddy/access.log
labels:
type: caddy

Reload after editing:

Terminal window
sudo systemctl reload crowdsec

Verify the agent is parsing logs correctly:

Terminal window
sudo cscli metrics

You’ll see lines parsed, events triggered, and decisions made. If the parsed count is zero, your log path is wrong or the label type doesn’t match the installed collection.


Docker Setup

Running everything in Docker? You can run the CrowdSec agent in a container alongside your stack:

docker-compose.yml
services:
crowdsec:
image: crowdsecurity/crowdsec:latest
container_name: crowdsec
restart: unless-stopped
environment:
- COLLECTIONS=crowdsecurity/traefik crowdsecurity/http-cve crowdsecurity/sshd
- GID=${GID:-1000}
volumes:
- ./crowdsec/config:/etc/crowdsec
- ./crowdsec/data:/var/lib/crowdsec/data
# Mount log files or Docker socket for log acquisition
- /var/log:/var/log:ro
- /var/run/docker.sock:/var/run/docker.sock:ro
networks:
- proxy
traefik:
image: traefik:v3.0
container_name: traefik
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./traefik/config:/etc/traefik
- ./traefik/logs:/logs
networks:
- proxy
# CrowdSec bouncer as Traefik plugin (traefik-bouncer)
bouncer-traefik:
image: fbonalair/traefik-crowdsec-bouncer:latest
container_name: bouncer-traefik
restart: unless-stopped
environment:
- CROWDSEC_BOUNCER_API_KEY=${CROWDSEC_BOUNCER_API_KEY}
- CROWDSEC_AGENT_HOST=crowdsec:8080
networks:
- proxy
networks:
proxy:
external: true

The COLLECTIONS env var tells the container to install and enable those collections on first boot. You still need acquis.yaml to tell it where the logs live — mount a config directory and drop the file in there.


Setting Up a Bouncer

The agent bans IPs — bouncers enforce those bans. Pick the right bouncer for your layer.

iptables/nftables Bouncer (host-level)

Best for bare-metal or VM setups where you want bans at the network layer:

Terminal window
sudo apt install crowdsec-firewall-bouncer-iptables

It reads decisions from the local API and inserts/removes iptables rules automatically. Check its status:

Terminal window
sudo systemctl status crowdsec-firewall-bouncer

Traefik Bouncer (middleware)

For Docker-based setups with Traefik, use the Traefik plugin approach. First, generate a bouncer API key:

Terminal window
sudo cscli bouncers add traefik-bouncer
# Outputs: API key — copy this, you won't see it again

Add the bouncer container (as in the compose above) and configure Traefik to use it as a forward-auth middleware:

traefik/config/dynamic.yml
http:
middlewares:
crowdsec-bouncer:
forwardAuth:
address: "http://bouncer-traefik:8080/api/v1/forwardAuth"
trustForwardHeader: true
routers:
my-service:
rule: "Host(`app.example.com`)"
middlewares:
- crowdsec-bouncer
service: my-service

Every request goes through the bouncer container, which checks CrowdSec’s decision list and returns 403 for banned IPs before Traefik ever forwards the request.

Caddy Bouncer

CrowdSec has a native Caddy module. Add it via xcaddy:

Terminal window
xcaddy build --with github.com/hslatman/caddy-crowdsec-bouncer/http

Then in your Caddyfile:

{
crowdsec {
api_url http://localhost:8080
api_key YOUR_BOUNCER_API_KEY
ticker_interval 15s
}
}
app.example.com {
crowdsec
reverse_proxy localhost:3000
}

The crowdsec directive tells Caddy to check every request against the decision list.


Reading the cscli Output

Once things are running, cscli is your main diagnostic tool.

Check current bans:

Terminal window
sudo cscli decisions list
+--------+----------+---------+------------------------------------+--------+
| ID | SOURCE | SCOPE:VALUE | REASON | ACTION|
+--------+----------+---------+------------------------------------+--------+
| 123456 | crowdsec | Ip:185.220.x.x | crowdsecurity/ssh-slow-brute | ban |
| 123457 | CAPI | Ip:91.108.x.x | community-blocklist | ban |
+--------+----------+---------+------------------------------------+--------+

The SOURCE: CAPI rows are bans from the community blocklist — IPs that other CrowdSec instances flagged, pushed to the central API, and distributed to you. You didn’t have to see that traffic to block it.

Check alerts (triggered scenarios):

Terminal window
sudo cscli alerts list

Check which parsers and scenarios are active:

Terminal window
sudo cscli parsers list
sudo cscli scenarios list

Manually ban an IP (useful for testing your bouncer):

Terminal window
sudo cscli decisions add --ip 1.2.3.4 --duration 1h --reason "testing bouncer"
sudo cscli decisions delete --ip 1.2.3.4

Check the community blocklist subscription:

Terminal window
sudo cscli capi status

If you see You can successfully interact with Central API you’re pulling the community blocklist. If not, check your internet connectivity and that the agent can reach api.crowdsec.net.


Enrolling with CrowdSec Console (Optional but Useful)

CrowdSec has a free web console at app.crowdsec.net that gives you dashboards, blocklist management, and the ability to add premium blocklists (threat actors by category, geography, etc.).

Enroll your instance:

Terminal window
sudo cscli console enroll YOUR_ENROLLMENT_KEY
sudo systemctl reload crowdsec

Get the key from app.crowdsec.net → Instances → Add. Once enrolled you get a nice dashboard showing decisions, alerts, and community contribution stats. The “contributing” status matters — the more CrowdSec instances share data, the better everyone’s blocklist gets.


Tuning: Whitelists and False Positives

CrowdSec will occasionally ban Googlebot, your monitoring system, or your own IP. Whitelist with:

Terminal window
sudo cscli decisions delete --ip 203.0.113.5 # one-off unban

For permanent whitelists, add to /etc/crowdsec/parsers/s02-enrich/whitelists.yaml:

whitelists.yaml
name: custom/whitelists
description: "My trusted IPs"
whitelist:
reason: "Internal and monitoring IPs"
ip:
- "192.168.1.0/24"
- "10.0.0.0/8"
cidr:
- "203.0.113.0/24"

Reload and the agent won’t trigger decisions against those ranges.


fail2ban vs CrowdSec: When to Use Which

Switch to CrowdSec when:

Stick with fail2ban when:

Honestly, for anything running public web services in 2026, CrowdSec is the correct choice. fail2ban is fine for what it is — a local log watcher with ban rules. CrowdSec is that plus a network-wide immune system. The install takes 15 minutes. The community blocklist is immediately useful. The bouncer integration with Traefik or Caddy is clean.

Your 2 AM self, staring at 40,000 blocked requests from a botnet you never had to configure rules for, will appreciate it.


Share this post on:

Send a Webmention

Written about this post on your own site? Send a webmention and it'll show up above once verified.


Previous Post
Blog Comments: Self-Host or SaaS?
Next Post
mergerfs + SnapRAID: The Poor Man's Unraid

Discussion

Powered by Garrul . Sign in with GitHub or Google, or post anonymously.

Related Posts