Traefik - Modern Reverse Proxy
Traefik is the reverse proxy at the heart of the homelab's Docker infrastructure. It handles routing of all HTTP/HTTPS requests to the appropriate containers, with automatic SSL certificate management and security integration via CrowdSec.
Global Architecture
The infrastructure uses two distinct Traefik instances, each on its own network interface:
- traefik-public (192.168.1.2): Public services accessible from the Internet (*.tellserv.fr)
- traefik-private (192.168.1.3): Local services reserved for the internal network (*.local.tellserv.fr)
This separation provides:
- Network isolation: Private services are never exposed publicly
- Enhanced security: Differentiated security policies based on exposure
- Simplified management: Each instance has its own configuration and rules
Network Prerequisites
The VM hosting Traefik has two NICs (Network Interface Cards):
- NIC 1 (192.168.1.2): Public interface for traefik-public
- NIC 2 (192.168.1.3): Local interface for traefik-private
Common Elements to Both Instances
Image and Version
Both instances use the official Traefik v3 image:
image: traefik:v3
Docker Network
Both instances connect to the external Docker network traefik_network which enables communication with all containers to be proxied:
networks:
- traefik_network
This network must be created beforehand with:
docker network create traefik_network
SSL Certificate Management
Both instances use Let's Encrypt with the Cloudflare DNS challenge to automatically generate wildcard SSL certificates.
DNS Challenge Advantages:
- Wildcard certificates (*.tellserv.fr, *.local.tellserv.fr)
- No need for public HTTP exposure for validation
- Works even for internal services
Common Configuration:
environment:
- TZ=Europe/Paris
- CF_DNS_API_TOKEN=${CF_DNS_API_TOKEN}
The Cloudflare API token must have the following permissions:
- Zone / DNS / Edit
- Zone / Zone / Read
Docker Provider
Both instances use the Docker provider for automatic service discovery:
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
Traefik monitors containers and automatically creates routes based on Docker labels.
Dynamic Configuration
Each instance loads dynamic configuration files from a dedicated directory:
volumes:
- ./dynamic-public:/etc/traefik/dynamic:ro # For traefik-public
- ./dynamic-private:/etc/traefik/dynamic:ro # For traefik-private
These files allow defining middlewares, routers, and services without restarting Traefik.
Traefik Dashboard
Both instances expose their dashboard via a dedicated subdomain:
- traefik-public:
traefik-public.local.tellserv.fr - traefik-private:
traefik-private.local.tellserv.fr
The dashboard allows real-time visualization of:
- Active routers and their rules
- Detected services
- Applied middlewares
- Backend health status
Restart Policy
restart: unless-stopped
Containers automatically restart unless manually stopped.
Docker Host Access
extra_hosts:
- "host.docker.internal:host-gateway"
This configuration allows Traefik containers to access the Docker host via the name host.docker.internal, useful for proxying services running directly on the host.
traefik-public Instance
Role and Usage
The traefik-public instance manages all services accessible from the Internet:
- Public web applications
- Exposed APIs
- Authenticated services accessible from outside
Network Binding
ports:
- "192.168.1.2:80:80"
- "192.168.1.2:443:443"
Traefik listens only on IP 192.168.1.2, corresponding to the VM's first NIC.
Entry Points
Port 80 (HTTP):
web:
address: ":80"
http:
redirections:
entryPoint:
to: websecure
scheme: https
Automatic HTTP to HTTPS redirection for all requests.
Port 443 (HTTPS):
websecure:
address: ":443"
http:
middlewares:
- crowdsec-bouncer@file
- secheaders@file
- ratelimit@file
transport:
respondingTimeouts:
idleTimeout: 300s
Three middlewares applied by default to all public services:
- crowdsec-bouncer: Blocking malicious IPs detected by CrowdSec
- secheaders: HTTP security headers (HSTS, CSP, etc.)
- ratelimit: Request rate limiting
Public Middlewares
File: dynamic-public/middlewares.yml
ratelimit:
ratelimit:
rateLimit:
average: 100
burst: 50
period: 1s
Allows an average of 100 requests/second with bursts up to 50 additional requests.
secheaders:
secheaders:
headers:
stsSeconds: 31536000
forceSTSHeader: true
Forces HSTS (HTTP Strict Transport Security) for 1 year, requiring browsers to always use HTTPS.
evasive:
evasive:
rateLimit:
average: 3
burst: 5
period: 1s
Strict rate limiting for sensitive endpoints (3 req/s average, 5 burst).
CrowdSec Integration
CrowdSec is a community-based intrusion detection and prevention system. Traefik-public integrates the CrowdSec bouncer to automatically block malicious IPs.
CrowdSec Middleware (applied to websecure):
middlewares:
- crowdsec-bouncer@file
The bouncer queries the local CrowdSec API to check if the source IP is banned. On match, the request is blocked with HTTP 403.
SSL Certificates
Certificate storage:
volumes:
- ./letsencrypt-public:/letsencrypt
ACME configuration in traefik-public.yml:
certificatesResolvers:
cloudflare:
acme:
email: your-[email protected]
storage: /letsencrypt/cloudflare_acme.json
keyType: EC256
dnsChallenge:
provider: cloudflare
resolvers:
- "1.1.1.1:53"
- "8.8.8.8:53"
Certificates are automatically renewed 30 days before expiration.
Logging
log:
level: DEBUG
filePath: "/var/log/traefik/traefik.log"
accessLog:
filePath: "/var/log/traefik/access.log"
format: json
Logs stored in /var/log/traefik/ on the Docker host:
- traefik.log: Traefik system logs (startup, errors, reloads)
- access.log: Access logs in JSON format (HTTP requests)
Docker Provider Configuration
providers:
docker:
endpoint: "unix:///var/run/docker.sock"
exposedByDefault: false
network: traefik_network
- exposedByDefault: false: Containers are not automatically exposed, Traefik labels must be explicitly added
- network: traefik_network: Traefik will use this network to communicate with containers
Docker Labels Example
To expose a service via traefik-public:
services:
myapp:
image: myapp:latest
labels:
- "traefik.enable=true"
- "traefik.http.routers.myapp.rule=Host(`app.tellserv.fr`)"
- "traefik.http.routers.myapp.entrypoints=websecure"
- "traefik.http.routers.myapp.tls.certresolver=cloudflare"
- "traefik.http.services.myapp.loadbalancer.server.port=80"
networks:
- traefik_network
traefik-private Instance
Role and Usage
The traefik-private instance manages all services reserved for the local network:
- Administration interfaces (Proxmox, Cockpit)
- Internal dashboards
- Monitoring services
- Development tools
Network Binding
ports:
- "192.168.1.3:80:80"
- "192.168.1.3:443:443"
Traefik listens only on IP 192.168.1.3, corresponding to the VM's second NIC.
Entry Points
Port 80 (HTTP):
weblocal:
address: ":80"
http:
redirections:
entryPoint:
to: local
scheme: https
Automatic HTTP to HTTPS redirection (to local entrypoint).
Port 443 (HTTPS):
local:
address: ":443"
http:
middlewares:
- localonly@file
A single middleware applied by default: localonly which restricts access to local IPs.
Private Middlewares
File: dynamic-private/middlewares.yml
localonly:
localonly:
ipWhiteList:
sourceRange:
- "127.0.0.1/32"
- "192.168.1.0/24"
- "100.64.0.0/10"
- "172.18.0.0/16"
Whitelist of allowed IPs:
- 127.0.0.1/32: Localhost
- 192.168.1.0/24: Main LAN network
- 100.64.0.0/10: Tailscale network (VPN)
- 172.18.0.0/16: Internal Docker network
Any request from an IP outside these ranges is rejected with HTTP 403.
ratelimit, secheaders, evasive:
Identical to traefik-public, available to be applied as needed to specific services.
SSL Certificates
Certificate storage:
volumes:
- ./letsencrypt-private:/letsencrypt
ACME configuration in traefik-private.yml:
certificatesResolvers:
cloudflare:
acme:
email: your-[email protected]
storage: /letsencrypt/cloudflare_acme.json
keyType: EC256
dnsChallenge:
provider: cloudflare
Although services are local, they benefit from valid SSL certificates thanks to DNS challenge.
Logging
log:
level: DEBUG
filePath: "/var/log/traefik-local/traefik.log"
accessLog:
filePath: "/var/log/traefik-local/access.log"
format: json
Logs stored in /var/log/traefik-local/ on the Docker host.
Docker Provider Configuration
Identical to traefik-public:
providers:
docker:
endpoint: "unix:///var/run/docker.sock"
exposedByDefault: false
network: traefik_network
Exposed Services
Static configuration files in dynamic-private/:
cockpit.yml: Proxying Cockpit (system administration web interface) proxmox.yml: Proxying Proxmox interface
These files define routers and services for applications not running in Docker.
Docker Labels Example
To expose a service via traefik-private:
services:
monitoring:
image: grafana/grafana:latest
labels:
- "traefik.enable=true"
- "traefik.http.routers.monitoring.rule=Host(`grafana.local.tellserv.fr`)"
- "traefik.http.routers.monitoring.entrypoints=local"
- "traefik.http.routers.monitoring.tls.certresolver=cloudflare"
- "traefik.http.services.monitoring.loadbalancer.server.port=3000"
networks:
- traefik_network