Traefik With Let's Encrypt Wildcard SSL Certificate Using Docker Compose
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.
Why traefik
Traefik is a beautiful piece of software which is container aware and can get/issue/renew Let’s Encrypt certificates automatically, once configured.
How?
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.
This article also uses duckdns.org for free/dynamic domains. Traefik supports other DNS providers, any of which can be used instead.
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[0].main=<your.domain.com>"
- "--entrypoints.websecure.http.tls.domains[0].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 traefik-dashboard.your.domain.com
Note:
Uncomment/add the following in command
when testing:
To get debug logs:
--log.level=DEBUG
To use the staging lets encrypt server as debugging on production servers will get you rate limited
- "--certificatesresolvers.myresolver.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory"
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.