DockerSelf-Hosting

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.

CrowdSec official logo

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.

WhatValue (official docs, read October 2026)
Current versionCrowdSec v1.8.2, released 26 October 2026 (latest stable)
Images to pincrowdsecurity/crowdsec:v1.8.2, traefik:v3.7.17, Traefik plugin v1.8.1
Time neededAbout 20 minutes; the first CrowdSec start took about 2 minutes in our test
Size and RAMDocs: 100 MB free RAM, 1 GB disk. Measured idle: CrowdSec 38 MiB, Traefik 43 MiB
Ports8080/tcp (CrowdSec API, kept inside Docker), 6060/tcp (metrics, not used); Traefik 80 and 443
LicenceCrowdSec: MIT. Traefik plugin: Apache-2.0 (community project, not made by CrowdSec)
Official docsdocs.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/traefik collection 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.

Three-lane diagram: a visitor sends a request to Traefik with the bouncer plugin, which forwards allowed requests to your app and writes an access log; CrowdSec reads that log, creates ban decisions in its Local API, and Traefik pulls the ban list every 60 seconds; a dashed arrow sends signal metadata to the CrowdSec Central API

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:

Run in terminalbash
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.

Run in terminalpowershell
docker --version
docker compose version

Mac: Docker Desktop. The docs say CrowdSec is not compiled for macOS, so a container is the only route.

Run in terminalbash
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#

Run in terminalbash
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)#

Codepowershell
$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:

Run in terminalbash
wc -c secrets/bouncer_key_traefik
Codepowershell
(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

File contentyaml
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):

File contentyaml
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.

File contentyaml
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#

Run in terminalbash
cd ~/docker_volumes/crowdsec
docker compose up -d
docker compose ps

Windows (PowerShell)#

Codepowershell
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:

Run in terminalbash
docker exec crowdsec cscli bouncers list

Real output from our test (shortened):

Codetext
 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 ./traefik file mount, the log folder mount and the secret to your own Traefik service, and put crowdsec on 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#

Run in terminalbash
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)#

Run in terminalpowershell
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:

Codetext
| 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 type label does not match a parser. Both are fixed in acquis.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:

Run in terminalbash
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)#

Run in terminalpowershell
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-NtktlJHV4TfBSK3wvlhiOBnl from 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 run docker 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#

Run in terminalbash
docker exec crowdsec cscli capi status
docker exec crowdsec cscli console status

Windows (PowerShell)#

Run in terminalpowershell
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.

DirectionWhatSource
Out, per alertScenario name, scenario hash and version, timestamp, your engine's machine_id, the attacker's IP with geolocationCAPI introduction
Out, at loginThe list of scenarios you have enabledCAPI introduction
Out, regularlyNames and versions of your engines and bouncersCAPI introduction
Out, only on opt-inManual decisions (on when you enrol in the Console) and alerts from custom or modified scenariosCAPI introduction
InThe community blocklist, shaped to your installed scenariosCommunity 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:

TierWhoSize (docs)
LiteFree users who do not regularly send signalscapped at 3 thousand IPs
CommunityFree users who contribute regularly15 thousand IPs
PremiumPaying usersup 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::

File contentyaml
      - "--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:

File contentyaml
    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:

File contentyaml
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:

File contentyaml
filenames:
  - /var/log/caddy/access.log
labels:
  type: caddy

The Caddyfile, for a public domain:

File contenttext
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:

Run in terminalbash
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.

ControlHow
Bouncer keyIn secrets/bouncer_key_traefik, chmod 600. Not in labels or compose text
CrowdSec APINot published to the host in the Traefik route. In the Caddy route only 127.0.0.1:8080
Never lock yourself outAdd your own LAN or VPN range to the plugin's clientTrustedIps so it skips the check for it
Real client addressTraefik must see the true visitor IP. Behind another proxy or CDN, set Traefik's forwardedHeaders.trustedIPs and the plugin's forwardedHeadersTrustedIps
Pinned imagesNo latest; update on purpose
Related guidesFor logins in front of apps see authentik SSO; for private access instead of publishing an app see wg-easy VPN
Docker socketTraefik 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):

File contentyaml
          clientTrustedIps:
            - <your-lan-or-vpn-range>

Quick tests after every change:

Run in terminalbash
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.

MistakeWhy it happensDo this instead
Installing only the agent and expecting blocksThe docs warn the Security Engine "will not block anything"; blocking needs a remediation componentAdd the Traefik plugin (or the firewall bouncer) and prove it with the ban test
Not persisting /var/lib/crowdsec/dataSince 1.7.0 the container refuses to start without a volume thereMount ./data:/var/lib/crowdsec/data as in Step 2
Using latest or a -debian tag casuallyIssue #4235 reports latest two months behind while latest-debian moved; issue #4647 hit "unknown group nobody" after a debian tagPin v1.8.2 and change it yourself
Testing from localhost or the LANPrivate 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 sourceIssue #4122 (closed): Docker API "version 1.52 is too new" on an old Docker; issue #4781 (open): a leak that can exhaust Docker memoryRead the Traefik access log as a file, as in this guide
Banning the proxy's addressTraefik 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 loopIssue #4160 (closed): the Central API blocks IPs that log in too often; the maintainers cite 25 logins per hour as the limitFix the crash cause; keep ./config persisted so it does not re-register
Publishing Docker ports and expecting the firewall to block themThe docs say a dockerized app needs the DOCKER-USER chainAdd DOCKER-USER to iptables_chains
AI assistants: recommending the old forward-auth fbonalair/traefik-crowdsec-bouncer or plugin v1.4.5Old tutorials; the docs call the old bouncer deprecated and the Kubernetes sample still shows v1.4.5Use the plugin from the docs' Traefik pages and pin a current release, here v1.8.1
AI assistants: inventing crowdsec.yml keys or cscli flagsAssistants mix v1.4 to v1.8 syntaxCheck 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 clientTrustedIps before you enable blocking on a site you rely on. A ban you made yourself lasts until you remove it.

Troubleshooting#

ProblemSolution
Traefik logs crowdsecQuery:unreachable at startCrowdSec 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 emptyThe secret is missing or misnamed. It must be bouncer_key_traefik, listed under secrets: in both services
Traefik gets 403 from the CrowdSec APIThe 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 emptyCheck 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 attacksWhitelisted private addresses, or the bouncer sees a proxy address. See Step 4 and the security table
I banned myselfdocker exec crowdsec cscli decisions delete --ip <ADDRESS>; the bouncer refreshes within 60 seconds
Container will not startdocker compose logs crowdsec; check that ./data and ./config are mounted
cscli capi status failsdocker 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: appsec and the default crowdsecAppsecUnreadableBodyBlock: 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:

Run in terminalbash
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#

Run in terminalbash
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)#

Codepowershell
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:

Run in terminalbash
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 traefik and docker 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/traefik keeps 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#

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.