🛡️ CrowdSec
CrowdSec acts as the intelligence hub for the server. It reads logs, detects attacks (brute force, scanners, botnets), and issues "Ban" decisions. It does not block traffic itself; it instructs Caddy (the "Bouncer") to do the blocking.
Compose File
The container is configured to persist its database locally (for backups) and read Caddy's logs in Read-Only mode.
networks:
dockerapps-net:
external: true
services:
crowdsec:
image: crowdsecurity/crowdsec:latest
container_name: crowdsec
hostname: crowdsec
networks:
dockerapps-net:
ipv4_address: 172.20.0.24
ipv6_address: fd00:dead:beef:2::24
environment:
- PUID=${PUID}
- PGID=${PGID}
- TZ=${TZ}
# ADDED: additional collections and scenarios
- COLLECTIONS=crowdsecurity/caddy crowdsecurity/http-cve crowdsecurity/base-http-scenarios crowdsecurity/whitelist-good-actors crowdsecurity/linux LePresidente/jellyfin
- SCENARIOS=crowdsecurity/http-probing LePresidente/jellyfin-bf baudneo/gotify-bf crowdsecurity/http-generic-bf
volumes:
- ./config:/etc/crowdsec:rw
- ./acquis.yaml:/etc/crowdsec/acquis.yaml:ro
- /mnt/pool01/homelab/services/gateway-stack/caddy/logs:/var/log/caddy:ro
#Mount Jellyfin's Logs - note to add filename log to acquis.yaml too
- /mnt/pool01/homelab/services/media-stack/jellyfin/jellyfin-config/log:/var/log/jellyfin:ro
- ./data:/var/lib/crowdsec/data/
# Added Docker Socket to monitor VoidAuth's instance - not yet configured with Crowdsec
- /var/run/docker.sock:/var/run/docker.sock:ro
restart: unless-stopped
Compose Notes
-
COLLECTIONS: Added a few more to handle generic web threats (crawlers, scanners) and LePresidente/jellyfin for application-specific parsing.
-
SCENARIOS: Implemented LePresidente/jellyfin-bf for brute-force detection and crowdsecurity/http-probing to catch bots scanning for sensitive files.
-
Log Mounting: Mounted the internal Jellyfin log path (/log:/var/log/jellyfin:ro) to allow CrowdSec to monitor internal application events that Caddy cannot see.
Acquisition Configuration (acquis.yaml)
These files tell CrowdSec specifically which files to tail and how to interpret them. Without this, CrowdSec runs blind.
Create file @ crowdsec/acquis.yaml
# Caddy Access Logs
filenames:
- /var/log/caddy/access.log
labels:
type: caddy
---
# Jellyfin Logs
filenames:
- /var/log/jellyfin/log_*.log
labels:
type: jellyfin
Acquis Notes
- Added Jellyfin's logs for
deniedentry loggings to ban. Instead of the broad .log wildcard, switched to log_.log. This prevents CrowdSec from attempting to parse FFmpeg transcode logs, which contain technical data but not relevant.
Expand to view acquis file if using Authentik
If using Authentik: Create new
File: crowdsec/config/acquis.d/authentik.yaml
# Watch the Authentik's instance via the Docker container
source: docker
container_name:
- authentik-server
labels:
type: authentik
Integration Workflow
CrowdSec requires a "Bouncer" API key to allow Caddy to check IP reputations.
Generate Bouncer API Key
If we rebuild the server, the old API key in Caddy's .env will be invalid. We must generate a new one:
- Start CrowdSec:
- Run the Registration Command:
docker exec crowdsec cscli bouncers add caddy-bouncer - Copy the Key: The output will be
Api key for 'caddy-bouncer': <KEY> - Update Caddy: Paste this key into
/mnt/pool01/homelab/services/gateway-stack/caddy/.envasCROWDSEC_API_KEY. - Restart Caddy:
docker compose restart caddy
Auto-Update Hub
Using Systemd Timer to ensure detection rules (Hub) remain fresh between software upgrades. This prevents the "stale rules" vulnerability.
The Service (.service)
Create New File: /etc/systemd/system/crowdsec-hub-update.service:
[Unit]
Description=Update CrowdSec Hub Rules and Parsers
After=docker.service
Requires=docker.service
[Service]
Type=oneshot
# Chain the update and upgrade commands safely inside the container
ExecStart=/usr/bin/docker exec crowdsec sh -c "cscli hub update && cscli hub upgrade"
The Timer (.timer)
Create New File: /etc/systemd/system/crowdsec-hub-update.timer:
[Unit]
Description=Weekly timer for CrowdSec Hub updates
[Timer]
# Run every Sunday at 04:15:00
OnCalendar=Sun *-*-* 04:15:00
# If the system was off, run immediately on boot
Persistent=true
# Add delay to prevent exact-second spikes if schedule miss due to shutdown system
RandomizedDelaySec=600
[Install]
WantedBy=timers.target
Enable & Activate:
# Reload systemd to recognize new files
sudo systemctl daemon-reload
# Enable and start the timer
sudo systemctl enable --now crowdsec-hub-update.timer
# Verify the schedule
systemctl list-timers | grep crowdsec
Security Layers & Cloudflare Sync
While CrowdSec handles the "Brain" locally, our Cloudflare WAF acts as the first filter at the Edge.
| Layer | Component | Function |
|---|---|---|
| Edge | Cloudflare WAF | Blocks non-SG traffic and known bots before they hit your home IP. |
| Origin | Caddy + MaxMind | Redundant Geo-IP check if traffic bypasses the Cloudflare proxy. |
| Logic | CrowdSec | Parses successful connections for behavioral threats (eg: brute force). |
Operational Commands (Cheatsheet)
Check Status: See if CrowdSec is successfully reading logs and parsing them.
docker exec crowdsec cscli metrics
Look for:
Acquisition Metrics(Lines read) andParser Metrics(Parseds);Local API AlertsandLocal API Bouncers Metrics
Check Community's Decision's Blocklist Count: See if CrowdSec is successfully getting pool of blocklists from upstream.
docker exec crowdsec cscli decisions list --origin CAPI | wc -l
Output should in 10K-20K - this confirms server is successfully downloading the "Community Blocklist" (CAPI). If this connection were broken, this number would be 0.

Verify Parsing: Ensure CrowdSec is seeing the new traffic from our tests.
docker exec crowdsec cscli hub list | grep "caddy"
List Active Bans: See who is currently blocked.
docker exec crowdsec cscli decisions list

Manually Ban an IP: Useful for testing or blocking a persistent pest.
docker exec crowdsec cscli decisions add --ip <IP_ADDRESS> --duration 24h --reason "manual ban"
Unban an IP:
docker exec crowdsec cscli decisions delete --ip <IP_ADDRESS>
Update Collections: Update the threat intelligence rules.
docker exec crowdsec cscli hub update
docker exec crowdsec cscli hub upgrade
Check CAPI Status:
docker exec crowdsec cscli capi status
Output should be like so:
