Setup Docker and Traefik using docker-compose

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.

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.

1
2
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

1
sudo usermod -aG docker <username>

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

1
2
3
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.

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.

1
2
3
4
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.

1
2
3
4
5
6
7
8
9
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.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
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.

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

1
2
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`.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
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.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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`.

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.

1
htpasswd -nb <username> <password>

This should give you something like:

1
<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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
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.

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.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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

Now start the two containers using

1
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.