Configuration differences between traefik v2 and traefik v1 (with the docker provider)

traefik logo

Introduction

Traefik v2 is out! It has already been released for a few months but it’s never too late to jump in.

This new version is quite a big one. It broke all my existing configurations and got myself all over the new documentation. As it was a bit of a pain for me, here is a tutorial with what I learned when migrating from traefik v1 to v2.

What’s new

I will try to be as concise as possible and sum up what’s new (this might come with some shortcuts). What’s written below should be considered only when using the docker provider.

Static vs dynamic configuration

The first things that lost me at the beginning (but which is very important nevertheless) is the difference between static and dynamic configuration.

  • Static configuration refers to configuration which is loaded on startup. When using docker, this configuration can be loaded from either a .toml file or from flags passed on the traefik container (in the command directive). For instance, entrypoints must be defined at the very beginning and are part of the static configuration.
  • Dynamic configuration refers to any configuration that traefik watches. Traefik does not have to restart to apply it. With the docker provider, this configuration could come from .toml files or from docker labels. In my opinion it is very easy to get confused with .toml files (detecting whether it’s dynamic or static is not obvious). As such I prefer to write every configuration in labels and flags. Plus this way, everything can be found in docker-compose files. Services are an example of what can be configured dynamically. They can scale which is a good indicator that they are part of what can be dynamically configured. Middleware can be added or removed on the fly ; they are also part of the dynamic configuration.

Providers, router, services and middleware

New concepts in a few words:

  • providers are infrastructure components (in this case docker and/or docker swarm) leveraged by traefik to handle service discovery
  • routers treat requests according to some rules
  • services serve the request
  • middleware modify the request

With this in mind, you will need to define routers attached to entrypoints, then attach middleware to these routers (middleware are anything between the entrypoint and the service ; basic authentication, redirections, etc.)

With the docker provider, you (almost) do not have to take care of services as treafik automatically creates and assigns these services to the router. The only thing you have to do within a swarm is to specify which container port to target.

Setup traefik

The dashboard

Let’s create a docker-compose.yml file with the following content. You will find configuration for both traefik version 1.7 and version 2.1 so it’s easy to compare and find differences.

  • version: '3.7'
    
    services:
    
      traefik:
        image: traefik:v2.1
        ports:
          - 80:80
        command:
          - --entrypoints.http.address=:80
          - --providers.docker.exposedByDefault=false
          - --log.level=DEBUG
          - --api=true
          - --api.dashboard=true
        labels:
          traefik.enable: 'true'
          traefik.http.routers.traefik-dashboard-http-router.entrypoints: http
          traefik.http.routers.traefik-dashboard-http-router.rule: Host(`traefik-ui.local`)
          traefik.http.routers.traefik-dashboard-http-router.service: api@internal
        volumes:
          - /var/run/docker.sock:/var/run/docker.sock
    
  • version: '3.7'
    
    services:
    
      traefik:
        image: traefik:v1.7
        ports:
          - 80:80
        command:
          - --entrypoints=Name:http Address::80
          - --docker
          - --docker.exposedbydefault=false
          - --logLevel=DEBUG
          - --api
        labels:
          traefik.port: 8080
          traefik.frontend.rule: Host:traefik-ui.local
          traefik.enable: 'true'
        volumes:
          - /var/run/docker.sock:/var/run/docker.sock
    

Many lines could be defaulted, but this way you do not rely on traefik “magic” and we know what we are doing.

This configuration sets up traefik with its dashboard accessible in http on the standard port (80) on domain traefik-ui.local. You can add this entry to your local dns (/etc/hosts on linux) to try it. Containers are not enabled by default, so we have to explicitly set the label traefik.enable to true to let traefik handle the traffic between internet and this container.

What about api@internal? Well I don’t know for now which is why I created a post on the community forum.

Run docker-compose up and open http://traefik-ui.local/ to access the dashboard.

Basic auth

You might not want to have this dashboard accessible from everyone. Let’s add basic authentication.

$ htpasswd -c file.htpasswd traefik_user
$ cat file.htpasswd
> traefik_user:$apr1$epoKf5li$QfTMJZOCS/halv3CiIUEu0

The above hashed password is password

In traefik v2, you need to create a basicauth middleware, and assign it to the router you want.

  • ...
    labels:
      ...
      traefik.http.middlewares.traefik-dashboard-auth.basicauth.users: traefik_user:$$apr1$$epoKf5li$$QfTMJZOCS/halv3CiIUEu0
      traefik.http.routers.traefik-dashboard-http-router.middlewares: traefik-dashboard-auth
    
  • ...
    labels:
      ...
      traefik.frontend.auth.basic.users: traefik_user:$$apr1$$epoKf5li$$QfTMJZOCS/halv3CiIUEu0
    

Be careful, in yaml you have to double the ‘$’ signs to escape them.

Run docker-compose up and refresh the dashboard to see the login form popping up.

🐳 github links (v2.1 and v1.7) to the full docker-compose files

Nginx backend container

Now let’s handle some traffic. In the same docker-compose.yml file, we create a nginx service:

If your backend containers and your traefik container are configured in separate docker-compose.yml files, make sure they have one network in common!

  • ...
    services:
    
      traefik:
        ...
    
      nginx:
        image: nginx:1.17.8-alpine
        labels:
          traefik.enable: 'true'
          traefik.http.routers.nginx-http-router.entrypoints: http
          traefik.http.routers.nginx-http-router.rule: Host(`nginx.local`)
          traefik.http.services.nginx-service.loadbalancer.server.port: 80
    
    
  • ...
    services:
    
      traefik:
        ...
    
      nginx:
        image: nginx:1.17.8-alpine
        labels:
          traefik.enable: 'true'
          traefik.frontend.rule: Host:nginx.local
          traefik.frontend.entryPoints: http
          traefik.port: 80
    
    

🐳 github links (v2.1 and v1.7) to the full docker-compose files

In details:

  • traefik.enable tells traefik to handle service discovery on this container, as containers are not exposed by default.
  • traefik use a router to route the traffic to our container. This router matches when http connections (entrypoints directive) are made towards nginx.local (rule directive). Add an entry in your /etc/hosts file to have this domain name resolving in local.
  • traefik redirect traffic on port 80 of the container.

Https support

Currently, our configuration support only the http entrypoint. The following diagram pictures how http and https requests can be resolved using traefik new concepts. This will allow TLS termination along with http to https redirection on the dashboard.

traefik dashboard flow diagram

Let’s update the configuration accordingly:

  • version: '3.7'
    
    services:
    
      traefik:
        image: traefik:v2.1
        ports:
          - 80:80
          - 443:443
        command:
          - --entrypoints.http.address=:80
          - --entrypoints.https.address=:443
          - --providers.docker.exposedByDefault=false
          - --log.level=DEBUG
          - --api=true
          - --api.dashboard=true
        labels:
          traefik.enable: 'true'
          traefik.http.routers.traefik-dashboard-http-router.entrypoints: http
          traefik.http.routers.traefik-dashboard-http-router.rule: Host(`traefik-ui.local`)
          traefik.http.middlewares.traefik-dashboard-redirect.redirectscheme.scheme: https #1
          traefik.http.routers.traefik-dashboard-http-router.middlewares: traefik-dashboard-redirect #2
          traefik.http.middlewares.traefik-dashboard-auth.basicauth.users: traefik_user:$$apr1$$epoKf5li$$QfTMJZOCS/halv3CiIUEu0
          traefik.http.routers.traefik-dashboard-https-router.middlewares: traefik-dashboard-auth #3
          traefik.http.routers.traefik-dashboard-https-router.service: api@internal #4
          traefik.http.routers.traefik-dashboard-https-router.entrypoints: https #5
          traefik.http.routers.traefik-dashboard-https-router.tls: 'true' #6
          traefik.http.routers.traefik-dashboard-https-router.rule: Host(`traefik-ui.local`) #7
          traefik.http.services.traefik-dashboard-service.loadbalancer.server.port: 9999 #8
        volumes:
          - /var/run/docker.sock:/var/run/docker.sock
    
  • version: '3.7'
    
    services:
    
      traefik:
        image: traefik:v1.7
        ports:
          - 80:80
          - 443:443
        command:
          - --entrypoints=Name:http Address::80
          - --entrypoints=Name:https Address::443 TLS
          - --docker
          - --docker.watch
          - --docker.exposedbydefault=false
          - --logLevel=DEBUG
          - --api
        labels:
          traefik.enable: 'true'
          traefik.port: 8080
          traefik.frontend.rule: Host:traefik-ui.local
          traefik.frontend.auth.basic.users: traefik_user:$$apr1$$epoKf5li$$QfTMJZOCS/halv3CiIUEu0
          traefik.frontend.redirect.entryPoint: https
          traefik.frontend.entryPoints: http,https
        volumes:
          - /var/run/docker.sock:/var/run/docker.sock
    

🐳 github links (v2.1 and v1.7) to the full docker-compose files

As you can see, configuring https with redirection on version v2.1 is way more verbose than on version 1.7. At a glance I thought it was unecessarily complicated, but now I think I prefer this new approach. Every line has its purpose, much less gets done under the hood without the user knowledge.

  • The first thing we added here (#1) is the traefik-dashboard-redirect middleware. It’s a redirectscheme middelware, redirecting traffic to https.
  • Then we attached this middleware to the traefik-dashboard-http-router router (#2)
  • Now than http is redirected to https, it’s useless to protect the http route with basic authentication. We keep the labels related to basic authentication, but we attach the middleware to the traefik-dashboard-https-router router instead (#3).
  • We also move the service api@internal from the http router to the https router (#4).
  • We set the traefik-dashboard-https-router router entrypoint to https obviously (#5).
  • Then we tell traefik that router traefik-dashboard-https-router will take care of tls termination (#6). Traefik will generate self-signed default certificates as we didn’t specify a way to resolve them.
  • Our https router will match the exact same rule as the http one, i.e. traefik-ui.local domain name (#7)
  • Finally we have to tell traefik to load balance the traffic on a dummy port. Without it, you will get an error such as ‘service "traefik-traefik-v2" error: port is missing”’. This part is applicable only for the dashboard as it uses an internal service (an issue is still opened here on this subject.

Nginx backend container with https

Following the same principles, we can handle tls termination and https redirection for the nginx backend. The configuration is very similar as the previous one - basic auth middleware being discarded.

nginx traefik flow diagram

Here is the resulting yaml:

  • ...
    services:
    
      traefik:
        ...
    
      nginx:
        image: nginx:1.17.8-alpine
        labels:
          traefik.enable: 'true'
          traefik.http.services.nginx-service.loadbalancer.server.port: 80
          traefik.http.middlewares.nginx-redirect.redirectscheme.scheme: https
          traefik.http.routers.nginx-http-router.entrypoints: http
          traefik.http.routers.nginx-http-router.rule: Host(`nginx.local`)
          traefik.http.routers.nginx-http-router.middlewares: nginx-redirect
          traefik.http.routers.nginx-https-router.entrypoints: https
          traefik.http.routers.nginx-https-router.tls: 'true'
          traefik.http.routers.nginx-https-router.rule: Host(`nginx.local`)
    
  • ...
    services:
    
      traefik:
        ...
    
      nginx:
        image: nginx:1.17.8-alpine
        labels:
          traefik.enable: 'true'
          traefik.frontend.rule: Host:nginx.local
          traefik.frontend.entryPoints: http
          traefik.port: 80
    
    

🐳 github links (v2.1 and v1.7) to the full docker-compose files

Let’s encrypt support

Traefik can issue let’s encrypt certificates for you. You only have to choose how trust is going to be established via what is called a challenge.

  • We define a certificate resolver on startup thanks to command flags
    • Certificates will be stored in /etc/traefik/acme.json (a volume is defined to persist this file)
    • The challenge used here will be the most common challenge: HTTP-01 challenge
  • For each service reachable on a domain name for which we can generate a certificate, we set the name of certificate resolver to be used (letsencrypt_resolver here)
  • volumes:
      conf:
    
    ...
    services:
    
      traefik:
        ...
        command:
          ...
          - --certificatesResolvers.letsencrypt_resolver.acme.email=you@mail.com
          - --certificatesResolvers.letsencrypt_resolver.acme.storage=/etc/traefik/acme.json
          - --certificatesResolvers.letsencrypt_resolver.acme.httpChallenge.entryPoint=http
        labels:
          ...
          traefik.http.routers.traefik-dashboard-https-router.tls.certresolver: letsencrypt_resolver
      volumes:
        - conf:/etc/traefik
    
      nginx:
        image: nginx:1.17.8-alpine
        labels:
          ...
          traefik.http.routers.nginx-https-router.tls.certresolver: letsencrypt_resolver
    
  • volumes:
      conf:
    
    ...
    services:
    
      traefik:
        ...
        command:
          ...
          - --acme.email=you@mail.com
          - --acme.storage=/etc/traefik/acme.json
          - --acme.entryPoint=http
          - --acme.onHostRule=true
          - --acme.httpchallenge.entrypoint=http
      volumes:
        - conf:/etc/traefik
    

🐳 github links (v2.1 and v1.7) to the full docker-compose files

If migrating from traefik v1.7 to traefik v2, you will need to convert the old acme.json file to a new version using the traefik-migration-tool.

# change the url depending on your specs
cd /path/to/cert/traefik/folder
curl -L "https://github.com/containous/traefik-migration-tool/releases/download/v0.12.1/traefik-migration-tool_v0.12.1_linux_amd64.tar.gz" -o traefik-migration-tool.tar.gz
mkdir migration-tool
tar xvzf traefik-migration-tool.tar.gz -C migration-tool/
cd migration-tool
./traefik-migration-tool acme -i /path/to/acme.json -o new_acme.json --resolver letsencrypt_resolver
mv new_acme.json /path/to/acme.json

What about swarm?

With everything you have, using traefik v2 in a docker swarm is a breeze.

In the traefik command:

  • Enable swarm mode on the docker provider
  • Set the common network shared between containers with traefik labels. This network must be created with the overlay driver (docker network create --driver=overlay traefik)
  • Set the swarm endpoint and the tls certificates if your swarm protects the docker daemon socket.
  • ...
    services:
    
      traefik:
        ...
        command:
          ...
          - --providers.docker.swarmMode=true
          - --providers.docker.network=traefik
          # optional below
          - --providers.docker.endpoint=tcp://swarm.manager.net:5555
          - --providers.docker.tls.ca=/etc/traefik/certs/ca.pem
          - --providers.docker.tls.cert=/etc/traefik/certs/cert.pem
          - --providers.docker.tls.key=/etc/traefik/certs/key.pem
        networks:
          - traefik
        deploy:
          placement:
            constraints: [node.role == manager]
    
    networks:
    
      traefik:
        driver: overlay
        external: true
        attachable: true
    
    

And then, on the backends do not forget the port

  • ...
    services:
    
      nginx:
        ...
        deploy:
          labels:
            ...
            traefik.http.services.nginx-service.loadbalancer.server.port: 80
        networks:
          - traefik
    
    networks:
    
      traefik:
        driver: overlay
        external: true
        attachable: true
    
  • ...
    services:
    
      backend_service:
        ...
        networks:
          - traefik
    
    networks:
    
      traefik:
        driver: overlay
        external: true
        attachable: true
    

🐳 github links (v2.1 and v1.7) to the full docker-compose files

That is everything you need!

If you found this tutorial helpful, star this repo as a thank you! ⭐