Introduction

Since I started using docker around a year ago, I’ve been using the same setup with Nginx and docker-compose. Lately this is not working as intended and I want to redo my docker setup. This is a great opportunity to try and use Traefik and an for me to document what I am doing.

Docker

I have a simple 2 CPU and 4GB RAM Ubuntu server running docker. Docker comes preinstalled from my VPS, so all I have to do it create a new user account, since I don’t want to use root for everything.

adduser <username>
usermod -aG sudo <username>

Now my regular user can use sudo so I don’t have to switch to root all the time.

Next, I want to avoid using sudo docker <cmd> and just be able to run docker commands without sudo. This can be done by writing

sudo usermod -aG docker <username>

Docker-Compose

Installing docker-compose is pretty easy. First I made the mistake of using apt-get install docker-compose, which gives me a wrong version of docker-compose and I had to remove a bunch of things. So starting over I went to the docker website and followed the guide using

sudo curl -L "https://github.com/docker/compose/releases/download/1.22.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
docker-compose --version

Now the version is correct and I could continue without problems.

Setting up containers

To start with, I want two simple containers; one for traefik and one for whoami. To keep it simple at first, I’ll use just one docker-compose file and later probably expand it so each application has their own file.

First I want to setup a folder structure to store my data and files in.

cd 
mkdir Docker && cd Docker
mkdir traefik && cd traefik
vi docker-compose.yml

Now we should be in a folder located here /home/<username>/Docker/traefik. Opening the docker-compose file we need to setup some basic things first.

version: "3"                                                 

services:                                                    
  traefik:                                                   
    container_name: traefik                                  
    image: traefik:alpine                                    
  whoami:                                                    
    container_name: whoami                                   
    image: emilevauge/whoami                                 

This is a very basic stack where we have two containers; traefik (based on alpine) and emilevauge/whoami. If we run this, we don’t really get anything usefull, so let’s add a few things.

version: "3"

services:
  traefik:
    container_name: traefik
    image: traefik:alpine
    ports:
      - 80:80
      - 443:443
    volumes:
      - ${PWD}/config:/etc/traefik
      - /var/run/docker.sock:/var/run/docker.sock
  whoami:
    container_name: whoami
    image: emilevauge/whoami    

This is more something we can work with. Running this we can access traefik on port 80 and 443, so http and https. But what we want is to access whoami and traefik on subdomains and have Let’s Encrypt sign certificated for https.

Subdomains

So let us first create the config files for traefik and then customize them a bit.

mkdir config
vi config/traefik.toml

In our config file, we need to specify entrypoints, which the domain accepts. This needs to be both http and https. It might seem illogical to have both, since we just want to use https, but if we don’t specify both as entrypoints, then we need to specifically write https://whoami.domain.com. Ideally we want traefik to redirect from http to https, so we can just go to whoami.domain.com and we get redirected to https://whoami.domain.local.

defaultEntryPoints = ["http", "https"]

[entryPoints]
  [entryPoints.http]
  address = ":80"
  [entryPoints.https]
  address = ":443"
    [entryPoints.https.tls]

[docker]
endpoint = "unix:///var/run/docker.sock"
domain = "domain.com"
exposedByDefault = false

So in this file, we setup the default entrypoints and specify which ports do what. Then we let traefik know where our docker endpoint is and what the default domain name is. Here you should write your own domain name. Lastly I don’t want every container to be exposed by default. I would rather specify that in the docker-compose file for each service.

Before we can access the two sites, we need to change a few things in the docker-compose file.

version: "3"

services:
  traefik:
    container_name: traefik
    image: traefik:alpine
    ports:
      - 80:80
      - 443:443
      - 8080:8080
    volumes:
      - ${PWD}/config:/etc/traefik
      - /var/run/docker.sock:/var/run/docker.sock
    labels:
      - "traefik.enable=true"
      - "traefik.frontend.rule=Host:traefik.domain.com"
      - "traefik.port=8080"
  whoami:
    container_name: whoami
    image: emilevauge/whoami
    labels:
      - "traefik.enable=true"
      - "traefik.frontend.rule=Host:whoami.domain.com"

Each container needs to have a label that enables it in traefik and a frontend rule, so we can find it on a subdomain. We also add port 8080 as the dashboard port and tell traefik that we want to access that on traefik.domain.com.

Authentication

You should be able to just access the sites now using http but if you try and access them using https, you will get a certificate error. Futhermore the dashboard for traefik is exposed to the internet, which really isn’t ideal. Therefore we can use htpasswd to setup a simple authentication for the site.

htpasswd -nb <username> <password>

This should give you something like:

<username>:$apr1$9WtvIi9R$7UmmK6YEs0dDtLlM.1sbh.

If you don’t have htpasswd installed, run sudo apt install apache2-utils -y and it will install the command in no time. So let’s open up the traefik.toml again and setup authentication

defaultEntryPoints = ["http", "https"]

[entryPoints]
  [entryPoints.http]
  address = ":80"
  [entryPoints.https]
  address = ":443"
    [entryPoints.https.tls]
  entryPoint = "https"
  [entryPoints.web]
  address = ":8080"
    [entryPoints.web.auth]
      [entryPoints.web.auth.basic]
        users = ["<username>:$apr1$9WtvIi9R$7UmmK6YEs0dDtLlM.1sbh."]
[api]
entryPoint = "web"

[docker]
endpoint = "unix:///var/run/docker.sock"
domain = "domain.com"
exposedByDefault = false

Now our port 8080, which is routed to traefik.domain.com will be protected by a username and password login.

Let’s Encrypt

Final thing to do is to setup Let’s Encrypt certificates and automatic redirect from http to https. This is setup in the traefik.toml file and we are going to the http challenge.

defaultEntryPoints = ["http", "https"]

[entryPoints]
  [entryPoints.http]
  address = ":80"
  [entryPoints.https]
  address = ":443"
    [entryPoints.https.tls]
  [entryPoints.http.redirect]
  entryPoint = "https"
  [entryPoints.web]
  address = ":8080"
    [entryPoints.web.auth]
      [entryPoints.web.auth.basic]
        users = ["<username>:$apr1$9WtvIi9R$7UmmK6YEs0dDtLlM.1sbh."]

[api]
entryPoint = "web"

[acme]
email = "xxx@xxx.com"
entryPoint = "https"
storage = "acme.json"
onHostRule = true
  [acme.httpChallenge]
  entryPoint = "http"

[docker]
endpoint = "unix:///var/run/docker.sock"
domain = "domain.com"
exposedByDefault = false

Running it

Now start the two containers using

docker-compose up

This should show a download of each container and then starting them. Right now the log is shown in the terminal and you cant really do anything else, I like to do this when I’m testing. If you visit whoami.domain.com you should be redirected to https with a valid certificated. Likewise, if you visit traefik.domain.com you should also be redirected but now you will be prompted for your login details before you can access the site.