Traefik 2.0 upgrade with Docker Compose

So apparently I wasn’t the only one who woke up one day to find everything offline because Traefik 2 had been released with breaking changes because I was running Watchtower and using the Traefik:latest tag.

I was also not the only one to quickly try and fix the issue, but Traefik 2 was quite a significant change, and the issue was not going to solved in a few mins. It was rollback time for many people, including myself, particularly with the lack of decent documentation available at the time.

Anyway, yesterday I was quite sick and at home, and decided to attempt this upgrade. I mean, how hard can it be? Not that hard to be honest, I was done in about 3hrs.

Here’s what you need to do in 2 simple steps

  1. Run the traefik-migration-tool to convert your Letsencrypt ACME config and your static config to 2.0 format.
  2. Update your docker compose file based on the new Traefik architecture (routers, middleware and services)

I won’t go over step 1, as this is very simple and the tool provides enough help – short version is you just run the tool against the acme.json file and traefik.toml file to get new v2 compatible versions. I had some warnings about SSL redirects, which were previous defined in the entrypoint but are now handled by middleware.

Step 2, however, is where things get a little tricker due to the new architecture of Traefik. There is a v1 to v2 migration guide, which helped a lot, and a blog post on version 2, but there were still things that were not well explained in the guide, which is what I’d like to highlight here.

New Traefik Architecture

First, lets look at a couple of examples, all of which can be found either in the current version, or previous versions, of my docker-compose.yml on Github.

Traefik Image

This is the labels section from the Traefik docker container (nothing else changed).

    labels:
      - traefik.enable=true
      - traefik.http.routers.traefik.rule=Host(`traefik.${DOMAINNAME}`)
      - traefik.http.routers.traefik.entrypoints=http
      - traefik.http.routers.traefik-secure.rule=Host(`traefik.${DOMAINNAME}`)
      - traefik.http.routers.traefik-secure.entrypoints=https
      - traefik.http.routers.traefik-secure.tls=true
      - traefik.http.routers.traefik-secure.middlewares=auth
      - [email protected]
      - traefik.http.services.traefik.loadbalancer.server.port=8080
      - traefik.http.middlewares.sslredirect.redirectscheme.scheme=https
      - traefik.http.middlewares.auth.basicauth.users=${HTTP_USERNAME}:${HTTP_PASSWORD}

Let’s break this down section by section.

    labels:
      - traefik.enable=true
      - traefik.http.routers.traefik.rule=Host(`traefik.${DOMAINNAME}`)
      - traefik.http.routers.traefik.entrypoints=http
      - traefik.http.routers.traefik.middlewares=sslredirect
      - traefik.http.routers.traefik-secure.rule=Host(`traefik.${DOMAINNAME}`)
      - traefik.http.routers.traefik-secure.entrypoints=https
      - traefik.http.routers.traefik-secure.tls=true

Here, I am enabling traefik to route traffic, because I have “exposedByDefault = false” set in my traefik.toml. I am then defining two routers, with a rule to match by hostname, their entry points, and enabling TLS on the secure one. I am also telling the non-secure one to send traffic to my sslredirect middleware (defined further down).

      - traefik.http.routers.traefik-secure.middlewares=auth
      - [email protected]
      - traefik.http.services.traefik.loadbalancer.server.port=8080

Here, I am telling the secure router to pass traffic through an authentication middleware (defined below), and to send traffic to Traefik’s own API (expose the Web UI). The last line is how you now define the port to send traffic to, which stumped me for a while.

      - traefik.http.middlewares.sslredirect.redirectscheme.scheme=https
      - traefik.http.middlewares.auth.basicauth.users=${HTTP_USERNAME}:${HTTP_PASSWORD}

Lastly, we are actually defining two different middleware, which can be reused on other containers: one for redirecting non-SSL traffic, named “sslredirect” by giving it a scheme of https, and the second “auth” middleware defines an authentication middleware .

Now, we have Traefik up and running with an accessible Web UI. Now all we need to do now is set Traefik up to pass traffic through to other containers you may have running.

I’ll use Transmission as an example of one container I have running, which Traefik passes HTTPS traffic to.

  transmission:
    image: linuxserver/transmission
    container_name: transmission
    environment:
      - PUID=${PUID}
      - PGID=${PGID}
      - TZ=${TZ}
      - TRANSMISSION_WEB_HOME=/transmission-web-control/
    volumes:
      - ${USERDIR}/docker/transmission:/config
      - ${USERDIR}/TransmissionDL:/downloads
    ports:
      - 55700:55700
      - 9091:9091
      - 55700:55700/udp
    restart: unless-stopped
    networks:
      - traefik_proxy
      - default
    labels:
      - traefik.enable=true
      - traefik.http.routers.transmission.rule=Host(`transmission.${DOMAINNAME}`)
      - traefik.http.routers.transmission.entrypoints=http
      - traefik.http.routers.transmission.middlewares=sslredirect
      - traefik.http.routers.transmission-secure.rule=Host(`transmission.${DOMAINNAME}`)
      - traefik.http.routers.transmission-secure.entrypoints=https
      - traefik.http.routers.transmission-secure.tls=true
      - traefik.http.services.transmission.loadbalancer.server.port=9091
      - traefik.docker.network=traefik_proxy

In this example, the only section that changed between Traefik 1.7 and 2.0 is the labels section. Hopefully, everything is self-explanatory after the above explanation. We are just defining two routers, one with an sslredirect middleware, and setting the port on the router depending on your container.

That’s it, rinse and repeat, or look at my docker-compose.yml for more examples.

New Blog

It’s been a while since I have blogged – probably back before 2003 if I recall correctly. Since then, I have been using Twitter a fair bit when I get the urge. I feel there’s a lower barrier to Twitter and it’s a lot easier to write a 140 character tweet than it is to write a blog post.

Anyway, I’ve decided to try my hand at blogging again. So here it is people – lean and mean. Until I add more posts and fix up the template to make it aesthetically pleasing.