Traefik

Getting started with Traefik 2 can be a handful (…even if you are migrating from v1). It’s high configurability, an element that makes Traefik a truly powerful reverse proxy, also makes comparability across setup guides difficult. And since no two systems are seldom alike, multiple guides often need referencing to complete a setup. The process is supposed to be easy, it’s certainly billed as such….

“Traefik is an open-source Edge Router that makes publishing your services a fun and easy experience”

…but hands-on can be a time-consuming and frustrating experience …unless you understand the underlying configuration architecture. Traefik’s own documentation in this respect is a bit muddled and as I have yet to see a clear and concise summary of this process online, I thought I’d try to provide one. What follows then will be more of a conceptual overview of Traefik’s configuration rather than a step-by-step guide. Some of the guides I have found helpful are listed at the end of this post and hopefully by then, digesting the relevant detail from those guides will be a little easier. Let’s begin.

The big hurdle with the Traefik 2 configuration is to understand two main concepts:

  1. The configuration is split between static and dynamic elements
  2. How and where the static and dynamic configurations are defined is determined by your infrastructure choices

Put another way, you have a static configuration bucket, a dynamic configuration bucket, and if you are using Docker, a container configuration bucket (via Docker Compose in this post). That’s three configuration buckets, and depending on your needs and preferences, these buckets may be represented in three separate configuration files, two files, or possibly even in a single Docker Compose file.

It sounds like a lot, but hopefully you’ll see it really is not. To get started, let’s set the stage with a basic initial Docker Compose file.

(traefik-docker-compose.yml)

services:
  traefik:
    container_name: traefik
    image: traefik:2.2
    restart: always
    ports:
      - 80:80
      - 443:443
    networks:
      - traefik
    volumes:
      - ${CONTAINERPATH}/traefik:/etc/traefik
      - ${CONTAINERPATH}/traefik/acme/acme.json:/acme.json
      - ${CONTAINERPATH}/traefik/traefik.yml:/traefik.yml 
      - ${CONTAINERPATH}/traefik/dynamic_config.yml:/config.yml:ro
    environment:
      - TZ=${TZ}

Pretty vanilla so far. Let’s put this aside for a bit and take a look at the static configuration.

Static Configuration

If you glossed over the image at the start of this post it’s worth your time viewing it again in detail (larger image here). You’ll notice the static configuration is essentially Traefik’s startup configuration. Entrypoints (the ports Traefik listens to) and providers (connections to the services you want Traefik to proxy) are defined here as well as other infrastructure and security related options (api, logs, certificationResolvers, etc…).

You have a few options in how to define the static configuration. The default method is to store the configuration as a standalone file in either the TOML or YAML formats (note: the expected file name is “traefik”, ex. traefik.yml).

Here’s a sample of what the contents of the static configuration file may look like:

Standalone Static File (traefik.yml)

log:
  level: DEBUG
  filepath: "/etc/traefik/log/traefik.log"
api:
  dashboard: true
  insecure: false  
  debug: true
entryPoints:
  web:
    address: ":80"
  websecure:
    address: ":443"
providers:
  docker:
    exposedByDefault: false
    endpoint: "tcp://dockerproxy:2375"
    network: "traefik"
    defaultRule: "Host(`{{ trimPrefix `/` .Name }}.example.com`)"
  file:
    filename: "/etc/traefik/dynamic_config.yml"
    watch: true
certificatesResolvers:
  namecheap:
    acme:
      email: anyname@anymail.com
      storage: "/etc/traefik/acme/acme.json"
      dnsChallenge:
        provider: namecheap
        delayBeforeCheck: 120
        resolvers:
          - "1.1.1.1:53"
          - "8.8.8.8:53"


TIP: The example above is in YAML but what if you are more familiar with TOML? Traefik’s documentation examples provide a toggle to let you see the same example in multiple formats.

Optionally, you can skip the stand alone file if you use either CLI arguments or environment variables to define the static configuration in your Docker Compose file.

Here is an abbreviated example for each method:

Docker CLI Arguments (traefik-docker-compose-staticCLI.yml)

services:
  traefik:
    container_name: traefik
    image: traefik:2.2
    restart: always
    ports:
      - ...
    networks:
      - ...
    volumes:
      - ...
    command:
      - "--log.level=DEBUG"
      - "--log.filepath=/etc/traefik/log/traefik.log"
      - "--api.insecure=false"
      - "--api.dashboard=true"
      - "--api.debug=true"
      - "--entryPoints.web.address=:80"
      - "--entryPoints.websecure.address=:443"
      - "--providers.docker"
      - "--providers.docker.exposedByDefault=false"
      - ...
    environment:
      - TZ=${TZ}



Docker Environment Variables - (traefik-docker-compose-staticENV.yml)

services:
  traefik:
    container_name: traefik
    image: traefik:2.2
    restart: always
    ports:
      - ...
    networks:
      - ...
    volumes:
      - ...
    environment:
      - TZ=${TZ}
      - TRAEFIK_LOG_LEVEL="DEBUG"
      - TRAEFIK_LOG_FILEPATH="/etc/traefik/log/traefik.log"
      - TRAEFIK_API_INSECURE="false"
      - TRAEFIK_API_DASHBOARD="true"
      - TRAEFIK_API_DEBUG="true"
      - TRAEFIK_ENTRYPOINTS_WEB_ADDRESS=":80"
      - TRAEFIK_ENTRYPOINTS_WEBSECURE_ADDRESS=":443"
      - TRAEFIK_PROVIDERS_DOCKER="true"
      - TRAEFIK_PROVIDERS_DOCKER_EXPOSEDBYDEFAULT="false"
      - ...

The above three methods to define a static configuration are functionally equivalent. This means the method you choose is laregly a matter of prefence. Just keep in mind they are mutually exclusive so you will need to pick one and stick with it.



Dynamic Configuration

The dynamic configuration tells Traefik how to process requests. In it, routers (a Traefik term) are defined and pick-up inbound requests (through the entrypoints) and direct them to the appropriate back-end service. A router is generally paired with a service/server. For example, a request for subdomainfoo.example.com is captured by Router1 (via a rule) then directed to Server1, while Router2 captures requests for subdomainfoobar.example.com and routes it to Server2. Middlewares (another Traefik term) can be defined to modify a request. A Middleware sits between the router and server and can perform a redirect (http to https), a header modification, an authentication, or something else. A list of all the available Middlewares can be found here.

Dynamic Configuration - Docker

When Docker is used as a service provider (recall a provider is defined in the static config) the dynamic configuration is defined by the labels associated with each container at the time of the container’s start up.

For example, to enable Traefik on a Nextcloud container in Docker Compose, the associated labels may look similar to the below:

services:
  nextcloud:
    container_name: nextcloud
    image: nextcloud
    restart: always
    volumes:
      - ...
    ports:
      - ...
    networks:
      - ...
    environment:
      - ...
    labels:
      - traefik.enable=true
      - traefik.http.routers.nextcloud-http.entrypoints=web
      - traefik.http.routers.nextcloud-http.rule=Host(`nextcloud.${DOMAINNAME}`)
      - traefik.http.routers.nextcloud-https.entrypoints=websecure
      - traefik.http.routers.nextcloud-https.rule=Host(`nextcloud.${DOMAINNAME}`)
      - traefik.http.routers.nextcloud-https.tls=true
      - traefik.http.routers.nextcloud-https.tls.certresolver=default
      - traefik.http.routers.nextcloud-http.middlewares=nextcloud-https@docker
      - traefik.http.middlewares.nextcloud-https.redirectscheme.scheme=https
      - traefik.http.middlewares.nextcloud-https.redirectScheme.permanent=true
      - ...

Decomposition:

  • - traefik.enable=true

    • Enables Traefik on the container and must be present
  • nextcloud-http & nextcloud-https

    • Two separately named routers * are defined. They use different entrypoints (naming convention up to you …both for routers & entrypoints)
  • traefik.http.routers.nextcloud-https.tls=true

    • Enables TLS on the nextcloud-https router
  • - traefik.http.routers.nextcloud-https.tls.certresolver=default

    • Assigns a certificate to the nextcloud-https router
  • - traefik.http.routers.nextcloud-http.middlewares=nextcloud-https@docker & - traefik.http.middlewares.nextcloud-https.redirectscheme.scheme=https

    • Taken together these create the https redirect

*NOTE: Traefik is generally able to resolve the service/server IP from Docker so a server specification (in relation to a router) is not needed in the labels. More detail here

Dynamic Configuration - File Based

If you are not using Docker (or another orchestrator or service registry) then the dynamic configuration needs to be defined using a standalone file. Like the static configuration file, this can be written in TOML or YAML. The first thing you’ll need to do is to tell Traefik where to find the dynamic configuration. This is accomplished by defining the filename and path (to the dynamic configuration) under the providers section in the static configuration.

(traefik.yml)

...
providers:
  file:
    filename: "/etc/traefik/dynamic_config.yml"
    watch: true



Having defined the path and filename, the dynamic configuration can now be entered. As per the example below, the basic structure will have sections to define any routers, services, and middlewares.

(dynamic_config.yml)

http:
  routers:
    router1-http:
      rule: "Host(`example.com`)" 
      entryPoints:
        - web
      middlewares:
        - https-redirect
    router2-https:
      rule: "Host(`example.com`)"
      entryPoints:
        - websecure
      service: site_name_example.com
      tls:
        certResolver: default
  services:
    site_name_example.com:
      loadBalancer:
        servers:
          - url: "http://172.2.2.100:90" 
  middlewares:
    https-redirect:
      redirectScheme:
        scheme: https
    ...



Wrapping it up

My goal here was to help make sense of the different parts of Traefik 2’s configuration and to show that while it may appear complicated, it really isn’t. If your own configuration now appears a little more understandable, or if you find pulling together a new configuration a little easier then I’m glad to have helped. If you have ideas for improvement…lemme know :-)

Here’s a few setup guides that I found useful: