Installation and Configuration of Traefik in Docker
Overview
Traefik is a reverse proxy which listens to docker events and can dynamically create backends and insert middleware just by including the appropriate docker labels in subsequent docker-compose.yml files.
Dependencies
Prior to setting up Traefik, a wildcard certificate for the domain will need to be obtained. In this example, we are using *.weepylabs.com, however this is just an example.
NOTE: It is possible to set traefik up to dynamically generate SSL certificates via Let’sEncrypt, but that is not covered in this article.
Installation
On the docker server where you wish to set Traefik up, you will first need to create a docker-compose.yml file:
version: '3'
services:
traefik:
image: traefik:v2.9
container_name: traefik
command:
- "--entrypoints.web.address=:80"
- "--entrypoints.web.http.redirections.entrypoint.to=web-secure"
- "--entrypoints.web.http.redirections.entrypoint.scheme=https"
- "--entrypoints.web-secure.address=:443"
- "--api.dashboard=true"
- "--providers.docker=true"
- "--providers.file.directory=/certs/"
- "--providers.file.watch=true"
ports:
- "80:80"
- "443:443"
volumes:
- /etc/localtime:/etc/localtime:ro
- /var/run/docker.sock:/var/run/docker.sock
- ./certs:/certs
labels:
- "traefik.enable=true"
- "traefik.http.routers.traefik.rule=Host(`traefik.weepylabs.com`)"
- "traefik.http.routers.traefik.tls=true"
- "traefik.http.routers.traefik.service=api@internal"
networks:
- traefik_frontend
restart: always
networks:
traefik_frontend:
external: trueBreaking down this config, here are the important bits:
Command
- "--entrypoints.web.address=:80"
- "--entrypoints.web.http.redirections.entrypoint.to=web-secure"
- "--entrypoints.web.http.redirections.entrypoint.scheme=https"
- "--entrypoints.web-secure.address=:443"This sets the web port to 80, the web-secure port to 443. This also sets up redirection from http to https.
- "--providers.docker=true"
- "--providers.file.directory=/certs/"
- "--providers.file.watch=true"This tells Traefik to use docker as a provider for service discovery. This also says to set up a file watcher in the /certs directory, so we can dynamically load in certificates and configurations.
Volumes
volumes:
- /etc/localtime:/etc/localtime:ro
- /var/run/docker.sock:/var/run/docker.sock
- ./certs:/certs/etc/localtime is mounted to the container so it can determine system time.
/var/run/docker.sock is mounted so that Traefik has access to the underlying docker stack for service discovery.
./certs is mounted so that we have a place to put in certificates/configurations dynamically.
Labels
labels:
- "traefik.http.routers.traefik.rule=Host(`traefik.weepylabs.com`)"
- "traefik.http.routers.traefik.tls=true"
- "traefik.http.routers.traefik.service=api@internal"This is where the Traefik magic happens. Using these labels, we can tell traefik we want it to reverse proxy this service.
"traefik.http.routers.<docker-compose-app-name>.rule=Host(`<domain>`)"This tells traefik that we want to match the Host header with . It is also important that with these labels, you use the same app name from the docker-compose.yml file in the router definition.
"traefik.http.routers.<docker-compose-app-name>.tls=true"This tells traefik that we want this to secure the endpoint with TLS (SSL).
"traefik.http.routers.<docker-compose-app-name>.service=api@internal"For Traefik specifically, this is linking the service to an internal traefik service so it knows to serve the traefik dashboard. This is not typically required.
Network
networks:
- traefik_frontend
...
networks:
traefik_frontend:It is imperative that these two values match. What we are doing here is defining the main docker network that Traefik will be communicating on. This means that we will need to add this network to every docker container we wish to put Traefik in front of.
Creating Certificate Store
We wish to serve all services with a wildcard TLS certificate. We will need to set up a default certificate configuration. Create the file certificates.yaml and populate it with the wildcard certificate and key:
tls:
stores:
default:
defaultCertificate:
certFile: /certs/star.weepytests.com.crt
keyFile: /certs/star.weepytets.com.keyNow, create a certs directory and place these files in it:
mkdir certs
mv certificates.yaml certs/
mv star.weepytests.com.crt certs/
mv star.weepytests.com.key certs/When the container starts, it will mount this certs directory and make the certificates and configuration available to docker.
Running Traefik
Now that the configuration is complete, we can bring this up by running docker-compose up -d. Once the container is online, we should be able to navigate to the hostname we provided in the config (in my exmaple, traefik.weepylabs.com) and be greeted with the Dashboard.
Adding new services to Traefik
Now that Traefik is up and running, we are going to want to bring a new service into the fold. In this example, I am using phpldapadmin/openldap service that I wish to make available to Traefik.
Original Configuration
version: '2'
services:
openldap:
image: osixia/openldap:1.5.0
container_name: openldap
environment:
LDAP_LOG_LEVEL: "256"
...
tty: true
stdin_open: true
volumes:
...
ports:
- "389:389"
- "636:636"
domainname: "weepylabs.com"
hostname: "wl-dc01"
restart: always
phpldapadmin:
image: osixia/phpldapadmin:latest
container_name: phpldapadmin
environment:
...
ports:
- "8080:80"
depends_on:
- openldap
restart: always
volumes:
...NOTE: Some of the content has been redacted for brevity.
Originally, this docker-compose.yml file brings up two containers, one serves ldap/ldaps, and the other is a web frontend which connects back to the ldap server. There are some environment variables, a couple volume mounts. Running docker-compose up -d would bring this online, but it would not offer a secure connection, and we would have URL’s with port definitions in them, and overall not a great implementation. We want to put the web frontend behind Traefik, but we want to leave the ldap server alone, as it needs to serve ldap/ldaps connections to other hosts on the network and not be interfered with.
Adding Traefik
The first thing that we will need to deal with, is that the frontend container still needs to communicate with the openldap container. Additionally, we need to place the frontend container in the Traefik network. If we only put the frontend container in the traefik network, it will lose it’s ability to communicate with the ldap container. If we put both on the traefik network, traefik will try to reverse-proxy the ldap server connections, which we do not want.
What we need to do, is define two networks:
- traefik_frontend is the named network from our traefik
docker-compose.ymlfile, which will be the frontend for our web application - backend will be a network that the ldap server and our web application can communicate on, which is local to the two of them
At the end of the docker-compose.yml, we will add this:
networks:
traefik_frontend:
external: true
backend:By indicating that external: true for the traefik_frontend network, we are saying that this network exists external to this docker-compose.yml file. Creating a backend network with no definition indicates we wish to create a local network for these containers.
Now, in the openldap container, we will add this network definition so that the container knows it needs to start only on the backend network:
...
hostname: "wl-dc01"
networks:
- backend
restart: alwaysIn our web app (phpldapadmin), we will need to add both networks, so it knows it needs to be in the traefik network to accept requests externally, as well as be on the same network to access ldap.
...
depends_on:
- openldap
networks:
- traefik_frontend
- backend
restart: alwaysNow that the networks have been set up, we can actually remove the exernal port dependancy on the web-app, as traefik will be managing this connection:
ports:
- 80Finally, we need to add the traefik labels to the frontend container, so that traefik knows it should be proxying this service:
labels:
- "traefik.http.routers.phpldapadmin.rule=Host(`ldap.weepylabs.com`)"
- "traefik.http.routers.phpldapadmin.tls=true"
- "traefik.docker.network=traefik_frontend"This tells traefik the hostname we should be listening on (in this case, ldap.weepylabs.com)
"traefik.http.routers.phpldapadmin.rule=Host(`ldap.weepylabs.com`)"
This indicates that we want it to be TLS
"traefik.http.routers.phpldapadmin.tls=true"
Finally, we want to tell traefik which of the two networks we want this configured on
"traefik.docker.network=traefik_frontend"
Final Configuration
Putting it all together, our docker-compose.yml file will now look like this (brevity redactions are still in place):
version: '2'
services:
openldap:
image: osixia/openldap:1.5.0
container_name: openldap
environment:
LDAP_LOG_LEVEL: "256"
...
tty: true
stdin_open: true
volumes:
...
ports:
- "389:389"
- "636:636"
domainname: "weepylabs.com"
hostname: "wl-dc01"
networks:
- backend
restart: always
phpldapadmin:
image: osixia/phpldapadmin:latest
container_name: phpldapadmin
environment:
...
ports:
- "8080:80"
labels:
- "traefik.http.routers.phpldapadmin.rule=Host(`ldap.weepylabs.com`)"
- "traefik.http.routers.phpldapadmin.tls=true"
- "traefik.docker.network=traefik_frontend"
depends_on:
- openldap
networks:
- traefik_frontend
- backend
restart: always
volumes:
...
networks:
traefik_frontend:
external: true
backend: