Tech Digest

Security

CrowdSec

Log-parsing intrusion prevention with local decisions and an opt-in community blocklist

SecurityMITStablearm64 builds
Last reviewed Profile maintained against the project's own documentation
Operational load
2 / 5
Light
Idle memory
120 MB
typical use ~250 MB
Moving parts
2
containers in a normal deployment
Backup shape
SQLite backup
needs a dump or a stop

CrowdSec parses your logs, matches attack patterns using scenarios from a shared hub, and hands decisions to separate enforcement components called bouncers. It replaces Fail2ban for people who want a curated, maintained ruleset instead of hand-written regexes.

Our verdict on CrowdSec#

The thing to understand about CrowdSec is that detection and enforcement are separate processes. The Security Engine reads logs, matches hub scenarios and writes decisions into a local SQLite database behind a Local API on 127.0.0.1:8080. Nothing is blocked until you install a bouncer, which is a different package: firewall, nginx, OpenResty, Traefik, HAProxy, Cloudflare, Apache, or the blocklist mirror. People install the engine, watch alerts scroll past in cscli alerts list, and assume they are protected. They are not. The shared-signal half is narrower than its reputation suggests: a signal pushed to the Central API carries the offending IP with geolocation, the scenario name and version that fired, the timestamp, your machine ID and the list of scenarios you run. Custom and tainted scenarios and manual decisions stay local unless you enroll in the Console, and share_context is off by default. In return you get the community blocklist. Default ban duration is four hours.

Who CrowdSec is for#

Choose it if

  • Anyone tired of writing Fail2ban regexes: cscli collections install crowdsecurity/nginx covers what an afternoon of hand-tuning would.
  • A public-facing web server where the community blocklist drops known scanners before they reach your application at all.
  • Traefik and Caddy users, who get layer 7 enforcement through a plugin without touching iptables or nftables.
  • Operators who want to see what was blocked and why in a readable form, rather than reading a firewall chain dump.

Look elsewhere if

  • You are unwilling to send any signal off the box. You can run it fully local by removing the online API credentials, but you lose the community blocklist and most of the reason to choose it.
  • You want zero moving parts. Fail2ban is one daemon; CrowdSec is an engine plus at least one separately installed and separately credentialed bouncer.
  • You need the premium blocklists, the Live Exploit Tracker or Console features beyond the free tier. Those are commercial products with commercial pricing.

What running it actually looks like#

Two moving parts on one host. The Security Engine installs from CrowdSec's own package repository, writes configuration to /etc/crowdsec/ and SQLite to /var/lib/crowdsec/data/crowdsec.db, and binds its Local API to 127.0.0.1:8080 and Prometheus to 127.0.0.1:6060. You declare log sources in /etc/crowdsec/acquis.yaml or acquis.d/, install collections from the hub with cscli collections install, and reload. Then, separately, install a bouncer: cscli bouncers add mybouncer prints an API key that goes into the bouncer's own config file. In Docker you mount the logs you want read plus a volume for /var/lib/crowdsec/data, and remember the LAPI sees container addresses. Engine upgrades are package upgrades; hub content moves independently through cscli hub update && cscli hub upgrade.

Resource profile#

Memory
120 MB idle, around 250 MB in ordinary use. Estimated from small single-host installs, not a documented minimum. The Go engine plus a SQLite decisions database; memory scales with the number of parsers and acquisition sources rather than with traffic.
CPU and acceleration
Parsing is the cost, and the default `parser_routines: 1` means one goroutine chewing through your access logs. A busy nginx log on a small vCPU is the usual bottleneck; the AppSec/WAF component costs considerably more than log parsing alone.
Storage growth
`/var/lib/crowdsec/data/crowdsec.db` holds alerts and decisions and is flushed at 5,000 items or 7 days by default, so it stays in the tens of megabytes. The community blocklist lives in that database and in memory.
Operational load
2 of 5, Light. Install a collection, point it at logs, install one bouncer, and it largely runs itself with package upgrades and the occasional whitelist. It is a 2 rather than a 1 because detection and enforcement are separate processes that can silently drift apart, and nothing tells you when they have.

Figures describe a small single-household install and are the working assumptions behind our stack planner. Your numbers will differ with library size, user count and hardware. See methodology.

Data and backup surface#

Back up /etc/crowdsec/ in full: config.yaml, acquis.yaml, profiles.yaml, console.yaml, the notification configs, and critically local_api_credentials.yaml and online_api_credentials.yaml, which are this machine's registered identity with the Local API and the Central API. Lose those and you re-enroll the engine and every bouncer. /var/lib/crowdsec/data/crowdsec.db holds alerts and current decisions; keep it if convenient, but it is not precious, because bans expire on their own and the community blocklist refetches on start. Hub content under /etc/crowdsec/hub/ is fully regenerable with cscli hub update. A live file copy of the SQLite database is acceptable given how short-lived the data is.

Derived backup shape

SQLite backup. sqlite3 <db> ".backup out.db" while running, or stop the container and copy. A live cp of a WAL-mode SQLite file can restore corrupt.

Traps and surprises#

The engine alone blocks nothing

Installing CrowdSec gives you detection and a decisions database, not enforcement. Until you install and register a bouncer, cscli decisions list fills with bans that no packet ever sees. Verify end to end rather than trusting the alert feed: run cscli decisions add --ip 1.2.3.4 --duration 1m, then confirm the bouncer actually installed a firewall rule or returned a 403. Check it again after any upgrade, because a bouncer whose API key was rotated fails quietly.

You will ban yourself

trusted_ips defaults to 127.0.0.1 and ::1 only, and that setting governs admin API access, not exemption from bans. A few fat-fingered SSH logins or a monitoring script hitting a rate-limited endpoint and your own address is in the decisions table. Add your management addresses to the whitelist parser before you enable anything, keep an out-of-band console, and know that cscli decisions delete --ip <you> is the recovery command.

Behind a proxy, CrowdSec bans your proxy

If your web server logs the reverse proxy's or CDN's address instead of the real client, the scenarios fire against that one address and the bouncer blackholes it. The result is a total outage that looks like a proxy failure. Configure set_real_ip_from or the equivalent so the log line carries the true client address, and whitelist the proxy's own address explicitly as belt and braces.

What actually leaves the box is specific and worth reading

Signals to the Central API contain the attacker IP plus geolocation, the scenario name and its hash or version, the decision timestamp, your machine ID and the list of scenarios you have enabled. Only unmodified hub scenarios are pushed: console.yaml ships with share_manual_decisions: false, share_custom: true, share_tainted: true and share_context: false. If any of that is unacceptable, change it before enrollment, not after.

Bouncer API keys are shown once

cscli bouncers add <name> prints the API key and never shows it again; cscli bouncers list gives you names and last-seen times, not credentials. If you lose the key, the fix is to delete the bouncer and add it again under a new name, then update the bouncer's config. Store the key with your other secrets when you generate it, and remember that local_api_credentials.yaml is the engine's own equivalent.

Specifications#

Category
Security and update hygiene
Licence
MIT (Permissive)
Written in
Go
First release
2020
Maturity
Stable
Datastore
SQLite by default (MySQL and PostgreSQL supported)
Default ports
8080, 6060
Ships as
deb/rpm package, docker, native binary
arm64 builds
Yes
Identity
Not applicable. The engine has no user accounts: `cscli` is local and bouncers authenticate with generated API keys. The optional Console is a hosted service with its own login.
Replaces
Cloudflare rate limiting, AWS WAF managed rules, Imunify360
Project site
www.crowdsec.net
Source
github.com/crowdsecurity/crowdsec
Documentation
docs.crowdsec.net

Alternatives to CrowdSec#

Everything else in security and update hygiene, closest in operational weight first.

ToolOps loadIdle RAMLicence
Fail2ban2, Light40 MBGPL-2.0-or-later
Watchtower2, Light15 MBApache-2.0
Diun1, Set and forget25 MBMIT
Wazuh5, Project-grade6144 MBGPL-2.0

Sources#

Facts on this page were checked against the project's own material. Where a number is our own estimate rather than a published figure, the text says so.

  1. Default config.yaml with LAPI and Prometheus ports and SQLite path
  2. Central API documentation, what is sent and received
  3. Remediation components (bouncers) overview
  4. CrowdSec v1.8.0 release notes
  5. CrowdSec pricing and commercial tiers

Published . Last reviewed . Found something out of date? Tell us and we will fix it and log the change.