Let’s encrypt has introduced wildcard certificates and traefik has released a v2 which is completely different from v1. This calls for a tutorial on how to use the two together using docker compose.
Why wildcard certificates?
You don’t need separate https certicates for your subdomain, especially if you are used to deploying your applications as different subdomains. This will significantly reduce calls to Let’s Encrypt servers which is now important since they have introduced serious rate limits. And of course, Let’s encrypt is totally free so you have no reason to not use HTTPS anymore, not to mention its more secure than using HTTP.
Traefik is a beautiful piece of software which is container aware and can get/issue/renew Let’s Encrypt certificates automatically, once configured.
At the time of writing this, Let’s Encrypt only supports wildcard certificates using the
DNS-01 verification method so thats what this article uses as well.
Docker compose file for Traefik:
version: "3.3" services: traefik: image: "traefik:v2.5" container_name: "traefik" command: #- "--log.level=DEBUG" #- "--certificatesresolvers.myresolver.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory" - "--api.insecure=true" - "--providers.docker=true" - "--providers.docker.exposedbydefault=false" - "--entrypoints.web.address=:80" - "--entrypoints.web.http.redirections.entrypoint.to=websecure" - "--entrypoints.websecure.address=:443" - "--entrypoints.websecure.http.tls.domains.main=<your.domain.com>" - "--entrypoints.websecure.http.tls.domains.sans=*.<your.domain.com>" - "--entrypoints.websecure.http.tls.certresolver=myresolver" - "--certificatesresolvers.myresolver.acme.dnschallenge=true" - "--certificatesresolvers.myresolver.acme.dnschallenge.provider=duckdns" - "--certificatesresolvers.myresolver.acme.email=<your email address>" - "--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json" environment: - "DUCKDNS_TOKEN=<token>" ports: - "80:80" - "443:443" volumes: - "./letsencrypt:/letsencrypt" - "/var/run/docker.sock:/var/run/docker.sock:ro" labels: - "traefik.enable=true" - "traefik.http.routers.traefik-dashboard.rule=Host(`traefik-dashboard.your.domain.com`)" - "traefik.http.routers.traefik-dashboard.service=traefik-dashboard" - "traefik.http.services.traefik-dashboard.loadbalancer.server.port=8080"
The above config also exposes the traefik dashboard as
Uncomment/add the following in
command when testing:
To get debug logs:
To use the staging lets encrypt server as debugging on production servers will get you rate limited
Deploy each application in a separate docker-compose file
I prefer using different docker-compose.yml files for different applications. It makes managing them easier, especially when you have a lot of applications.
The following is an example docker-compose file for an application, that I use:
--- version: "3" services: heimdall: image: ghcr.io/linuxserver/heimdall container_name: heimdall environment: - PUID=1000 - PGID=1000 - TZ=Europe/London volumes: - ./config:/config labels: - "traefik.enable=true" - "traefik.http.routers.heimdall.rule=Host(`heimdall.your.domain.com`, `www.your.domain.com`)" - "traefik.http.routers.heimdall.service=heimdall" - "traefik.http.services.heimdall.loadbalancer.server.port=80" networks: - traefik_default restart: unless-stopped networks: traefik_default: external: true
Please note the
networks sections (two) in the above template/example. Since traefik and the applications are in different docker compose files, they will endup in different networks by default and will not be able to communicate with each other. The
networks sections add the application containers to traefik’s network as well so that they can communicate with each other. Also note that there are no ports forwarded to the host except for
80 (http) and
443 (https) which traefik runs on.
Deploy applications in the same docker-compose file as that of traefik
You can put all the applications as different services in a single docker-compose file along with the traefik service. This can be use full if there are only a few applications or if the applications are dependent on each other for some reason or simply because its simpler.
If that’s the route you are taking, note that the
networks sections (both) are
not required since all the services in a docker-compose file will end up in the same network by default. Except for the lack of
networks sections, the service definitions are exactly the same.