Traefik, Let's Encrypt and Docker Compose

I'm a huge fan of Docker and Let's Encrypt. It's a dream come true for someone who manages their family and friends' Wordpress sites - just set up a container and let them have at it.

Lately I've been making use of the reverse proxy Traefik. One of the coolest features about it is that it can dynamically configure itself based on the labels you give to a Docker container, and can also automatically fetch certificates from Let's Encrypt.

A Simple Example

Let's say you have a docker-compose file running your website:

version: "2"

services:
  website:
    image: mywebsite
    ports:
      80:5000

Something along those lines. If you wanted to have SSL support you have a couple of options: one, run something like Nginx within your docker container and map certificates in from outside the container, for example:

version: "2"

services:
  website:
    image: mywebsite
    ports:
      80:5000
      443:5001
    volumes:
      - ${PWD}/cert.pem:/usr/share/nginx/certs/cert.pem
      - ${PWD}/cert.cer:/usr/share/nginx/certs/cert.cer

This is fine but kind of a pain to manage, and anything configuration management we have outside of the Docker containers puts more reliance on configuration management tools like Ansible or Puppet or what-have-you.

Getting Traefik to work

Traefik comes in the form of a docker image, so we can just plop it into our docker-compose file. It does need some external configuration. Here's a really boiled down example that should give you all you need:

traefikLogsFile = "/var/log/traefik/traefik.log"
logLevel = "INFO"
defaultEntryPoints = ["http", "https"]

[entryPoints]
[entryPoints.http]
address = ":80"

[entryPoints.http.redirect]
entryPoint = "https"

[entryPoints.https]
address = ":443"

[entryPoints.https.tls]

[acme]
email = "your-email@gmail.com"
storage = "/etc/traefik/acme/acme.json"
entryPoint = "https"
dnsProvider = "digitalocean"

[[acme.domains]]
main = "yoursite.com"
sans = ["www.yoursite.com", "web.yoursite.com"]

That's actually pretty close to what I use to run my site. It tells Traefik to listen on both port 80 and 443, but to redirect all HTTP requests to HTTPS. It also configures Let's Encrypt (the acme sections) with the DigitalOcean DNS providers to do the challenge-response. Finally we tell it which domains to try and register the cert with, as well as which SAN's to associate the cert with. You can have up to 100 SAN's for one cert with Let's Encrypt!

Now we just configure the docker compose file:

version: "2"

service:
  website:
    image: mywebsite
    labels:
      traefik.port: "5000"
      traefik.frontend.rule: "Host: yoursite.com, www.yoursite.com, web.yoursite.com"
  traefik:
    image: traefik
    command: --docker
    environment:
      DO_AUTH_TOKEN: "SOMEAUTHTOKEN"
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - /var/log/traefik:/var/log/traefik
      - ${PWD}/traefik.toml:/etc/traefik/traefik.toml
      - ${PWD}/acme:/etc/traefik/acme

The traefik service is a little long but that's it - we just map the Docker socket so that Traefik can use Docker labels for configuration, we map our traefik.toml file in, and then map the Acme directory in so that even if we restart Traefik, it won't lose info on which certificates it's registered.

We also have the DigitalOcean auth token so that Traefik can update your DNS for the challenge-response, and then finally set the Docker labels for your website container.

You can find all the labels you can pass in to your container that Traefik will understand by checking out the Traefik docs here. The documentation is pretty nice, but could stand to have some more simple examples.

You can even use the traefik.frontend.auth.basic label to set up some simple authentication for your service!