CrowdSec with Docker: block bad IPs at Traefik (Linux server, Windows and Mac)
Before you start
- 20 min read
Boxes marked Run in terminal are what you type; plain text explains why. Check what you need first

What you get in one minute#
CrowdSec is a free, open-source intrusion prevention engine: it reads your server's logs, spots attackers (scanners, brute force, known exploits) and gets a "bouncer" to block their IP addresses, while sharing and receiving a community blocklist.

Logo: the official CrowdSec project (github.com/crowdsecurity/crowdsec)
It suits a home lab or small server where a reverse proxy faces the internet and bots probe it all day. Think of fail2ban with a plug-in blocker and a shared threat list. In this guide CrowdSec reads the Traefik access log and a Traefik plugin refuses the banned addresses with HTTP 403.
| What | Value (official docs, read October 2026) |
|---|---|
| Current version | CrowdSec v1.8.2, released 26 October 2026 (latest stable) |
| Images to pin | crowdsecurity/crowdsec:v1.8.2, traefik:v3.7.17, Traefik plugin v1.8.1 |
| Time needed | About 20 minutes; the first CrowdSec start took about 2 minutes in our test |
| Size and RAM | Docs: 100 MB free RAM, 1 GB disk. Measured idle: CrowdSec 38 MiB, Traefik 43 MiB |
| Ports | 8080/tcp (CrowdSec API, kept inside Docker), 6060/tcp (metrics, not used); Traefik 80 and 443 |
| Licence | CrowdSec: MIT. Traefik plugin: Apache-2.0 (community project, not made by CrowdSec) |
| Official docs | docs.crowdsec.net and the repository |
At a glance:
- Agent vs bouncer: the agent (Security Engine) only detects and decides. A bouncer (the docs now say "remediation component") enforces. With no bouncer, nothing is blocked.
- Right logs: CrowdSec reads a file. Traefik writes its access log to a shared folder; the
crowdsecurity/traefikcollection parses it. - Community: you can receive a blocklist of known bad IPs and send back signals. What leaves your server is listed below.
- Platforms: the server should be Linux. Windows and Mac run the same Docker stack for practice only; the docs do not build the engine for macOS.
- Test it: you will ban one address on purpose and watch the 403 appear, then remove the ban.
How it fits together#
Visitors hit Traefik, which asks CrowdSec's decision list. CrowdSec builds that list by reading the Traefik log, and can share what it sees with the CrowdSec cloud.
Drawn for this guide from the official docs and the plugin's example stack. The dashed sharing path is optional.
Before you start#
You need a Linux server with Docker and Compose, and Traefik in front of the apps you want to protect (this guide includes a small one so you can follow along).
Linux server: Docker Engine and Compose, any amd64, arm64 or armhf machine, curl and openssl. Check:
docker --version docker compose version
Windows: Docker Desktop. The docs also offer a native Windows install (MSI or choco install crowdsec); this guide does not cover it.
docker --version docker compose version
Mac: Docker Desktop. The docs say CrowdSec is not compiled for macOS, so a container is the only route.
docker --version docker compose version
Checklist:
- [ ] Docker and Compose installed (Docker on Ubuntu guide if you need it)
- [ ] Ports 80 free on the host (and 443 for the HTTPS section)
- [ ] Your current config backed up if you already run Traefik
- [ ] For the HTTPS section only: a domain with a DNS record pointing to the server, and ports 80 and 443 reachable
Tested by DIY Lab Hub on 2026-10-31: Linux = not run, Windows = the stack ran on Docker Desktop (Windows 11), Mac = not run. The Linux and Mac steps follow the official docs.
Step 1: Create the folders, the network and the bouncer key#
The bouncer (Traefik plugin) and CrowdSec share one secret key. We generate it once and keep it in a file, not in a label.
Linux#
mkdir -p ~/docker_volumes/crowdsec/{config,data,logs/traefik,secrets,traefik,backups}
cd ~/docker_volumes/crowdsec
docker network inspect proxy >/dev/null 2>&1 || docker network create proxy
printf '%s' "$(openssl rand -hex 32)" > secrets/bouncer_key_traefik
chmod 600 secrets/bouncer_key_traefik
Windows (PowerShell)#
$base = "$HOME\docker_volumes\crowdsec"
"config","data","logs\traefik","secrets","traefik","backups" | ForEach-Object { New-Item -ItemType Directory -Force "$base\$_" | Out-Null }
Set-Location $base
docker network inspect proxy *> $null
if ($LASTEXITCODE -ne 0) { docker network create proxy }
$bytes = New-Object byte[] 32
[System.Security.Cryptography.RandomNumberGenerator]::Create().GetBytes($bytes)
Set-Content -Path "$base\secrets\bouncer_key_traefik" -Value (-join ($bytes | ForEach-Object { $_.ToString('x2') })) -NoNewline
Mac (Terminal)#
Same commands as Linux.
Check it worked. The key file holds 64 characters and no newline:
wc -c secrets/bouncer_key_traefik
(Get-Item secrets\bouncer_key_traefik).Length
Both should print 64. In our Windows test the PowerShell snippet produced a 64-character file.
Tip: Never paste the key into a chat or a screenshot. Anyone with it can read your ban list.
Step 2: Write the files and start the stack#
Save these three files in ~/docker_volumes/crowdsec (Windows: $HOME\docker_volumes\crowdsec). Use plain text, UTF-8, no Word. The Compose file follows the plugin project's own example stack with three changes: pinned tags, the key read from a secret file, and the CrowdSec API not published to the host.
docker-compose.yml
services:
traefik:
image: traefik:v3.7.17
container_name: traefik
restart: unless-stopped
command:
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--providers.docker.network=proxy"
- "--providers.file.directory=/etc/traefik/dynamic"
- "--entrypoints.web.address=:80"
- "--accesslog=true"
- "--accesslog.filepath=/var/log/traefik/access.log"
- "--experimental.plugins.bouncer.modulename=github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin"
- "--experimental.plugins.bouncer.version=v1.8.1"
ports:
- "80:80"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./traefik:/etc/traefik/dynamic:ro
- ./logs/traefik:/var/log/traefik
secrets:
- bouncer_key_traefik
depends_on:
- crowdsec
networks:
- proxy
crowdsec:
image: crowdsecurity/crowdsec:v1.8.2
container_name: crowdsec
restart: unless-stopped
environment:
COLLECTIONS: "crowdsecurity/traefik"
TZ: "UTC"
secrets:
- bouncer_key_traefik
volumes:
- ./config:/etc/crowdsec
- ./data:/var/lib/crowdsec/data
- ./acquis.yaml:/etc/crowdsec/acquis.d/traefik.yaml:ro
- ./logs/traefik:/var/log/traefik:ro
networks:
- proxy
whoami:
image: traefik/whoami:v1.11.0
container_name: whoami
restart: unless-stopped
labels:
- "traefik.enable=true"
- "traefik.http.routers.whoami.rule=PathPrefix(`/`)"
- "traefik.http.routers.whoami.entrypoints=web"
- "traefik.http.routers.whoami.middlewares=crowdsec@file"
- "traefik.http.services.whoami.loadbalancer.server.port=80"
networks:
- proxy
secrets:
bouncer_key_traefik:
file: ./secrets/bouncer_key_traefik
networks:
proxy:
external: true
acquis.yaml tells CrowdSec which file to read and which parser to use (the same as in the plugin project's example):
filenames: - /var/log/traefik/access.log labels: type: traefik
traefik/crowdsec.yml defines the bouncer middleware. stream mode is the plugin's recommended mode: it keeps only banned IPs in memory and refreshes them every 60 seconds.
http:
middlewares:
crowdsec:
plugin:
bouncer:
enabled: true
crowdsecMode: stream
crowdsecLapiScheme: http
crowdsecLapiHost: crowdsec:8080
crowdsecLapiKeyFile: /run/secrets/bouncer_key_traefik
CrowdSec creates a bouncer called traefik from the secret named bouncer_key_traefik; this is the Docker image's documented secret naming. Your own apps get protection by adding crowdsec@file to their router's middlewares, as whoami does above.
Linux#
cd ~/docker_volumes/crowdsec docker compose up -d docker compose ps
Windows (PowerShell)#
Set-Location "$HOME\docker_volumes\crowdsec" docker compose up -d docker compose ps
Mac (Terminal)#
Same commands as Linux.
Check it worked. Both containers show running. For the first minute or two Traefik logs crowdsecQuery:unreachable while CrowdSec downloads its rules; this is normal. Then the bouncer is listed:
docker exec crowdsec cscli bouncers list
Real output from our test (shortened):
Name IP Address Valid Last API pull Type Version Auth Type traefik 172.30.0.3 ✔️ 2026-10-31T06:10:29Z Crowdsec-Bouncer-Traefik-Plugin v1.8.1 api-key
Tip: Already run Traefik? Do not start a second one. Add the three plugin flags, the
./traefikfile mount, the log folder mount and the secret to your own Traefik service, and putcrowdsecon the same network.
Step 3: Check that CrowdSec reads the right log#
The most common failure is a running CrowdSec that reads nothing. Send one request, then ask for the read counters.
Linux#
curl -s -o /dev/null -w "%{http_code}\n" http://localhost/
docker exec crowdsec cscli metrics show acquisition
docker exec crowdsec cscli collections list
Windows (PowerShell)#
curl.exe -s -o NUL -w "%{http_code}`n" http://localhost/
docker exec crowdsec cscli metrics show acquisition
docker exec crowdsec cscli collections list
Mac (Terminal)#
Same commands as Linux.
Check it worked. The first command prints 200. The table must list file:/var/log/traefik/access.log with lines read and parsed above zero. Real output from our test:
| file:/var/log/traefik/access.log | 1 | 1 | - | - | 1 |
The columns are lines read, parsed, unparsed, poured to bucket and whitelisted. The "1 whitelisted" is correct: private and local addresses are whitelisted by default (see the next step). crowdsecurity/traefik must appear as enabled in the collections list.
Tip: "Lines read" zero means a wrong file path or a missing shared folder. "Lines unparsed" means the
typelabel does not match a parser. Both are fixed inacquis.yaml.
Step 4: Ban an address on purpose and watch it blocked#
You will make a manual ban for a short time. Do this from the address you want to test with, and keep it short: you can lock yourself out of your own sites.
Linux#
Find the address Traefik sees, ban it for two minutes, wait a minute, then test:
docker exec traefik tail -n 3 /var/log/traefik/access.log docker exec crowdsec cscli decisions add --ip <ADDRESS_FROM_LOG> --duration 2m --reason "ban test" sleep 65 curl -i http://localhost/ docker exec crowdsec cscli decisions delete --ip <ADDRESS_FROM_LOG>
Windows (PowerShell)#
docker exec traefik tail -n 3 /var/log/traefik/access.log docker exec crowdsec cscli decisions add --ip <ADDRESS_FROM_LOG> --duration 2m --reason "ban test" Start-Sleep -Seconds 65 curl.exe -i http://localhost/ docker exec crowdsec cscli decisions delete --ip <ADDRESS_FROM_LOG>
Mac (Terminal)#
Same commands as Linux.
Check it worked. The ban call returns HTTP/1.1 403 Forbidden with an empty body. After the delete, wait about a minute and the page returns 200 again. Real result from our Windows test: 200 before, 403 about 65 seconds after the ban, 200 again after the delete. On Docker Desktop the address in the log was the Docker network gateway (172.30.0.1), not a real visitor address, so a real test needs a Linux host or a visitor from outside.
Tip: The docs' own detection test is a request to
/crowdsec-test-NtktlJHV4TfBSK3wvlhiOBnlfrom a public IP (for example your phone on mobile data). It will not fire from localhost or from any home-network (private RFC 1918) address, which are whitelisted. Then rundocker exec crowdsec cscli alerts list -s crowdsecurity/http-generic-test.
Step 5: Check the community connection#
CrowdSec registers with the Central API (CAPI) on first start unless you disable it. This is what delivers the community blocklist.
Linux#
docker exec crowdsec cscli capi status docker exec crowdsec cscli console status
Windows (PowerShell)#
docker exec crowdsec cscli capi status docker exec crowdsec cscli console status
Mac (Terminal)#
Same commands as Linux.
Check it worked. The docs' expected line is INFO You can successfully interact with Central API (CAPI). We ran our test with CAPI switched off, so we saw instead no configuration for Central API (CAPI). Read the next section before you decide what to share.
What CrowdSec shares with the community#
This is the part to read slowly. CrowdSec is "crowdsourced": your engine and the Central API exchange data both ways.
| Direction | What | Source |
|---|---|---|
| Out, per alert | Scenario name, scenario hash and version, timestamp, your engine's machine_id, the attacker's IP with geolocation | CAPI introduction |
| Out, at login | The list of scenarios you have enabled | CAPI introduction |
| Out, regularly | Names and versions of your engines and bouncers | CAPI introduction |
| Out, only on opt-in | Manual decisions (on when you enrol in the Console) and alerts from custom or modified scenarios | CAPI introduction |
| In | The community blocklist, shaped to your installed scenarios | Community Blocklist page |
The documented list contains no log lines and no page content. We did not capture the traffic to confirm it, so treat the table as what the docs promise.
How big the blocklist is depends on who you are:
| Tier | Who | Size (docs) |
|---|---|---|
| Lite | Free users who do not regularly send signals | capped at 3 thousand IPs |
| Community | Free users who contribute regularly | 15 thousand IPs |
| Premium | Paying users | up to 60 thousand IPs |
Only unmodified Hub scenarios count as a contribution. The docs say self-hosted services seen by few people, VPN-only access or OAuth in front often produce few signals, so you may stay on Lite. That is a result of your setup, not a fault.
To opt out of sending signals, the configuration reference documents api.server.online_client.sharing: false. Our test used a stronger switch from the image README, DISABLE_ONLINE_API=true, which turns the Central API off completely: no sharing and no community blocklist. A reporter in issue #4641 (still open) says CAPI timeout errors went away once sharing was disabled; read it before relying on either setting.
Put it behind HTTPS (with or without Traefik)#
CrowdSec has no web page of its own, so this section protects your sites with HTTPS in front of them. It is for a server with a domain; home-only users can skip it or use the tls internal variant under Caddy. Both routes need a DNS record for the site and ports 80 and 443 reaching the server.
With Traefik#
Add a second entry point on 443, a Let's Encrypt resolver, and an HTTPS router that uses the same crowdsec@file middleware.
In the traefik service of docker-compose.yml, add to command::
- "--entrypoints.https.address=:443"
- "--certificatesresolvers.le.acme.email=<your-email>"
- "--certificatesresolvers.le.acme.storage=/letsencrypt/acme.json"
- "--certificatesresolvers.le.acme.httpchallenge.entrypoint=web"
Add "443:443" to its ports: and ./letsencrypt:/letsencrypt to its volumes: (create the folder first). Then give your app these labels, replacing the old HTTP router:
labels:
- "traefik.enable=true"
- "traefik.docker.network=proxy"
- "traefik.http.routers.whoami.rule=Host(`whoami.<your-domain>`)"
- "traefik.http.routers.whoami.entrypoints=https"
- "traefik.http.routers.whoami.tls=true"
- "traefik.http.routers.whoami.tls.certresolver=le"
- "traefik.http.routers.whoami.middlewares=crowdsec@file"
- "traefik.http.services.whoami.loadbalancer.server.port=80"
Apply with docker compose up -d. Check it worked. curl -I https://whoami.<your-domain>/ returns 200 over HTTPS. In our test, the https entry point, tls=true and the crowdsec@file middleware worked together (200 with Traefik's default certificate); we did not run Let's Encrypt.
Without Traefik: Caddy#
Caddy gets and renews its own certificates. CrowdSec reads Caddy's log, and a firewall bouncer on the host blocks the bad addresses before they reach Docker.
Caddy has no first-party CrowdSec bouncer in the CrowdSec docs, so this route uses the documented firewall bouncer instead. We did not run this route; it follows the Caddy, Hub and bouncer docs.
Replace the traefik and whoami services with Caddy and an app, publish the CrowdSec API on loopback only, and change the collection:
services:
caddy:
image: caddy:2.11.7
container_name: caddy
restart: unless-stopped
ports:
- "80:80/tcp"
- "443:443/tcp"
- "443:443/udp"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- ./caddy-data:/data
- ./caddy-config:/config
- ./logs/caddy:/var/log/caddy
networks:
- proxy
crowdsec:
image: crowdsecurity/crowdsec:v1.8.2
container_name: crowdsec
restart: unless-stopped
environment:
COLLECTIONS: "crowdsecurity/caddy"
ports:
- "127.0.0.1:8080:8080"
volumes:
- ./config:/etc/crowdsec
- ./data:/var/lib/crowdsec/data
- ./acquis.yaml:/etc/crowdsec/acquis.d/caddy.yaml:ro
- ./logs/caddy:/var/log/caddy:ro
networks:
- proxy
networks:
proxy:
external: true
The acquis.yaml for Caddy:
filenames: - /var/log/caddy/access.log labels: type: caddy
The Caddyfile, for a public domain:
app.example.com {
log {
output file /var/log/caddy/access.log
}
reverse_proxy whoami:80
}
For a home-only name, add tls internal inside the block. Caddy's own local certificate then needs trusting on each device: docker exec caddy find /data -name root.crt, docker cp caddy:<path-from-find> ./caddy-root.crt, and install that file as a trusted root (Windows: Trusted Root Certification Authorities; Mac: Keychain Access with Always Trust; Linux: your distribution's CA store). Windows and Mac only run the stack for practice here; the firewall step below is Linux only.
Now create a key and install the firewall bouncer on the Linux host. The docs say a dockerized app needs the DOCKER-USER chain, otherwise published ports bypass the normal rules:
cd ~/docker_volumes/crowdsec && docker compose up -d docker exec crowdsec cscli bouncers add firewall-bouncer sudo apt install crowdsec-firewall-bouncer-iptables sudo nano /etc/crowdsec/bouncers/crowdsec-firewall-bouncer.yaml
In that file set api_url: http://127.0.0.1:8080/, api_key: <KEY_PRINTED_ABOVE> and list DOCKER-USER under iptables_chains, then sudo systemctl restart crowdsec-firewall-bouncer. Check it worked. curl -I https://app.example.com/ returns a status line over HTTPS, and docker exec crowdsec cscli bouncers list shows firewall-bouncer with a recent pull time.
Tip: Let's Encrypt needs the DNS record to point at the server and ports 80 and 443 reachable. A certificate error usually means the name in the Caddyfile differs from the DNS record.
Security and testing#
CrowdSec is a security tool, so run it safely.
| Control | How |
|---|---|
| Bouncer key | In secrets/bouncer_key_traefik, chmod 600. Not in labels or compose text |
| CrowdSec API | Not published to the host in the Traefik route. In the Caddy route only 127.0.0.1:8080 |
| Never lock yourself out | Add your own LAN or VPN range to the plugin's clientTrustedIps so it skips the check for it |
| Real client address | Traefik must see the true visitor IP. Behind another proxy or CDN, set Traefik's forwardedHeaders.trustedIPs and the plugin's forwardedHeadersTrustedIps |
| Pinned images | No latest; update on purpose |
| Related guides | For logins in front of apps see authentik SSO; for private access instead of publishing an app see wg-easy VPN |
| Docker socket | Traefik reads it, so it sees all containers. Expose only what you route |
To skip the check for your own network, add this under bouncer: in traefik/crowdsec.yml (key from the plugin README; we did not test it):
clientTrustedIps:
- <your-lan-or-vpn-range>
Quick tests after every change:
docker compose ps docker compose logs --tail=100 crowdsec docker exec crowdsec cscli metrics show acquisition docker exec crowdsec cscli decisions list docker exec crowdsec cscli alerts list
On Windows run the same commands in PowerShell. For the HTTPS check use curl -I https://<your-domain>/ (curl.exe on Windows).
Common mistakes#
These come from the official docs' own warnings, the CrowdSec and plugin issue trackers, and common assistant answers.
| Mistake | Why it happens | Do this instead |
|---|---|---|
| Installing only the agent and expecting blocks | The docs warn the Security Engine "will not block anything"; blocking needs a remediation component | Add the Traefik plugin (or the firewall bouncer) and prove it with the ban test |
Not persisting /var/lib/crowdsec/data | Since 1.7.0 the container refuses to start without a volume there | Mount ./data:/var/lib/crowdsec/data as in Step 2 |
Using latest or a -debian tag casually | Issue #4235 reports latest two months behind while latest-debian moved; issue #4647 hit "unknown group nobody" after a debian tag | Pin v1.8.2 and change it yourself |
| Testing from localhost or the LAN | Private addresses are whitelisted by default, so no alert appears (docs health check) | Test from a public address, or use the manual ban in Step 4 |
| Reading Docker logs with the Docker data source | Issue #4122 (closed): Docker API "version 1.52 is too new" on an old Docker; issue #4781 (open): a leak that can exhaust Docker memory | Read the Traefik access log as a file, as in this guide |
| Banning the proxy's address | Traefik sees the Docker gateway or a CDN address, not the visitor (our Windows test: 172.30.0.1) | Fix the real-IP setting first; use clientTrustedIps for your own network |
| Restarting CrowdSec in a loop | Issue #4160 (closed): the Central API blocks IPs that log in too often; the maintainers cite 25 logins per hour as the limit | Fix the crash cause; keep ./config persisted so it does not re-register |
| Publishing Docker ports and expecting the firewall to block them | The docs say a dockerized app needs the DOCKER-USER chain | Add DOCKER-USER to iptables_chains |
AI assistants: recommending the old forward-auth fbonalair/traefik-crowdsec-bouncer or plugin v1.4.5 | Old tutorials; the docs call the old bouncer deprecated and the Kubernetes sample still shows v1.4.5 | Use the plugin from the docs' Traefik pages and pin a current release, here v1.8.1 |
AI assistants: inventing crowdsec.yml keys or cscli flags | Assistants mix v1.4 to v1.8 syntax | Check each command against cscli --help and the docs |
Tip: Start in a fresh folder and run the ban test on one test route before you add the middleware to every router.
Tip: After each change read
docker compose logs --tail=100 crowdsec. Most CrowdSec errors, such as a bad acquisition file, print there in plain words.
Tip: Add your own address to
clientTrustedIpsbefore you enable blocking on a site you rely on. A ban you made yourself lasts until you remove it.
Troubleshooting#
| Problem | Solution |
|---|---|
Traefik logs crowdsecQuery:unreachable at start | CrowdSec is still starting (about 2 minutes on first run in our test). If it never stops: both services must share the proxy network and the host name must be crowdsec |
cscli bouncers list is empty | The secret is missing or misnamed. It must be bouncer_key_traefik, listed under secrets: in both services |
| Traefik gets 403 from the CrowdSec API | The key does not match. Read the file with cat secrets/bouncer_key_traefik (no newline) and recreate with docker compose up -d --force-recreate |
| Acquisition table is empty | Check the path inside the container: docker exec crowdsec cat /etc/crowdsec/acquis.d/traefik.yaml and that ./logs/traefik is mounted in both services |
| Lines read but "unparsed" | The type label must match the parser (traefik). Check docker exec crowdsec cscli collections list |
| No alerts for obvious attacks | Whitelisted private addresses, or the bouncer sees a proxy address. See Step 4 and the security table |
| I banned myself | docker exec crowdsec cscli decisions delete --ip <ADDRESS>; the bouncer refreshes within 60 seconds |
| Container will not start | docker compose logs crowdsec; check that ./data and ./config are mounted |
cscli capi status fails | docker exec crowdsec ping -c 3 api.crowdsec.net from the docs; check outbound access. If credentials are missing: docker exec crowdsec cscli capi register then docker restart crowdsec |
Diagnostics first: docker compose ps, docker compose logs --tail=100 crowdsec, docker compose logs --tail=100 traefik, docker exec crowdsec cscli metrics.
Known bugs (October 2026)#
- Plugin issue #405 (open): with
crowdsecMode: appsecand the defaultcrowdsecAppsecUnreadableBodyBlock: true, some HTTP/2 requests with no content length get a silent 403 (a restic backup was hit). This guide does not enable AppSec. - CrowdSec issue #4646 (open): the bot-detection page can break behind a strict Content-Security-Policy. This guide does not enable bot detection.
- CrowdSec issue #4781 (open): Docker log reading can leak streams. We read a file instead.
Updates and backups#
Update. Read the release notes, change the tag, then:
cd ~/docker_volumes/crowdsec docker compose pull docker compose up -d docker compose logs --tail=100 crowdsec
On Windows run the same three commands in PowerShell after Set-Location "$HOME\docker_volumes\crowdsec". The container also runs a hub update and upgrade on start, so parsers and scenarios refresh themselves. Do not set NO_HUB_UPGRADE unless you want that off.
Back up. The config holds credentials, so treat the archive as secret.
Linux#
cd ~/docker_volumes/crowdsec docker compose stop crowdsec tar -czf "backups/crowdsec-$(date +%F).tar.gz" config data secrets chmod 600 backups/crowdsec-*.tar.gz docker compose start crowdsec
Add a crontab entry for 03:00 that runs the same commands.
Windows (PowerShell)#
Set-Location "$HOME\docker_volumes\crowdsec" docker compose stop crowdsec tar -czf "backups\crowdsec-$(Get-Date -Format yyyy-MM-dd).tar.gz" config data secrets docker compose start crowdsec
Schedule it with Task Scheduler (a daily task that runs PowerShell with this script).
Mac (Terminal)#
Same commands as Linux; use cron or launchd for the schedule.
Restore test. Unpack into a new empty folder and check the contents, never over the live one:
mkdir -p /tmp/restore-test && tar -xzf backups/crowdsec-<date>.tar.gz -C /tmp/restore-test ls /tmp/restore-test/config /tmp/restore-test/data
On Windows use New-Item -ItemType Directory $env:TEMP\restore-test and tar -xzf ... -C $env:TEMP\restore-test.
Performance and resources#
The docs ask for 1 CPU core, 100 MB of free RAM and 1 GB of disk, because the database can grow. In our test, with almost no traffic, docker stats showed CrowdSec at about 38 MiB and Traefik at about 43 MiB. Your numbers rise with log volume.
- Watch it:
docker stats crowdsec traefikanddocker exec crowdsec cscli metrics. - Speed: stream mode keeps only banned IPs in memory, so a request costs a cache lookup, not an API call.
- Disk: the SQLite database lives in
./data; the Traefik access log in./logs/traefikkeeps growing, so watch its size. - Prometheus: metrics are on 6060 inside the container; this guide does not publish them.
FAQ#
Is CrowdSec free? Yes, the engine is MIT-licensed. The Premium blocklist tier is paid; the free tiers work as described above.
Does it replace fail2ban? The project's README says it reads the same logs but splits detection from blocking, so you can detect on one machine and block on another.
Will it block me or my customers by mistake? It can. Use clientTrustedIps for your own networks and check cscli decisions list when something odd happens.
Do I need an account? No. The Console is optional; enrolment needs a key from app.crowdsec.net and turns on extra sharing options.
Does it work without Traefik? Yes. The docs list bouncers for Nginx, Apache, HAProxy, firewalls and more. The Caddy section above uses the firewall bouncer.
Can I run the server on Windows or Mac? Windows has a native installer; macOS has none, so use Docker. For a public server, use Linux.
Sources#
- CrowdSec release v1.8.2, README, Docker image README and LICENSE - read October 2026
- Introduction, Docker install, macOS, Windows and Health-Check - read October 2026
- Traefik bouncer, Traefik WAF quickstart, firewall bouncer and Local API for bouncers - read October 2026
- Central API introduction, Community Blocklist and configuration reference - read October 2026
- Traefik plugin repository (README, docker-compose.yml, acquis.yaml, release v1.8.1) and firewall bouncer repository - read October 2026
- Hub collection traefik and caddy - read October 2026
- Traefik ACME reference, Caddy log directive and Caddy release v2.11.7 - read October 2026
- Issues: CrowdSec #4122, #4160, #4235, #4641, #4646, #4647, #4781; plugin #405 - read October 2026
- Related DIY Lab Hub guides: authentik SSO, wg-easy VPN, restic Docker backups
Tested by DIY Lab Hub on 2026-10-31 (stack on Docker Desktop for Windows; Linux, Mac, Let's Encrypt, Caddy route and firewall bouncer not run). Checked against the official docs on 2026-10-31.