Simple Traefik 2.0 Setup with Docker Compose

Johnny AM

Getting started with Traefik and Docker

Traefik is a versatile cloud native router that can be used with many orchestration tools (swarm, nomad, and kubernetes). The easiest way to explore Traefik is with Docker Compose.

If the end goal is to build a simple static site (blog, portfolio, marketing page), then Traefik is likely overkill. Free hosting solutions like Vercel and Netlify are better options. If your requirements get more complex, it may make sense to evolve to Traefik and Docker Compose.

Here are some scenarios where I'll consider Traefik + Docker-Compose setup.

  1. Mutiple technologies needed for the app. For example, a React front-end, a Node/Express backend, and GoLang services.
  2. Need to support multiple domains and/or subdomains with TLS Certificates (HTTPS), which is a best practice for any modern apps.
  3. Self-host an open source tool like Strapi or OpenFaas that doesn't have a hosted option.

Tutorial Requirements

Locally

  • MacOS or Linux Machine (have not tested on windows, but likely works)
  • Docker Desktop

Deployed/Internet Accessible (only needed when you are ready to deploy)

  • Linux virtual machine - I recommend Digital Ocean ($100 credit when using this link)
  • A domain name (preferably one not in use)
  • Ability to update the DNS entries for your domain (we'll add a A and CNAME record)

Building in stages

Let's build a solution in steps so we have a better understanding of how it comes together

Running Locally - Get Traefik Dashboard Working

Create a new file named docker-compose.yml and add this yaml


version: "3.3"

services:
  traefik:
    image: traefik:v2.3
    command:
      - --log.level=INFO
      - --api.insecure=true

    ports:
      - "8080:8080"
  

Explaining the Configuration

  • This is a basic docker-compose file. Docker compose uses YAML configuration.
  • We set the log.level to INFO via a command. The default is ERROR. I like to set this to INFO or DEBUG for Traefik during development, and ERROR once stable in production.
  • we set api.insecure to true. By default, this is set to false and won't display the dashboard locally (needs TLS cert).

This compose file isn't doing much yet. It simply launches our Traefik container. We could have started the Traefik container directly in standalone mode, but knowing that our final solution includes docker-compose, it made sense to start here.

The equivalent docker run command to the compose file above would be:
docker run -p 8080:8080 traefik:v2.3 --log.level=INFO --api.insecure=true

This is why these values are called commands in the yaml above. They translate into command line arguments for the container. These are considered Static Configuration, which I'll cover in more detail in the Q/A section.

From the directory where your docker-compose.yml file is located, type in the terminal:


> docker-compose up
  

You can now navigate to the Traefik dashboard on http://localhost:8080 and see the dashboard.

Add Two Sample Containers

Now that Traefik is running with docker compose, let's add a couple docker containers that will help with testing. I have chosen two very popular whoami docker images that simply output information to a webpage:

Update your docker-compose.yml file to


version: "3.3"

services:
  traefik:
    image: traefik:v2.3
    command:
      - --log.level=INFO
      - --api.insecure=true

    ports:
      - "8080:8080"

  hello:
    image: containous/whoami
    ports:
      - "8000:80"

  whoami:
    image: jwilder/whoami
    ports:
      - "8001:8000"
  

Explaining the Configuration

  • Added new service entries for the two new containers, hello and whoami.
  • These containers are not proxying through Traefik yet. Docker compose is handling the traffic from the host port to the container port "[host-port]:[container-port]"
  • You should be able to navigate to http://localhost:8000 and http://localhost:8001 and see the running whoami containers.

We still aren't taking advantage of Traefik. Here we just added two containers so we can test routing and subdomains in subsequent steps.

Stop the running instance of docker-compose with [control]+c

Restart docker-compose, but this time in detached mode to simplify these steps moving forward.


> docker-compose down
> docker-compose up -d  # this will run docker-compose in detached mode
  

Let's Proxy Our Traffic Through Traefik

We can now start using the power of Traefik and make sure all our request are routed via Traefik. Make the following changes and restart docker-compose.


version: "3.3"

services:
  traefik:
    image: traefik:v2.3
    command:
      - --log.level=INFO
      - --api.insecure=true
      - --entrypoints.web.address=:80
      - --providers.docker

    volumes:
      - /var/run/docker.sock:/var/run/docker.sock

    labels:
      traefik.http.routers.dash.rule: Host(`dash.localhost`)
      traefik.http.routers.dash.service: api@internal

    ports:
      - "80:80"

  hello:
    image: containous/whoami
    labels: # auto-detects port 80 in next line
      traefik.http.routers.hello.rule: Host(`hello.localhost`)

  whoami:
    image: jwilder/whoami
    labels: # auto-detects port 8000 in next line
      traefik.http.routers.whoami.rule: Host(`whoami.localhost`)
  

Key Learnings

  • We removed all port entries except for 80. We also added - --entrypoints.web.address=:80. We will be routing all traffic through this single entry point, including the Traefik dashboard.
  • We set docker as a provider with - --providers.docker. This tells Traefik to retrieve configuration directly from Docker.
  • We added a new Traefik volumes section and map the host containers socket, to the container socket with - /var/run/docker.sock:/var/run/docker.sock. In short, this is how Traefik can utilize Dynamic Configuration (see Q/A)
  • We added label sections to each service, and a routing rule (basically the domain/subdomain) to reach each service. Traefik will auto-detect the port for the running container.
  • In the traefik labels section, we tell the route to go to api@internal which is the service name to the dashboard.

Restart docker compose


> docker-compose up -d # updates services in place with the new configuration
  

The Traefik dashboard and containers will now be available via their hostnames instead of by port number. Navigate to http://dash.localhost, http://hello.localhost, and http://whoami.localhost

We have completed our simple example of Traefik and Docker Compose locally and learned some key concepts. Let's take what we learned and deploy to a VM on the internet.

Setup your VM with Docker

For this next section, you'll need a virtual machine (VM). You could use almost any provider, but I'll be using Digital Ocean for this example (the basic $5 Ubuntu Droplet).

Each provider will have different steps to provision and get SSH access, so I'm going to skip these parts and assume you have SSH'd into your new VM.

Install Docker

Docker provider a simple install script at https://get.docker.com/. Run the following two commands.


# retrieve the docker script and save it locally
> curl -fsSL get.docker.com -o get-docker.sh

# install docker. Command takes ~1 minute due to running apt-get update
> sh get-docker.sh
  

Create your docker-compose file locally

If you know how to sync your compose file, feel free to do so, otherwise simply copy it or retype it. We'll need to make a couple small changes. You won't be able to use localhost on the internet, so here is where' we'll swap localhost for your domain name. My domain is called code1.site, but please use your domain in this place.

  • dash.localhost >> dash.[your-domain].[tld]
  • hello.localhost >> hello.[your-domain].[tld]
  • whoami.localhost >> whoami.[your-domain].[tld]

Here is my docker-compose.yaml file after I swapped the domain names.


version: "3.3"

services:
  traefik
    image: traefik:v2.3
    command:
      - --log.level=INFO
      - --api.insecure=true
      - --entrypoints.web.address=:80
      - --providers.docker

    volumes:
      - /var/run/docker.sock:/var/run/docker.sock

    labels:
      traefik.http.routers.dash.rule: Host(`dash.code1.site`)
      traefik.http.routers.dash.service: api@internal

    ports:
      - "80:80"

  hello:
    image: containous/whoami
    labels:
      traefik.http.routers.hello.rule: Host(`hello.code1.site`)

  whoami:
    image: jwilder/whoami
    labels:
      traefik.http.routers.whoami.rule: Host(`whoami.code1.site`)
  

This Article is in Draft / Incomplete Status

This article is currently being written. Expect frequent updates and completion within the next few weeks. I often release early to motivate myself to finish the articles :)

Common Questions / Answers

What is static and dynamic configuration in Traefik? What's the difference?

Honestly, this is difficult to understand. Here is my attempt. Once Traefik starts, static configuration cannot change without a restart, however Traefik will ingest changes to dynamic configuration and adapt. In a perfect world, everything would be dynamic, but currently some config must be defined statically at startup. Let's explore the differences

Static Configuration (known as the Startup configuration)

  • Deals with EntryPoints to Traefik (the main entry points, usually 80 and 443)
  • Handles provider setup (i.e. providers.docker, providers.file.directory)
  • Handles setup for Tracing, AccessLogs, Metrics, and CertificateResolvers
  • Can only be defined in one of three places (mutually exclusive), you must choose only 1. Choices are TOML/YAML config file, Command arguments, or ENV variables
  • You can view the full list of Static configuration arguments by running this command: docker run traefik:v2.2 --help

Dynamic Configuration (all the routing stuff)

  • Retrieves it's configruation from providers, in our case Docker
  • Listens to events from the provider (Docker, File etc..) and adapts based on changes
  • Can be defined in standalone TOML/YAML file(s), inside Docker Compose's service Label section, or both. Common to put reusuable config in File and Service config in Docker Labels
  • Handles all routing details to services. For example: This host name goes to this service, this service uses this auth middleware, this service should use TLS from the LE certificate resolver
  • You can see Dynamic Configuration Reference Here

How does Traefik auto-detect the container port if there are zero or multiple port(s) exposed?
Port auto-detection will only work if the container has exactly 1 port exposed. If it doesn't have exactly 1 port exposed, then you have to add that configuration to that services label section. This feature will only work in Docker Compose mode, it does not work in Docker Swarm mode.

Terms of Service  |  Privacy Policy

© 2020 johnny.am. All rights reserved.