Every self-hosted service needs a front door. Traefik is the door; Crowdsec is the bouncer. Together they handle routing, TLS, and intrusion detection for your entire homelab from a single point.

Why this combination

Traefik is a reverse proxy that discovers services automatically. Deploy a Docker container with the right labels and Traefik routes traffic to it — no config file editing, no nginx reload. It handles Let's Encrypt certificates automatically. For a homelab with 25+ services, this is the difference between manageable and miserable.

Crowdsec is a collaborative security engine. It parses your Traefik access logs, detects attack patterns (brute force, credential stuffing, path traversal, scanner fingerprints), and blocks malicious IPs. The collaborative part matters: Crowdsec nodes share threat intelligence, so an attack seen by one node gets blocked by all of them. Think fail2ban, but aware of application-layer attacks and connected to a community threat feed.

The architecture

Internet → Traefik (ports 80/443) → your services
              ↓ (access logs)
           Crowdsec → (ban decisions) → Traefik bouncer

Traefik is the only thing exposed to the internet. It terminates TLS, routes requests by hostname, and forwards to backend containers. Crowdsec reads the access logs Traefik produces, evaluates them against its detection scenarios, and pushes ban decisions back to a Traefik bouncer plugin that drops requests from flagged IPs before they reach your services.

Setting up Traefik

Create the project directory and a Docker network that all proxied services will share:

mkdir -p ~/services/traefik
docker network create proxy

The compose file:

services:
  traefik:
    image: traefik:v3.0
    container_name: traefik
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./config:/etc/traefik
      - ./certs:/certs
    networks:
      - proxy

networks:
  proxy:
    external: true

The static config (config/traefik.yml):

entryPoints:
  web:
    address: ":80"
    http:
      redirections:
        entryPoint:
          to: websecure
  websecure:
    address: ":443"

certificatesResolvers:
  letsencrypt:
    acme:
      email: you@yourdomain.com
      storage: /certs/acme.json
      httpChallenge:
        entryPoint: web

providers:
  docker:
    exposedByDefault: false
    network: proxy

accessLog:
  filePath: /var/log/traefik/access.log

Now any container on the proxy network with the right labels gets automatically routed:

labels:
  - "traefik.enable=true"
  - "traefik.http.routers.myservice.rule=Host(`myservice.yourdomain.com`)"
  - "traefik.http.routers.myservice.tls.certresolver=letsencrypt"

Adding Crowdsec

Crowdsec reads Traefik's access logs and makes ban decisions. Add it to the Traefik compose file or run it separately — I prefer co-located:

  crowdsec:
    image: crowdsecurity/crowdsec:latest
    container_name: crowdsec
    restart: unless-stopped
    volumes:
      - ./crowdsec/config:/etc/crowdsec
      - ./crowdsec/data:/var/lib/crowdsec/data
      - /var/log/traefik:/var/log/traefik:ro
    environment:
      - COLLECTIONS=crowdsecurity/traefik crowdsecurity/http-cve

Install the Traefik bouncer plugin and register it with your Crowdsec instance. The bouncer intercepts requests at the Traefik layer and drops anything from a banned IP before it reaches your services.

What you get

After this setup:

  • Every service gets automatic HTTPS with valid certificates
  • New services are discovered and routed automatically via Docker labels
  • Attack traffic is detected and blocked collaboratively
  • You have structured access logs for every request that hits your infrastructure

This is the foundation. Everything else in The Stack sits behind this gateway.