Command Palette

Search for a command to run...

Blog
PreviousNext

Secure Services with Traefik Proxy

Set up Traefik as a reverse proxy to route traffic, enable HTTPS, and manage multiple services easily.

Introduction

When running multiple services in a homelab or production environment, a reverse proxy is essential for centralizing traffic management, handling HTTPS certificates, and applying security policies. Traefik Proxy is a modern, cloud-native reverse proxy that makes this process efficient and flexible.

This guide demonstrates how to set up Traefik using configuration files (traefik.yml and dynamic.yml) instead of relying on Docker labels. Separating configuration into YAML files improves maintainability and makes it easier to add advanced security features.

Installation steps

Docker Compose Setup

In this step, we define the Traefik service using Docker Compose. The configuration mounts the static and dynamic files, as well as persistent volumes for certificates and logs. This ensures that Traefik is reproducible, secure, and does not lose state between restarts.

docker-compose.yml
services:
  traefik:
    container_name: traefik
    image: traefik:v3.4
    restart: unless-stopped
    environment:
      - TZ=Europe/Madrid
    ports:
      - "80:80"
      - "443:443"
    security_opt:
      - no-new-privileges:true
    volumes:
      - ./config/traefik.yml:/traefik.yml:ro
      - ./config/dynamic.yml:/dynamic.yml:ro
      - ./config/users.txt:/users.txt:ro
      - ./letsencrypt:/letsencrypt
      - ./traefik_logs:/var/lib/traefik
 
volumes:
  letsencrypt:
    driver: local
  traefik_logs:
    driver: local

Once this file is created, you can start Traefik by running:

docker compose up -d

This will download the Traefik image (if not already present), create the container, and attach the required volumes. The proxy will then be accessible on ports 80 (HTTP) and 443 (HTTPS). At this stage, Traefik is running but not yet configured to serve any specific service.

Static Configuration (traefik.yml)

The traefik.yml file defines the static configuration. These are settings Traefik reads only at startup, so they cannot be reloaded dynamically. Here we configure entry points, logging, metrics, certificate resolvers, and providers. The goal is to establish how Traefik should behave globally before it starts serving traffic.

Entry Points

We define two entry points: one for plain HTTP web that redirects all traffic to HTTPS, and one for secure traffic websecure.

traefik.yml
entryPoints:
  web:
    address: ":80"
    http:
      redirections:
        entryPoint:
          to: websecure
          scheme: https
          permanent: true
 
  websecure:
    address: ":443"
    http:
      tls:
        certResolver: production

Logging

Traefik produces two kinds of logs:

  • Service logs (internal status, warnings, errors).
  • Access logs (every request handled).
traefik.yml
log:
  level: INFO
  filePath: /var/lib/traefik/traefik.log
  format: common
 
accessLog:
  filePath: /var/lib/traefik/access.log
  format: common

Metrics

In a future tutorial, we’ll cover how to enable Prometheus metrics in Traefik. This provides visibility into routers, services, and traffic patterns.

traefik.yml
metrics:
  prometheus:
    addEntryPointsLabels: true
    addRoutersLabels: true
    addServicesLabels: true
    entryPoint: web

Providers

Traefik loads dynamic configuration (routers, services, middlewares) from a separate file dynamic.yml. With watch: true, it reloads automatically when the file changes.

traefik.yml
providers:
  file:
    filename: ./dynamic.yml
    watch: true

Certificates Resolvers

We configure Let’s Encrypt resolvers for automatic TLS certificate management. The production resolver requests real certificates, while staging is useful for testing.

traefik.yml
certificatesResolvers:
  production:
    acme:
      email: [email protected]
      storage: /letsencrypt/acme-production.json
      caServer: "https://acme-v02.api.letsencrypt.org/directory"
      httpChallenge:
        entryPoint: web
 
  staging:
    acme:
      email: [email protected]
      storage: /letsencrypt/acme-staging.json
      caServer: "https://acme-staging-v02.api.letsencrypt.org/directory"
      httpChallenge:
        entryPoint: web

Dynamic Configuration (dynamic.yml)

The dynamic.yml file defines the routing logic for Traefik. Unlike the static configuration, this file can be reloaded without restarting Traefik. It contains three main elements:

  • Routers: Match incoming requests (e.g., based on hostname).
  • Services: Define where the traffic should be forwarded (usually Docker containers).
  • Middlewares: Apply transformations or restrictions before requests reach the service.

Routers and Services

A router inspects incoming traffic and decides which service should handle it. Each service points to one or more backend servers.

dynamic.yml
http:
  routers:
    example-app:
      entryPoints:
        - websecure
      rule: "Host(`app.example.com`)"
      service: example-app
      middlewares: [auth, geoblock, torblock]
      tls:
        certResolver: production
 
  services:
    example-app:
      loadBalancer:
        servers:
          - url: "http://backend:8080"

In this example:

  • Requests to https://app.example.com are routed to the container named backend on port 8080.
  • The router enforces HTTPS via the websecure entry point.
  • Middlewares auth and geoblock are applied before traffic reaches the backend.

Enabling Authentication

To restrict access with Basic Authentication, you must create a users.txt file that stores usernames and bcrypt-hashed passwords.

Generate the file using htpasswd (from the apache2-utils package):

htpasswd -nbB admin strongpassword > config/users.txt

This produces an entry like:

users.txt
admin:$2y$05$zG6XyYx1rGc...

Then, reference it in your middleware configuration:

dynamic.yml
http:
  middlewares:
    auth:
      basicAuth:
        usersFile: users.txt

Any router that includes middlewares: [auth] will now prompt users for credentials before allowing access.

Security Plugins

Traefik can be extended with plugins that act as middlewares to filter or protect incoming requests. Plugin definitions live in the static configuration traefik.yml, while their usage and policies are applied in the dynamic configuration dynamic.yml.

In this setup, three plugins are particularly useful for hardening exposed services:

GeoBlock

Blocks or allows requests based on the client’s country of origin.

traefik
experimental:
  plugins:
    GeoBlock:
      moduleName: github.com/PascalMinder/geoblock
      version: v0.2.5
dynamic.yml
http:
  middlewares:
    geoblock:
      plugin:
        GeoBlock:
          api: "https://get.geojs.io/v1/ip/country/{ip}"
          countries:
            - US
            - ES
          allowLocalRequests: true

TorBlock

Denies requests originating from Tor exit nodes, preventing anonymous abuse.

traefik
experimental:
  plugins:
    torblock:
      moduleName: github.com/jpxd/torblock
      version: v0.1.1
dynamic.yml
http:
  middlewares:
    torblock:
      plugin:
        torblock:
          enabled: true

Apply this middleware to routes that should not be accessible from Tor.

ModSecurity Plugin

Integrates Traefik with a ModSecurity Web Application Firewall (WAF). This adds deep request inspection and protection against common web attacks (SQL injection, XSS, etc.).

docker-compose.yml
waf:
  container_name: waf
  image: owasp/modsecurity-crs:apache
  environment:
    - PARANOIA=1
    - ANOMALY_INBOUND=10
    - ANOMALY_OUTBOUND=5
    - BACKEND=http://dummy
 
dummy:
  container_name: dummy
  image: traefik/whoami
traefik
experimental:
  plugins:
    traefik-modsecurity-plugin:
      moduleName: github.com/acouvreur/traefik-modsecurity-plugin
      version: v1.3.0
dynamic.yml
http:
  middlewares:
    modsecurity:
      plugin:
        traefik-modsecurity-plugin:
          ModsecurityUrl: http://waf:8080/

Here, http://waf:8080 points to a companion WAF container that processes and filters traffic.

Conclusion

By separating static (traefik.yml) and dynamic (dynamic.yml) configuration files, Traefik deployments become cleaner, easier to manage, and more secure.

  • docker-compose.yml handles container lifecycle.
  • traefik.yml defines entry points, TLS certificates, and the file provider.
  • dynamic.yml manages routers, services, and middlewares.
  • Plugins such as GeoBlock, TorBlock, and ModSecurity further strengthen your reverse proxy setup.

This modular approach ensures that your Traefik setup can scale as you add more services, while keeping security and maintainability at the forefront.

Appendices

docker-compose.yml
services:
  traefik:
    container_name: traefik
    image: traefik:v3.4
    restart: unless-stopped
    environment:
      - TZ=Europe/Madrid
    ports:
      - "80:80"
      - "443:443"
    security_opt:
      - no-new-privileges:true
    volumes:
      - ./config/traefik.yml:/traefik.yml:ro
      - ./config/dynamic.yml:/dynamic.yml:ro
      - ./config/users.txt:/users.txt:ro
      - ./letsencrypt:/letsencrypt
      - ./traefik_logs:/var/lib/traefik
 
  waf:
    container_name: waf
    image: owasp/modsecurity-crs:apache
    environment:
      - PARANOIA=1
      - ANOMALY_INBOUND=10
      - ANOMALY_OUTBOUND=5
      - BACKEND=http://dummy
 
  dummy:
    container_name: dummy
    image: traefik/whoami
 
volumes:
  letsencrypt:
    driver: local
  traefik_logs:
    driver: local