I Switched from NPM to Traefik
I really like NPM. But Traefik allows me to do the same, without going through a web UI. So I switched, and this is how I've done it.
I really like Nginx Proxy Manager. It's easy to use, quick to set up and does everything I'm looking for in a reverse proxy. But Traefik always intrigued me. It has the same functionality as NPM, but you don't have to interact with an interface. It loads the config based on the labels on your docker containers. In the short run, it would take longer to implement. However in the long run, it would save me from having to define every new instance in the web interface and use tags instead. So I switched.
Setting up Traefik
I'll show you my compose file and then I'm going to try and explain it step by step.
version: "3"
networks:
default:
external:
name: npm_network
services:
traefik-app:
image: traefik:latest
restart: always
ports:
- 80:80
- 443:443
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ${DATADIR}/app/letsencrypt:/letsencrypt
command:
- --api.insecure=true
- --providers.docker
- --providers.docker.exposedbydefault=false
- --entrypoints.web.address=:80
- --entrypoints.web.http.redirections.entrypoint.to=websecure
- --entrypoints.web.forwardedHeaders.trustedIPs=172.26.0.0/16,172.80.0.0/24
- --entrypoints.websecure.address=:443
- --entrypoints.websecure.forwardedHeaders.trustedIPs=172.26.0.0/16,172.80.0.0/24
- --entrypoints.websecure.http.tls.domains[0].main=${DOMAIN}
- --entrypoints.websecure.http.tls.domains[0].sans=*.${DOMAIN}
- --entrypoints.websecure.http.tls.certresolver=myresolver
- --certificatesresolvers.myresolver.acme.dnschallenge=true
- --certificatesresolvers.myresolver.acme.dnschallenge.provider=godaddy
- --certificatesresolvers.myresolver.acme.email=${MAIL_ADR}
- --certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json
environment:
GODADDY_API_KEY: ${GODADDY_API_KEY}
GODADDY_API_SECRET: ${GODADDY_API_SECRET}
GODADDY_POLLING_INTERVAL: 10
GODADDY_PROPAGATION_TIMEOUT: 180
labels:
traefik.enable: true
traefik.http.routers.traefik.rule: Host(`${APP_URL}`)
traefik.http.services.traefik.loadbalancer.server.port: 8080
The first part describes the compose syntax version and the default network to use. I'm reusing my NPM network, as all my services are already connected to this. Using the same network across my compose files, allows me to use Traefik as a reverse proxy without opening container ports on my host.
Next we define the service. The first few rules are pretty standard. The image
tag to tell Docker what image to use. The parameter restart
is set to always because we want our reverse proxy to always come back up. We expose port 80 and 443 because Traefik will handle all http(s) requests to our host. In the volumes
section, we give Traefik read-only access to our Docker socket so it can read the labels. We also define a storage location where it can store its SSL certificates.
Command
With docker-compose we can define extra flags and/or parameters for the start command using comand
. I'll go over all these and tell what they do:
--api.insecure=true
This tells Traefik to expose its dashboard using http. It will use port 8080 by default.--providers.docker
It lets Traefik know it should look for Docker images and tags to create its config.--providers.docker.exposedbydefault=false
This makes sure only containers with the labeltraefik.enable=true
are handled by Traefik.--entrypoints.web.address=:80
Traefik creates an endpoint that will listen to requests on port 80.--entrypoints.web.http.redirections.entrypoint.to=websecure
It will send all http traffic on port 80 to another endpoint called websecure.--entrypoints.websecure.address=:443
Traefik creates an endpoint that will listen to requests on port 80.--entrypoints.websecure.http.tls.domains[0].main=${DOMAIN}
For the websecure endpoint, traefik will use a certificate for the domain saved in that variable.--entrypoints.websecure.http.tls.domains[0].sans=*.${DOMAIN}
The certificate will also be valid for the wildcard domain.--entrypoints.websecure.http.tls.certresolver=myresolver
Traefik will try to get SSL certificates from Let's Encrypt using the resolver myresolver.--certificatesresolvers.myresolver.acme.dnschallenge=true
We create a certificate resolver that will try to get a certificate using a DNS challenge. This is needed for wildcard certificates.--certificatesresolvers.myresolver.acme.dnschallenge.provider=godaddy
I use GoDaddy for my domain names and DNS, so we tell the resolver to use that.--certificatesresolvers.myresolver.acme.email=${MAIL_ADR}
Use the email address from that variable to accept the Let's Encrypt agreements.--certificatesresolvers.myresolver.acme.storage=/letsencrypt
Tell Traefik where to store its certificates.
Environment
To use the GoDaddy API for the DNS challenge, we need to define it in the environment variables of our container. The GODADDY_API_KEY
and GODADDY_API_SECRET
are filled from variables. These can be created in the GoDaddy developer portal. GODADDY_POLLING_INTERVAL
defines the time in seconds between test to see if the DNS record has been updated. GODADDY_PROPAGATION_TIMEOUT
tells Traefik how long it should wait before giving up on trying to resolve the DNS record. I had to increase these values from the defaults, as the DNS updates weren't quick enough.
Labels
Now to expose our dashboard, we need to label the container. Traefik creates its config from labels, even his own config is read from his own labels.
traefik.enable: true
Tells Traefik to configure this container.traefik.http.routers.traefik.rule: Host(`${APP_URL}`)
Use the APP_URL variable to define a reverse proxy based on hostname for this container. All requests for http(s)://${APP_URL} will be send to this container.traefik.http.services.traefik.loadbalancer.server.port: 8080
The dashboard is hosted on port 8080, so using this label Traefik knows to send traffic to port 8080.
.env
As you can see, we used a few variables in our compose file. We define these in the .env
file in the same folder:
DATADIR=*****
DOMAIN=*****
APP_URL=*****
MAIL_ADR=*****
GODADDY_API_KEY=*****
GODADDY_API_SECRET=*****
Fill in these variables with your config parameters and you will be able to launch Traefik using docker-compose up -d
. Once it is running you should be able to reach your Traefik dashboard using the url you filled in in the APP_URL
variable.
Exposing Containers
If you want to expose other containers, you can adapt its compose file and add the same three labels:
labels:
traefik.enable: true
traefik.http.routers.traefik.rule: Host(`${APP_URL}`)
traefik.http.services.traefik.loadbalancer.server.port: 8080
Just change the APP_URL
for each service, and tell Traefik the right port to send the traffic to.