Traefik 2.0 & Docker 101

Tips & Tricks the Documentation Doesn’t Tell You

Docker friends — Welcome!

Today we decided to dedicate some time to walk you through the 2.0 changes using practical & common scenarios. Hopefully, after having read this article, you’ll understand every concept there is to know, and you’ll keep learning by yourself, discovering tips & tricks to share with the community.

Before we go further, I’ll assume for this article that you already have a docker setup using Traefik 2.0. Since I like to use docker-compose files for basic demonstrations, I’ll use the following base compose file:

version: "3.3" services: traefik: image: "traefik:v2.0.0" command: - --entrypoints.web.address=:80 - --providers.docker=true ports: - "80:80" - "8080:8080" volumes: - "/var/run/docker.sock:/var/run/docker.sock:ro"

my-app:
image: containous/whoami:v1.3.0Full compose file available there.

Nothing fancy, we declare an entrypoint (web for port 80), enable the docker provider, attach our traefik container to the needed ports and make sure we can listen to Docker thought the socket. We also have an application my-app we’ll expose later.

Side Note: You can get the examples from our repository if you want to play with them. (Yes, we know how dangerous it can be to copy/paste some YAML :-))

Now that we’re all set, let’s start!

Let’s Enable the Dashboard!

Because we all enjoy seeing what we’re doing, we’ll first enable Traefik’s Dashboard in development mode, and all we need to do is add one argument to the Traefik command itself.

services: traefik: image: "traefik:v2.0.0" command: - --entrypoints.web.address=:80 - --providers.docker - --api.insecure # Don't do that in production # ...Full compose file available there.

There we are! By adding --api.insecure we’ve enabled the API along with the dashboard. But beware, in this first step, we’ve enabled the insecure development mode — Don’t do that in production!

Of course, we’ll see at the end of the article how to enable a secured dashboard, but for now, you can enjoy and see it on localhost:8080/dashboard/

My Application Handles Requests on "example.com"

If you only need to route requests to my-app based on the host, then attach one label to your container — That’s it!

services: my-app: image: containous/whoami:v1.3.0 labels: - traefik.http.routers.my-app.rule=Host(`example.com`)Full compose file available there.

Quick Explanation

In English, this label means, “Hey Traefik! (traefik.) This HTTP router (http.routers.) I call my-app (my-app.) must catch requests to example.com (rule=Host(`example.com`)).”

More Details (Optional Read)

Traefik 2.0 introduces the notion of Routers. Routers define the routes that connect your services to the requests, and you use rules to define what makes the connection. This is the reason why you see routers in the label, as well as rule.

Traefik 2.0 also introduces TCP support (in addition to the existing HTTP support). Since Traefik supports both protocols, it wants to know what kind of protocol you’re interested in, which explains the http keyword in the label.

My Application Listens on a Specific Port

What happens if your application listens on a different port than the default :80? Let’s say it listens on :8082. We’ll build on the previous example and add (again) one label.

services: my-service: image: containous/whoami:v1.3.0 command: - --port=8082 # Our service listens on 8082 labels: - traefik.http.routers.my-app.rule=Host(`example.com`) - traefik.http.services.my-app.loadbalancer.server.port=8082Full compose file available there.

Quick Explanation

In English, this label means, “Hey Traefik! (traefik.) This HTTP service (http.services.) I call my-app (my-app.) will load balance incoming requests between servers (.server) that listen on port 8082 (.port=8082).”

More Details (Optional Read)

Traefik 2.0 introduces the notion of Services. Services are the targets for the routes. They usually define how to reach your programs in your cluster. Services can have different types. The most common one is the LoadBalancer type. The LoadBalancer type is a round robin between all the available instances (called server). By default, Traefik considers that your program is available on the port exposed by the Dockerfile of your program, but you can change that by explicitly defining the port.

Since we specify only one service in the example, there is no need to define the target of the previously defined router explicitly.

Side Note: The—-port=8082 command is specific to our whoami application and has nothing to do with Traefik. It tells whoami to start listening on 8082, so we can simulate our use case.

I Need BasicAuth (Or Any Piece of Middleware)

Once Traefik has found a match for the request, it can process it before forwarding it to the service. In the following example, we’ll add a BasicAuth mechanism for our route. This is done with two additional labels.

services: my-svc: image: containous/whoami:v1.3.0 labels: - traefik.http.routers.my-app.rule=Host(`example.com`) - traefik.http.routers.my-app.middlewares=auth - traefik.http.middlewares.auth.basicauth.users=test:xxxFull compose file available there.

Quick Explanation

In English, the first label means, “Hey Traefik! (traefik.) My HTTP router I called my-app, remember? (http.routers.my-app.) I’d like to attach to it a piece of middleware named auth (.middlewares=auth).”

Of course, since we haven’t yet declared the auth middleware, we need to be a bit more explicit, so the second label means, “Hey Traefik! (traefik.) Let’s talk about an HTTP middleware (http.middlewares.) I call auth (auth.). It’s a BasicAuth middleware (basicauth.). Since you probably need users to know who can do what, here is the users list (.users=test:xxx).”

More Details (Optional Read)

Traefik 2.0 introduces the notion of Middleware. Middleware is a way to define behaviors and tweak the incoming request before forwarding it to the service. Since they act before the request is forwarded, they are attached to Routers. You can define middleware and reuse them as many times as you like (this is why you need to name them, in the example auth). There are many kinds of middleware, and BasicAuth is one of them. Each middleware has a different set of parameters to define their behaviors (in the example, we define the users list).

I Need HTTPS

With Traefik, enabling automatic certificate generation is a matter of 4 lines of configuration, and enabling HTTPS on your routes is a matter of 2 lines of configuration.

1 — Enabling Automatic Certificate Generation

We’ll introduce a little tip here — Since Traefik is launched as a container, we’ll attach labels to it for common configuration options. (What is specific to other containers will, of course, stay on other containers, we’re not messy people!)

services: traefik: image: "traefik:v2.0.0" command: - --entrypoints.websecure.address=:443 # ... - --certificatesresolvers.le.acme.email=my@email.com - --certificatesresolvers.le.acme.storage=/acme.json - --certificatesresolvers.le.acme.tlschallenge=true # ... ports: # ... - "443:443"Full compose file available there.

Quick Explanation

We’ve seen already the first command line given to Traefik. In English, it means, “I have an entrypoint (entrypoints.) I call websecure (websecure.) that uses port 443 (.address=:443).” And since Traefik now listens to 443, we need to tell Docker that it should bind external port 443 to our service’s port 443 ("443:443").

Now, the others are a bit trickier, but nothing crazy if you’ve had time to drink your coffee/tea. The first says, “I’d like a mechanism to generate certificates for me (certificatesresolvers.) that I’ll call le (le.). It’s an acme resolver (acme.), my account there is my@email.com (email=my@email.com).” (Disclaimer: not my real email address, don’t try it.)

The second says, “This mechanism named le I told you about, the acme stuff (certificatesresolvers.le.acme.), it will save the certificates in the file /acme.json (storage=/acme.json).”

And the third is our inner geek speaking, “Since this le mechanism I defined before (certificatesresolvers.le.acme.) supports different challenges for certificate generation, I’ll choose … the TLS challenge (tlschallenge=true).”

That was a bit more text than usual, but here we are: we have a fully functional mechanism to generate/renew certificates for us!

More Details (Optional Read)

Traefik 2.0 introduces the notion of CertificatesResolvers. Certificates resolvers are a system that handles certificate generation/renewal/disposal for you. They detect the hosts you’ve defined for your routers and get the matching certificates.

Currently, certificates resolvers leverage Let’s Encrypt to get certificates, and expect you to configure your account (which is basically your email address). In order to prove Let’s Encrypt that you’re the owner of the domains you’ll request certificates for, LE will give Traefik a challenge. There are multiple possible challenges, and we chose in the example the TLSChallenge. In the documentation, you’ll find a description for each other challenges (dnsChallenge and httpChallenge).

Know that advanced users can define multiple CertificatesResolvers using different challenges, and that they can use them to generate wildcards … but that’s a story we’ll talk about later :-)

2 — Enabling Automatic Certificate Generation

Now that we have a mechanism to generate certificates for us, let’s leverage it to enable HTTPS on our route. We’ll only need two labels!

my-app: image: containous/whoami:v1.3.0 labels: - traefik.http.routers.my-app.rule=Host(`example.com`) - traefik.http.routers.my-app.middlewares=auth - traefik.http.routers.my-app.tls.certresolver=le - traefik.http.routers.my-app.entrypoints=websecureFull compose file available there.

Quick Explanation

In English, the first label means, “Hey Traefik! (traefik.) My HTTP router (http.routers.) I call my-app (my-app.) uses TLS and the CertificateResolver named le (certresolver=le).”

And the second says, “Traefik! (traefik.) this router, you know? (http.routers.my-app) It will only listen to the entrypoint I call websecure (entrypoints=websecure).”

More Details (Optional Read)

Traefik 2.0 allows you to define TLS termination directly on your routers!

Also, by default, routers listen to every known entrypoints. In our example, we wanted Traefik to limit the use of https on port 443, which is the reason why we told the router to listen only to websecure (defined to port 443 with entrypoints.websecure.address=:443)

I Want HTTPS Redirection!

Now that we have HTTPS routes, let’s redirect every non-https requests to their https equivalent. For that, we’ll reuse the previous trick and add just 4 labels to declare a redirect middleware and a catch-all router for unsecured routes.

services: traefik: image: "traefik:v2.0.0" # ... labels: # ...
  # middleware redirect
  - "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https"
  
  # global redirect to https
  - "traefik.http.routers.redirs.rule=hostregexp(`{host:.+}`)"
  - "traefik.http.routers.redirs.entrypoints=web"
  - "traefik.http.routers.redirs.middlewares=redirect-to-https"Full compose file available there.<h2>Quick Explanation</h2>

In English, the first label means, “Hey Traefik! (traefik.) let’s declare an HTTP middleware (http.middlewares.) we’ll call redirect-to-https (redirect-to-https.). It’s a RedirectScheme middleware (redirectscheme.) that will force the scheme to https (scheme=https).”

Then, let’s see the router part, “Hey Traefik! (you know the drill) (traefik.) I have an HTTP router (http.routers.) I’ll call redirs (redirs.) that will match requests on any host (rule=hostregexp(`{host:.+}`)) Yes sir! I’m insane and will catch everything, that’s how greedy I am.”

Then, we add, “Hey Traefik! (traefik.) I was kidding … the redirs HTTP router (http.routers.redirs.) won’t catch everything but just requests on port 80 (entrypoints=web).”

Finally, we’ll add the redirect middleware to the router. “Traefik? (traefik.) On the redirs HTTP router (http.routers.redirs.) we’ll add the redirect-to-https middleware (middlewares=redirect-to-https).”

More Details (Optional Read)

By now, we’ve seen everything there is to know, so no additional details to learn about :-)

Compiling Everything for a Secured Dashboard!

Now that we’ve manipulated every important notion (Entrypoints, Routers, Middleware, Services, CertificatesResolvers & TLS), we can combine them to obtain a secured Dashboard!

version: "3.3" services: traefik: image: "traefik:v2.0.0" command: - --entrypoints.web.address=:80 - --entrypoints.websecure.address=:443 - --providers.docker - --api - --certificatesresolvers.le.acme.email=your@email.com - --certificatesresolvers.le.acme.storage=/acme.json - --certificatesresolvers.le.acme.tlschallenge=true ports: - "80:80" - "443:443" volumes: - "/var/run/docker.sock:/var/run/docker.sock:ro" - "./acme.json:/acme.json" labels: # Dashboard - "traefik.http.routers.traefik.rule=Host(`api.example.com`)" - "traefik.http.routers.traefik.service=api@internal" - "traefik.http.routers.traefik.middlewares=admin" - "traefik.http.routers.traefik.tls.certresolver=le" - "traefik.http.routers.traefik.entrypoints=websecure" - "traefik.http.middlewares.admin.basicauth.users=admin:xxx" # ...Full compose file available there.

Quick Explanation

First, we remove the insecure api (specifying --api instead of --api.insecure).

Then, we tell Traefik (traefik.) to add an HTTP router called traefik (http.routers.traefik.) catching requests on api.example.com (rule=Host(`api.example.com`)).

This router (traefik.http.routers.traefik.) will forward requests to a service called api@internal (service=api@internal), uses a middleware named admin (middlewares=admin), and uses tls (tls=true) with a certresolver called le (tls.certresolver=le).

Finally, we declare the admin middleware (traefik.http.middlewares.admin.basicauth.users=admin:xxx).

More Details (Optional Read)

The only subtle thing to know is that when you enable the api (in default mode, it creates an internal service called api@internal (It’s then up to you to properly secure it).

Questions? Where to Go Next?

Hopefully, we’ve gone through important questions you’ll have when dealing with Traefik 2.0 in a Docker setup, and we hope this article brings many answers.

If you want to keep the conversation going, let us know on the community forum!

In the meantime — Happy Traefik!


This is a companion discussion topic for the original entry at https://traefik.io/blog/traefik-2-0-docker-101-fc2893944b9d/
2 Likes

Very nice article, this clarified a lot to me. Note however that when you use bascauth on multiple containers, you have to create a separate middleware with a distinct name for every container. This was not directly clear to me, so on every docker-compose you have to add traefik.http.routers..middlewares=auth_something_that_makes_this_unique.

This is wrong: the definition must be unique but you can re-use the same middleware on multiple containers.

The middleware definition:

The name of the middleware must be unique (ex: auth)

- "traefik.http.middlewares.auth.basicauth.users=user:$$apr1$$q8eZFHjF$$Fvmkk//V6Btlaf2i/ju5n/"

The usage

The values referenced by traefik.http.routers.xxx.middlewares don't need to be unique

  my-app1:
    image: containous/whoami:v1.4.0
    labels:
      - "traefik.http.routers.my-app1.rule=Host(`app1.docker.localhost`)"
      - "traefik.http.routers.my-app1.middlewares=auth"
      - "traefik.http.routers.my-app1.entrypoints=web"

  my-app2:
    image: containous/whoami:v1.4.0
    labels:
      - "traefik.http.routers.my-app2.rule=Host(`app2.docker.localhost`)"
      - "traefik.http.routers.my-app2.middlewares=auth"
      - "traefik.http.routers.my-app2.entrypoints=web"

A full example:

docker-compose.yml
version: "3.7"

services:
  traefik:
    image: traefik:v2.1.2
    command:
      - --entrypoints.web.address=:80
      - --entrypoints.websecure.address=:443
      - --providers.docker
      - --api
      - --certificatesresolvers.leresolver.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory
      - --certificatesresolvers.leresolver.acme.email=your@email.com
      - --certificatesresolvers.leresolver.acme.storage=/acme.json
      - --certificatesresolvers.leresolver.acme.tlschallenge=true
    ports:
      - 80:80
      - 443:443
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./acme.json:/acme.json
    labels:
      # Dashboard
      - "traefik.http.routers.traefik.rule=Host(`traefik.docker.localhost`)"
      - "traefik.http.routers.traefik.service=api@internal"
      - "traefik.http.routers.traefik.tls.certresolver=leresolver"
      - "traefik.http.routers.traefik.entrypoints=websecure"
      - "traefik.http.routers.traefik.middlewares=authtraefik"
      - "traefik.http.middlewares.authtraefik.basicauth.users=user:$$apr1$$q8eZFHjF$$Fvmkk//V6Btlaf2i/ju5n/" # user/password
      
      # global redirect to https
      - "traefik.http.routers.http-catchall.rule=hostregexp(`{host:.+}`)"
      - "traefik.http.routers.http-catchall.entrypoints=web"
      - "traefik.http.routers.http-catchall.middlewares=redirect-to-https"

      # middleware redirect
      - "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https"

      # app auth middleware
      - "traefik.http.middlewares.auth.basicauth.users=user:$$apr1$$q8eZFHjF$$Fvmkk//V6Btlaf2i/ju5n/" # user/password

  my-app1:
    image: containous/whoami:v1.4.0
    labels:
      - "traefik.http.routers.my-app1.rule=Host(`app1.docker.localhost`)"
      - "traefik.http.routers.my-app1.middlewares=auth"
      - "traefik.http.routers.my-app1.entrypoints=websecure"
      - "traefik.http.routers.my-app1.tls=true"
      - "traefik.http.routers.my-app1.tls.certresolver=leresolver"

  my-app2:
    image: containous/whoami:v1.4.0
    labels:
      - "traefik.http.routers.my-app2.rule=Host(`app2.docker.localhost`)"
      - "traefik.http.routers.my-app2.middlewares=auth"
      - "traefik.http.routers.my-app2.entrypoints=websecure"
      - "traefik.http.routers.my-app2.tls=true"
      - "traefik.http.routers.my-app2.tls.certresolver=leresolver"
1 Like

Nice topic there.

Though i have a question regarding "Key-value store configuration" is it active also in the new version of Traefik like V1.7?

Could this post be expanded to cover non-www to www redirection and setting a minimum TLS version please?

Great article! I noticed a mistake in a section title, and wanted to let you know in case someone else got a bit confused.

Under "I Need HTTPS", you state that you will have two subsections: "enabling automatic certificate generation" and "enabling HTTPS on your routes". However, the actual names of the two subsections are the same, " Enabling Automatic Certificate Generation". I believe that you intended the second name to be "Enabling HTTPS on your Routes".

Thanks!

1 Like

Thanks for the feedback! We'll take a look. :smile:

It'd be great for this with swarm and DNS resolving... :slight_smile:

The non-www to www redirection I covered here as a community contribution blog post: Redirect non-www to www Traefik v2

Great post, thank you!
I tried to configure the HTTPS redirection described in the section "I Want HTTPS Redirection" but it complains about a missing service.

So instead of

  # middleware redirect
  - "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https"
  
  # global redirect to https
  - "traefik.http.routers.redirs.rule=hostregexp(`{host:.+}`)"
  - "traefik.http.routers.redirs.entrypoints=web"
  - "traefik.http.routers.redirs.middlewares=redirect-to-https

I tried that in my toml file:

[http.middlewares]
  [http.middlewares.redirect-to-https.redirectScheme]
    scheme = "https"

[http.routers]
  [http.routers.redirs]
    rule = "HostRegexp(`{host:.+}`)"
    entryPoints = ["web"]
    middlewares = ["redirect-to-https"]

What is wrong? Is it possible to do it with a TOML file?