CrowdSec is a modern open-source IPS (Intrusion Prevention System) that works as an improved replacement for fail2ban. The key difference: all CrowdSec users share information about attacking IPs, forming a collective threat database from millions of sources.
CrowdSec vs fail2ban
| Feature | fail2ban | CrowdSec |
|---|---|---|
| Architecture | Monolithic | Agent + Bouncer |
| Threat Intelligence | Local logs only | Global shared DB |
| Proactive blocking | No | Yes (known attackers) |
| Web dashboard | No | Yes (app.crowdsec.net) |
| Scenario collections | Regex only | YAML scenarios, Hub |
| License | GPL | MIT (Community) |
Installing CrowdSec
curl -s https://packagecloud.io/install/repositories/crowdsec/crowdsec/script.deb.sh | bash
apt install crowdsec -y
# Install the Nginx Bouncer (blocker)
apt install crowdsec-nginx-bouncer -y
# Or the firewall bouncer
apt install crowdsec-firewall-bouncer-nftables -y
Installing scenario collections
cscli collections list
cscli collections install crowdsecurity/nginx
cscli collections install crowdsecurity/wordpress
cscli collections install crowdsecurity/ssh-bf
cscli collections install crowdsecurity/linux-lpe
systemctl reload crowdsec
Managing blocks
cscli decisions list
cscli decisions add --ip 1.2.3.4 --duration 24h --reason "manual ban"
cscli decisions delete --ip 1.2.3.4
cscli alerts list --limit 20
Monitoring metrics
cscli metrics
cscli bouncers list
cscli machines list
CrowdSec Hub: app.crowdsec.net offers a free dashboard with attack graphs, a map of threat sources, and management of your agents.
CrowdSec doesn't replace firewall configuration and SSH hardening — it's an additional layer. Use it together with ufw/nftables and sshd_config rules.
Common problems when deploying CrowdSec
level=fatal msg="unable to run local API: listen tcp 127.0.0.1:8080: bind: address already in use"— port 8080 is already taken by default, usually by a Node.js app, a panel, or a test PHP server. Changeapi.server.listen_uriin/etc/crowdsec/config.yamlto a free port.cscli decisions addruns without errors, yet the attacker's traffic still gets through — a decision is only a record in the LAPI database. Actual blocking is done by a bouncer, and without one installed and configured, nothing stops the traffic.cscli bouncers listshows a bouncer with an empty Last Pull column — it is registered but not pulling decisions. The cause is almost always a wrongapi_urlorapi_keyin its config, for example/etc/crowdsec/bouncers/crowdsec-nginx-bouncer.yaml.cscli metricsshows zero events for an installed scenario — the log source fromacquis.yamlis not being read: either the path is wrong, or the user crowdsec runs as lacks permission to read the log.- The nginx-bouncer fails to run on a stock distro Nginx package —
crowdsec-nginx-bouncerrequires Nginx built withngx_http_lua_module, or OpenResty; the standard apt build of Nginx does not include that module.
Bouncer types and what each one blocks
| Bouncer | Blocking level | Where it is installed | Limitation |
|---|---|---|---|
| nginx-bouncer | HTTP request (application level) | On every Nginx server | Needs the Lua module, has no effect on non-HTTP traffic |
| firewall-bouncer (nftables/iptables) | Network packet | On the whole host | Blocks any traffic, but cannot see HTTP headers or URLs |
| cloudflare-bouncer | CDN edge | Inside a Cloudflare account | Only works if all traffic already goes through Cloudflare |
| Custom bouncer via API | Whatever you implement | Your own script calling the LAPI | Requires you to build and maintain it |
When CrowdSec is not the right fit
- A single low-traffic server where fail2ban already covers the need — CrowdSec adds an agent, LAPI, and a bouncer, meaning more moving parts and memory for not much extra gain.
- An isolated network with no internet access — sharing the collective attacker database with CrowdSec's CAPI requires reaching an external service; in a closed environment, only local scenario analysis remains, with no network effect.
- A server with very limited memory (512 MB to 1 GB) — the CrowdSec agent, its local decision database, and a bouncer together are heavier on resources than fail2ban.
- You need blocking based on complex post-login behavioral signals (device fingerprint, session anomalies) — that is an application-level or dedicated anti-fraud task, not an IPS at CrowdSec's level.
Verifying that blocking actually works
# Add a short-lived test block for YOUR OWN external IP
cscli decisions add --ip 1.2.3.4 --duration 1m --reason "test block"
# Confirm the decision landed in the LAPI database
cscli decisions list
# From the blocked IP, check that the connection is really refused
curl -I https://example.com
# Remove the test block before it expires on its own
cscli decisions delete --ip 1.2.3.4
Replace 1.2.3.4 with your own external address and run the check from that machine, not from the server itself — otherwise the third command only proves the server works locally and says nothing about whether the bouncer actually blocks someone else's traffic.