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

DateChange
2025-07-10Added Special Use-Case: Included a clear example of how to deploy a service without CrowdSec protection for specific needs.
2025-07-09Initial 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, and apache2-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 your-domain.com and your-email@example.com with your actual values.

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 traefik_proxy as an external network. This allows other Docker Compose stacks to easily connect to it. Create it once with: docker network create traefik_proxy.

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.

๐Ÿ“šTRAEFIK DOCUMENTATION ๐Ÿ›ก๏ธCROWDSEC DOCUMENTATION