Traefik is a feature rich, open source reverse proxy. It offers many interesting features like auto service discovery, middlewares, automatic https using let's encrypt...
For local testing purposes, we're gonna be using Vagrant + DuckDNS to simulate a server and domain to deploy traefik to. If you already have a dedicated server and domain, you can skip directly to the Bringing up Traefik section.
Table of Contents
- Setting up Vagrant and port forwarding
- Setting up DuckDNS
- Bringing up Traefik
- Setting up LetsEncrypt
- Setting up automatic HTTP to HTTPS redirect
- Deploying a docker container behind traefik
- Extra credit: Custom 404 page
Setting up Vagrant and port forwarding
If you don't already have Vagrant installed, you can follow their install guide.
We're gonna be using the following Vagrantfile to set up our VM:
Vagrant.configure(2) do |config|
config.vm.box = "ubuntu/bionic64"
# Install Docker using the corresponding vagrant provisioner
config.vm.provision :docker
# Forward port 80 for HTTP access
config.vm.network :forwarded_port, guest: 80, host: 80
# Forward port 443 for HTTPS access
config.vm.network :forwarded_port, guest: 443, host: 443
end
Save this code to a file called Vagrantfile in a directory of your choice, then
run this command in said directory to bring up our VM:
vagrant up
Next we'll need to install docker compose, you can do so by following their install guide.
Next, you're gonna need to set up port forwarding on your router so that your vagrant box can be accessible from your public IP. Look up how to do that for your specific router.
Setting up DuckDNS
Duck DNS is a cool little tool that gives free dynamic DNS with wildcard subdomain support. We'll use it as a domain for our vagrant box.
Setting it up should easy enough, sign up on their website and follow through the instruction to get your free domain and set up the cron job resposnible for updating your ip address.
Bringing up Traefik
We need to create a traefik.toml file in the /opt/traefik/config directory with the following contents:
[accessLog]
filePath = "/etc/traefik/access.log"
bufferingSize = 100
[api]
dashboard = true
[entrypoints]
[entrypoints.web]
address = ":80"
[entrypoints.websecure]
address = ":443"
[providers.docker]
exposedbydefault = false
network = "traefik"
[providers.file]
directory = "/etc/traefik"
watch = true
Quick overview of the config file:
[accessLog]
filePath = "/etc/traefik/access.log"
bufferingSize = 100
Since traefik writes logs asynchronously, bufferingSize represents the number of log lines Traefik will keep in memory before writing them to the selected output.
[api]
dashboard = true
dasboard = true
enables the traefik dashboard
[entrypoints]
[entrypoints.web]
address = ":80"
[entrypoints.websecure]
address = ":443"
EntryPoints are the network entry points into Traefik. They define the port which will receive the packets. Here we defined one called web for HTTP requests and websecure for HTTPS requests.
[providers.docker]
exposedbydefault = false
network = "traefik"
exposedbydefault = false
is specified so that traefik doesn't try to expose all docker containers unless explicitly specified. Can be quite useful for not exposing unnecessary containers like databases, service workers...
network = "traefik"
Defines a default docker network to use for connections to all containers, useful for when a container is connected to multiple networks.
[providers.file]
directory = "/etc/traefik"
watch = true
The watch
option is set to true
to allow Traefik to automatically watch for file changes and apply them without restarting the traefik service.
Next, we'll create a dashboard.toml file in the /opt/traefik/config/routers directory with the following contents:
[provider.file]
watch = true
[http.routers]
[http.routers.dashboard]
rule = "Host(`dashboard.<your-duckdns-domain>`)"
service = "api@internal"
Create a docker-compose.yml file in the /opt/traefik/ directory with the following contents:
version: '3'
services:
traefik:
image: traefik:v2.2.1
container_name: traefik
restart: always
ports:
- "80:80"
- "443:443"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- /opt/traefik/config:/etc/traefik
environment:
- DUCKDNS_TOKEN=<YOUR-DUCKDNS-TOKEN>
networks:
- traefik
networks:
traefik:
external: true
Make sure you substitute <YOUR-DUCKDNS-TOKEN>
with your duckdns generated token.
Before we bring up our traefik service, we need to. first create the aforementioned network that our containers are gonna connect to in order to be discovered by traefik.
We can do that by running this command:
docker network create traefik
Then we can bring up our traefik service using this command:
docker-compose up -d
If navigate in your browser to dashboard.<your-duckdns-domain>
you should see the traefik dashboard in all of its glory:
Setting up LetsEncrypt
Open up the traefik.toml file that we created earlier and add the letsencrypt config at the end of the file:
[certificatesresolvers.letsEncrypt.acme]
email = "<your-email>"
storage = "/acme.json"
[certificatesresolvers.letsEncrypt.acme.dnschallenge]
provider = "duckdns"
delayBeforeCheck = 0
For the sake of this tutorial, we are using the 'duckdns' provider for our dns challenge, but traefik supports many many more providers. You check out the full list here:
https://docs.traefik.io/https/acme/
Setting up automatic HTTP to HTTPS redirect
Create a file called redirect.toml in the /opt/traefik/config/middlewares directory, with the following contents:
[provider.file]
watch = true
[http.middlewares.redirect.redirectscheme]
scheme = "https"
[http.routers]
[http.routers.http-to-https]
entryPoints = ["web"]
middlewares = ["redirect"]
rule = "HostRegexp(`{host:.+}`)"
service = "dummy"
[http.services]
[http.services.dummy.loadBalancer]
[[http.services.dummy.loadBalancer.servers]]
url = ""
Let's see what's going in this file.
[http.middlewares.redirect.redirectscheme]
scheme = "https"
Here, we're creating a middleware called redirect that uses traefik's redirectscheme to redirect all of a router's http requests to https.
Since traefik doesn't support global middlewares that modify all incoming requests and instead does it on a per router basis. In the second snippet:
[http.routers]
[http.routers.http-to-https]
entryPoints = ["web"]
middlewares = ["redirect"]
rule = "HostRegexp(`{host:.+}`)"
service = "dummy"
[http.services]
[http.services.dummy.loadBalancer]
[[http.services.dummy.loadBalancer.servers]]
url = ""
We're creating a dummy catch-all router that uses the redirect middleware to redirect incoming requests to HTTPS. For this router, we're specifying the entryPoints = ["web"]
option to only catch http requests and not fall into an endless loop of redirects.
A full list of traefik's middlewares can be found here:
https://docs.traefik.io/middlewares/overview/
Now we're gonna modify the dashboard.toml file so that it only accepts HTTPS requests and use our letsencrypt certificate resolver:
[provider.file]
watch = true
[http.routers]
[http.routers.dashboard]
entrypoints = ["websecure"] # Only accept HTTP requests
rule = "Host(`dashboard.<your-duckdns-domain>`)"
service = "api@internal"
[http.routers.dashboard.tls]
certresolver = "letsEncrypt" # Use our letsencrypt certificate resolver
If you now go to dashboard.<your-duckdns-domain>
on your browser, it should redirect you to https and you should have a valid ssl certificate.
Deploying a docker container behind traefik
Let's now see how you can deploy a container behind traefik. We're gonna be using the nginxdemos/hello image for this example.
Let's create a docker compose file that uses this image, and see how we can make the container visible to traefik.
version: "3"
services:
nginx-demo:
image: nginxdemos/hello
container_name: traefik-demo
labels:
- "traefik.enable=true"
- "traefik.http.routers.traefik-demo.entrypoints=websecure"
- "traefik.http.routers.traefik-demo.rule=Host(`traefik-demo.<your-duckdns-domain>`)"
- "traefik.http.routers.traefik-demo.tls=true"
- "traefik.http.routers.traefik-demo.tls.certresolver=letsEncrypt"
- "traefik.http.services.traefik-demo.loadbalancer.server.port=80"
networks:
- traefik
networks:
traefik:
external: true
Traefik uses docker labels to exppose containers.
traefik.enable=true
We specify that we want to expose this container through traefik.
traefik.http.routers.traefik-demo.entrypoints
We only want to accept HTTPS requests.
traefik.http.routers.traefik-demo.rule
We set the url we want to access the container from.
traefik.http.routers.traefik-demo.tls
Enable tls.
traefik.http.routers.traefik-demo.tls.certresolver
We specify the certificate resolver we want to use.
traefik.http.services.traefik-demo.loadbalancer.server.port
The port that traefik should use to access the container. Since traefik automatically detects exposed ports, this value can be omitted if your container only exposes one port. But if it exposes multiple ports this value should be specied or else traefik is going to use the first port that docker returns.
networks:
- traefik
We add the container to the traefik network.
Now if you access traefik-demo.<your-duckdns-domain>
you should see this page:

Congratulations, you're now successfully running traefik on you machine!
Extra credit: Custom 404 page
A cool thing we could do is change the boring default 404 page that traefik has.
This is the default page:
We're gonna be changing it with this animation from codepen:
You can either export the codepen and create your own docker image or use the docker compose file below:
version: "3"
services:
global-404:
image: ttlgeek/yeti-404
container_name: global-404
networks:
- traefik
labels:
- "traefik.enable=true"
- "traefik.http.routers.global-404.entrypoints=websecure"
- "traefik.http.routers.global-404.rule=HostRegexp(`{host:.*}`)"
- "traefik.http.routers.global-404.tls=true"
- "traefik.http.routers.global-404.tls.certresolver=letsEncrypt"
- "traefik.http.services.global-404.loadbalancer.server.port=80"
networks:
traefik:
external: true