Migrating from Traefik V1 to V2

As I’ve mentioned before, this blog runs a very simple set up, literally 2 containers, Traefik itself, and Ghost, the blog platform.  This means, that embarking on a migration from Traefik V1 to V2 for this blog was not such a daunting task!

I embarked on this project, and it was completed and up and running on V2 about an hour later!  A really impressive feat, primarily brought on by the simple and effective configuration and setup of Traefik itself.

Let’s briefly go over what changes I had to make in order to get this to work.

Here is a brief list of the sections, so that you can quickly jump to the section you’re interested in if you’d like:

The Traefik Service

In this section, I’ll briefly go over the changes I had to make to the Traefik service itself in the Docker Compose file:

Migrating the Traefik Docker Provider

In the v1 compose file, we had the following:

      - "--docker"
      - "--docker.swarmMode"
      - "--docker.watch"
      - "--api"

This enables the docker provider, sets it to Swarm mode, tells Traefik to watch for new events, and enables the Traefik API, which in turn enables the dashboard for Traefik.

In the v2 configuration, this becomes:

      - "--api=true"
      - "--providers.docker.swarmMode=true"
      - "--providers.docker=true"

Fairly similar, but a bit more self explanatory as it specifically mentions that you are enabling the Docker Swarm mode provider.

Migrating the Entrypoints

In Traefik v1 entrypoints are defined and used globally.  In v2 there is a lot more configurability, however i opted to keep the configuration as similar to my v1 configuration as possible, for the ease of migration.  As you can see we are declaring two entrypoints, one for http and one for https:

v1 config:

      - "--entrypoints=Name:http Address::80 Redirect.EntryPoint:https"
      - "--entrypoints=Name:https Address::443 TLS"
      - "--defaultentrypoints=http,https"

v2 config:

      - "--entrypoints.web.address=:80"
      - "--entrypoints.websecure.address=:443"
      - "--entrypoints.web.http.redirections.entrypoint.to=websecure"
      - "--entrypoints.web.http.redirections.entrypoint.scheme=https"

Migrating the Let’s Encrypt Config

This was the most challenging part for me, as I do not have a lot of experience with Let’s Encrypt and Traefik, however it ended up being a rather simple migration.

V1 config:

      - "--acme"
      - "--acme.storage=acme.json"
      - "--acme.entryPoint=https"
      - "--acme.onHostRule=false"
      - "--acme.onDemand=false"
      - "--acme.acmeLogging=true"
      - "--acme.email=myemail@mydomain.com"
      - "--acme.dnsChallenge.provider=route53"
      - "--acme.domains=blog.devinsmith.co.za"

V2 config:

      - "--certificatesresolvers.myresolver.acme.email=myemail@mydomain.com"
      - "--certificatesresolvers.myresolver.acme.storage=acme.json"
      - "--certificatesresolvers.myresolver.acme.dnschallenge=true"
      - "--certificatesresolvers.myresolver.acme.dnschallenge.provider=route53"
      - "--certificatesresolvers.myresolver.acme.dnschallenge.delaybeforecheck=0"

As we can see here, v2 uses certificate resolvers as opposed to a global acme configuration. This does make the name-spacing and other configuration items rather simple to understand.  As I have mentioned in previous posts, I use Route53 for the DNS configuration here, and thus I have configured this to use that provider.  

Note that I have configured and set the relevant environment variables needed for this to work in the environment variables for Traefik, these are AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY and AWS_HOSTED_ZONE_ID.

Enabling the Traefik Dashboard

As a further step in this move to V2, I wanted to have my Traefik dashboard now also be SSL encrypted, secured with authentication, and published on its own hostname.

Luckily, the V2 Traefik migration guide provides a very helpful section on doing exactly this, so I added the following labels to my Traefik service to enable this:

        - "traefik.http.services.api.loadbalancer.server.port=8080"
        - "traefik.http.routers.api.rule=Host(`myhost.mydomain.com`)"
        - "traefik.http.routers.api.entrypoints=websecure"
        - "traefik.http.routers.api.service=api@internal"
        - "traefik.http.routers.api.middlewares=myAuth"
        - "traefik.http.routers.api.tls=true"
        - "traefik.http.routers.api.tls.certresolver=myresolver"
        - "traefik.http.middlewares.myAuth.basicauth.users=user:${USER_AUTH_STRING}"

This automatically allowed the dashboard to be exposed on the hostname I had defined, and since I was already feeding in the relevant AWS environment variables, I was able to feed in USER_AUTH_STRING as another environment variable, so that I can easily modify/rotate the credentials if need be.  I detail how I am pushing in these environment variables in my Github Actions post here: https://blog.devinsmith.co.za/deploying-this-blog-with-github-actions/

Note that these are Traefik labels, on the Docker service in the compose file, and are NOT under the boot arguments for Traefik.  This is an important distinction.

The Blog Service

Now I’ll quickly go over how the configuration of the Ghost blog service itself needed to change in order to achieve this migration.

V1 labels:

        traefik.port: 2368
        traefik.blog.frontend.rule: Host:blog.devinsmith.co.za
        traefik.docker.network: traefik

V2 labels:

        - "traefik.http.routers.blog.tls=true"
        - "traefik.http.routers.blog.tls.certresolver=myresolver"
        - "traefik.http.routers.blog.rule=Host(`blog.devinsmith.co.za`)"
        - "traefik.http.services.blog.loadbalancer.server.port=2368"

As V2 moved to the concept of Routers (which you can read about extensively in their documentation: https://docs.traefik.io/) as such, we now need to tell Traefik which TLS resolver we want to use for this specific service, as we have the ability to declare multiple resolvers and use them independently.

I have also kept the declaration of what port Traefik should use to load balance this service. I believe this is something that V2 detects by default, however I have chosen to keep it in here as I find it provides a bit more clarity and understanding when glancing at the compose file.

Furthermore, there is only a single Docker network in play here, so I opted to exclude the declaration of the Docker network to use.

The Conclusion

Those were all the changes I needed to make in order to get Traefik updated to V2! Merging my changes into master of my git repo, and having the deploy pipeline take care of the rest, resulted in:

  • Working HTTP and HTTPS entrypoints with HTTP > HTTPS redirection.

Traefik V2 Entrypoints

  • Traefik dashboard secured with HTTP authentication, and also having SSL on it’s own independent hostname

Traefik V2 dashboard showing working dashboard service with TLS/authentication

  • Working blog with correct Let’s Encrypt SSL certificate.

Traefik V2 Dashboard showing working blog service with TLS enabled Everything is now happily running the latest v2.2.1 image from Traefik, and upon checking the logs for the service, there are no errors or problems that I can see.


  • As you can see, this is a very simplistic setup, I understand that there are vastly more complex setups to contend with in most situations, but it’s awesome to see that with Traefik you can set up, run and migrate to new versions of Traefik very easily, without too many issues.
  • Using this in combination with the Github actions workflow did allow me to get things working pretty quickly, and enabled quick iteration when I spotted problems with configuration
  • Traefik’s v1 to v2 migration doc was rather helpful here, however one does need to read the documentation (especially that around Let’s Encrypt) multiple times before it gets clearer where everything needs to be.
  • I realise there may be options that can be set differently, or different approaches to the same solution that I achieved here, however this was my own process, and I ended up with the intended result.
  • Thanks once again to the great folks over at Containous that develop and create Traefik, check them out! - https://containo.us/traefik/

I use Unsplash for all my post header images, credits to Thomas Jensen (https://unsplash.com/photos/ISG-rUel0Uw) for this one.

This was a really fun side project that turned into a rather exciting post for me!

Let me know if you have suggestions or ideas around how I can extend and improve what is happening here!

Thanks again for reading.


comments powered by Disqus