Traefik v3 and CrowdSec with Docker Compose: A Modern Security Stack
Table of Contents ๐
This guide provides a streamlined approach to deploying a powerful and secure web stack using Traefik as a reverse proxy and CrowdSec for threat protection. We will use Docker Compose to orchestrate the services, creating a setup that is easy to manage, scale, and maintain.
Changelog
Date | Change |
---|---|
2025-07-10 | Added Special Use-Case: Included a clear example of how to deploy a service without CrowdSec protection for specific needs. |
2025-07-09 | Initial Version: Article created based on best practices for a modern Traefik v3 and CrowdSec deployment with Docker Compose. |
1. Prerequisites
Before you begin, ensure you have the following installed on your server (e.g., Ubuntu 22.04 or Debian 12):
- Docker and Docker Compose
curl
,openssl
, andapache2-utils
(for password generation)- A domain name pointed to your serverโs IP address.
sudo
or root access.
You can install the required utilities with:
sudo apt update
sudo apt install -y curl openssl apache2-utils
2. Directory Structure
A well-organized directory structure is key. We will create a central location for our stackโs configuration and data.
# Create the main directory
sudo mkdir -p /opt/containers/traefik-stack
cd /opt/containers/traefik-stack
# Create directories for each service
sudo mkdir -p traefik/{config,dynamic,logs,certs}
sudo mkdir -p crowdsec/{config,data}
# Set correct permissions for Let's Encrypt certificates
sudo chmod 600 traefik/certs
This structure keeps everything tidy and separated.
3. Configuration Files
Now, letโs create the configuration files for our stack.
3.1. Main Configuration (.env
)
This file holds all your environment-specific variables.
sudo tee .env > /dev/null << 'EOF'
# --- General Settings ---
TZ=Europe/Berlin
DOMAIN_NAME=your-domain.com
# --- Traefik Settings ---
TRAEFIK_DASHBOARD_HOST=traefik.${DOMAIN_NAME}
LETSENCRYPT_EMAIL=your-email@example.com
# --- CrowdSec Settings ---
CROWDSEC_COLLECTIONS="crowdsecurity/traefik crowdsecurity/linux"
# --- Credentials ---
# Generate a secure API key for the bouncer
CROWDSEC_BOUNCER_API_KEY=$(openssl rand -base64 32)
EOF
โ ๏ธ IMPORTANT |
Remember to replace |
3.2. Docker Compose (docker-compose.yml
)
This file defines our three core services: Traefik, CrowdSec, and the Traefik Bouncer.
sudo tee docker-compose.yml > /dev/null << 'EOF'
version: '3.9'
services:
traefik:
image: traefik:v3.0
container_name: traefik
restart: unless-stopped
depends_on:
- crowdsec-bouncer
ports:
- "80:80"
- "443:443"
networks:
- traefik_proxy
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./traefik/config/traefik.yml:/etc/traefik/traefik.yml:ro
- ./traefik/dynamic:/etc/traefik/dynamic:ro
- ./traefik/certs:/certs
- ./traefik/logs:/var/log/traefik
environment:
- TZ=${TZ}
crowdsec:
image: crowdsecurity/crowdsec:latest
container_name: crowdsec
restart: unless-stopped
networks:
- traefik_proxy
volumes:
- ./crowdsec/config:/etc/crowdsec:z
- ./crowdsec/data:/var/lib/crowdsec/data:z
- ./traefik/logs:/var/log/traefik:ro
- /var/log:/var/log/host/:ro
environment:
- TZ=${TZ}
- COLLECTIONS=${CROWDSEC_COLLECTIONS}
crowdsec-bouncer:
image: crowdsecurity/traefik-bouncer:latest
container_name: crowdsec-bouncer
restart: unless-stopped
depends_on:
- crowdsec
networks:
- traefik_proxy
environment:
- TZ=${TZ}
- CROWDSEC_BOUNCER_API_KEY=${CROWDSEC_BOUNCER_API_KEY}
- CROWDSEC_AGENT_HOST=crowdsec:8080
networks:
traefik_proxy:
name: traefik_proxy
external: true
EOF
๐ก EXTERNAL NETWORK |
We define |
3.3. Traefik Static Configuration (traefik/config/traefik.yml
)
This file contains the startup configuration for Traefik.
sudo tee traefik/config/traefik.yml > /dev/null << 'EOF'
global:
checkNewVersion: true
sendAnonymousUsage: false
# API and Dashboard
api:
dashboard: true
# Entrypoints
entryPoints:
web:
address: ":80"
http:
redirections:
entryPoint:
to: websecure
scheme: https
websecure:
address: ":443"
# Providers
providers:
docker:
endpoint: "unix:///var/run/docker.sock"
exposedByDefault: false
file:
directory: /etc/traefik/dynamic
watch: true
# Let's Encrypt Certificate Resolver
certificatesResolvers:
tls_resolver:
acme:
email: ${LETSENCRYPT_EMAIL}
storage: /certs/acme.json
tlsChallenge: {}
# Logging
log:
level: INFO
filePath: /var/log/traefik/traefik.log
accessLog:
filePath: /var/log/traefik/access.log
EOF
3.4. Traefik Dynamic Configuration (traefik/dynamic/middlewares.yml
)
Here we define reusable middleware pieces, including security headers and the CrowdSec bouncer.
sudo tee traefik/dynamic/middlewares.yml > /dev/null << 'EOF'
http:
middlewares:
# 1. General security headers
security-headers:
headers:
browserXssFilter: true
contentTypeNosniff: true
forceSTSHeader: true
stsIncludeSubdomains: true
stsPreload: true
stsSeconds: 31536000
# 2. CrowdSec Bouncer
crowdsec-bouncer:
forwardAuth:
address: http://crowdsec-bouncer:8080/api/v1/forwardAuth
trustForwardHeader: true
# 3. Basic Auth for the dashboard
traefik-dashboard-auth:
basicAuth:
usersFile: "/etc/traefik/dynamic/.htpasswd"
routers:
# Define the router for the Traefik dashboard
dashboard:
rule: "Host(`${TRAEFIK_DASHBOARD_HOST}`)"
service: api@internal
entryPoints:
- websecure
middlewares:
- traefik-dashboard-auth
tls:
certResolver: tls_resolver
EOF
3.5. Create Dashboard Password
Secure your Traefik dashboard with a username and password.
# You will be prompted to enter a password for the user 'admin'
sudo htpasswd -c traefik/dynamic/.htpasswd admin
3.6. CrowdSec Acquisition Configuration (crowdsec/config/acquis.yaml
)
Tell CrowdSec which log files to monitor.
sudo tee crowdsec/config/acquis.yaml > /dev/null << 'EOF'
filenames:
- /var/log/traefik/access.log
labels:
type: traefik
---
filenames:
- /var/log/host/auth.log
- /var/log/host/syslog
labels:
type: syslog
EOF
4. Launch and Verify the Stack
With all configuration files in place, itโs time to start the stack.
# First, create the external network
docker network create traefik_proxy
# Start the services
docker-compose up -d
# Check the status
docker-compose ps
All three containers should be Up
and healthy.
To verify CrowdSec is working, check the bouncer list:
docker-compose exec crowdsec cscli bouncers list
You should see the traefik-bouncer
listed and validated. You can now access your Traefik dashboard at the URL you configured (e.g., https://traefik.your-domain.com
).
5. Adding Services to Traefik
Here is how to expose other Docker services through Traefik, with and without CrowdSec protection.
5.1. Scenario 1: Service Protected by CrowdSec (Standard)
This is the recommended setup for most public-facing services. Weโll use WordPress as an example. Create a docker-compose.yml
in a separate directory (e.g., /opt/containers/wordpress
):
version: '3.9'
services:
wordpress:
image: wordpress:latest
restart: unless-stopped
networks:
- default
- traefik_proxy
environment:
WORDPRESS_DB_HOST: db:3306
# ... other WordPress environment variables
labels:
- "traefik.enable=true"
# --- Routing ---
- "traefik.http.routers.wordpress.rule=Host(`blog.your-domain.com`)"
- "traefik.http.routers.wordpress.entrypoints=websecure"
# --- TLS ---
- "traefik.http.routers.wordpress.tls=true"
- "traefik.http.routers.wordpress.tls.certresolver=tls_resolver"
# --- Middlewares (Security + CrowdSec) ---
- "traefik.http.routers.wordpress.middlewares=security-headers@file,crowdsec-bouncer@file"
# --- Service Port ---
- "traefik.http.services.wordpress.loadbalancer.server.port=80"
db:
image: mariadb:10.6
restart: unless-stopped
networks:
- default
# ... other database configuration
networks:
traefik_proxy:
external: true
default:
The key label is traefik.http.routers.wordpress.middlewares=security-headers@file,crowdsec-bouncer@file
, which applies both our security headers and the CrowdSec bouncer.
5.2. Scenario 2: Service Bypassing CrowdSec (Special Case)
Sometimes you need to expose a service without CrowdSecโs interference. Common reasons include:
- A public API that must not be blocked.
- A site heavily reliant on ad network traffic, where false positives could impact revenue.
- Internal tools that are already secured by other means.
To do this, you simply omit the crowdsec-bouncer@file
middleware from the routerโs labels.
# In the labels for your service (e.g., an API)
labels:
- "traefik.enable=true"
- "traefik.http.routers.my-api.rule=Host(`api.your-domain.com`)"
- "traefik.http.routers.my-api.entrypoints=websecure"
- "traefik.http.routers.my-api.tls=true"
- "traefik.http.routers.my-api.tls.certresolver=tls_resolver"
# --- Middlewares (Security ONLY) ---
- "traefik.http.routers.my-api.middlewares=security-headers@file"
- "traefik.http.services.my-api.loadbalancer.server.port=3000"
โน๏ธ MAXIMUM FLEXIBILITY |
By applying middleware on a per-router basis instead of globally on the entrypoint, you gain complete control over which services are protected by CrowdSec and which are not. |
6. Maintenance
To update your stack to the latest container images:
cd /opt/containers/traefik-stack
docker-compose pull
docker-compose up -d --remove-orphans
Conclusion
You now have a modern, secure, and flexible reverse proxy setup. Traefik handles routing and TLS termination effortlessly, while CrowdSec provides a powerful, community-driven security layer. By managing middlewares on a per-service basis, you can tailor the level of protection to fit the exact needs of each application you deploy.