Docker compose and Traefik example configuration (domain name + SSL certificate)

24 Jan · by Tim Kamanin · 3 min read

Traefik is a great reverse-proxy for Docker, but it can take some time to set it up correctly.

Here I'm posting a reference config that adds a domain name, a certificate generated by letsencrypt and directs all incoming traffic to a container of choice.

Before we begin let's prepare the following directory structure:

- app # this where our app lives
- traefik # directory to hold traefik config
- volumes # here we store our local mounts

At first, we create a docker-compose.yml file:

version: "3"

services:
  app:
    build: ./app # A node app
    command: pm2-docker start server.js # Start server via pm2
    ports:
      - 3000
    labels:
      - "traefik.enable=true" # Enable reverse-proxy for this service
      - "traefik.frontend.rule=Host:example.com" # Domain name for the app

  reverse-proxy:
    image: traefik
    command: --api # Enables the web UI
      - "80:80" # The HTTP port
      - "443:443" # The HTTPS port
      - "8080:8080" # The web UI
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock # So that Traefik can listen to the Docker events
      - ./traefik/traefik.toml:/traefik.toml # Traefik configuration file
      - ./volumes/traefik-acme:/acme # Tell Traefik to save SSL certs here

The app service is not essential here; it can be any Docker service, the primary requirement it should fulfill is to have labels defined as in the example.

Note how we mapped ./volumes/traefik-acme:/acme volumes. In official Traefik document the suggested mapping is something like ./volumes/acme.json:/acme.json. But if we map a file to a file instead of a directory to a directory we'll get a nasty error Failed to read new account, ACME data conversion is not available : read acme.json: is a directory. Btw, thanks to this error I'm writing this post.

Now let's add a traefik/traefik.toml file with the following contents:

debug = false

logLevel = "ERROR"
defaultEntryPoints = ["https","http"]

[entryPoints]
  [entryPoints.http]
  address = ":80"
    [entryPoints.http.redirect]
    entryPoint = "https"
  [entryPoints.https]
  address = ":443"
  [entryPoints.https.tls]

[retry]

[docker]
exposedByDefault = false

[acme]
email = "admin@example.com"
storage = "acme/certs.json"
entryPoint = "https"
onHostRule = true
[acme.httpChallenge]
entryPoint = "http"

Notice how we defined acme.storage: acme/certs.json, this is to prevent the acme.json mapping issue I've described earlier.

Now you can go and run docker-compose up on your server, and if everything is done right, you should be able to access your service via https://example.com. If you have issues, please check the Web UI output at https://example.com:8080 and make sure you run this setup on a server, not on your local machine.

This is because acme in Traefik is letsencrypt.org and to generate a certificate for you, it needs to make a callback to your server and access Traefik reverse-proxy via the domain name that you've defined (example.com in our case).

Hope this all works for you... or for future me.

Want to get more 🔥 tips like this one?

Subscribe to get notified about new dev tutorials

Comments