Traefik with Docker and Let's Encrypt
Tags: traefik, docker, https
Published: 2018-12-03 21:52:02

---
title: Traefik with Docker and Let's Encrypt
published: true
tags: traefik,docker,https
canonical_url: https://jonnev.se/traefik-with-docker-and-lets-encrypt/
---

 ![Traefik with Docker and Let's Encrypt](https://jonnev.se/content/images/2018/03/Screen-Shot-2018-03-13-at-22.19.45-1.png)

I run most of my services in Docker and previously I was using `nginx` as a reverse and TLS termination proxy together with Let's Encrypt. That worked great but everytime I wanted to try something new I had to copy-paste another conf and change a few values. I probably could have automated that to some extent and there are others who have but with my recent migration to VPSes I figured I'd give [Traefik](https://traefik.io) a try, if nothing else for their **awesome** logo!

Here I'll show you how to set up Traefik with GUI, http redirection and automatic Let's Encrypt certificates. We'll also add _basic auth_ to the Traefik GUI. 


## Prerequisites

This guide assumes some general knowledge of Linux and that you have a server available with these services installed:
- [docker](https://docs.docker.com/engine/installation/linux/docker-ce/ubuntu/)
- [docker-compose](https://docs.docker.com/compose/install/)
- A **domain** to host your apps on

For cheap and good servers, checkout [DigitalOcean](https://www.digitalocean.com) or [Hetzner](https://www.hetzner.com/cloud). Also I always recommend [this guide](https://plusbryan.com/my-first-5-minutes-on-a-server-or-essential-security-for-linux-servers) when setting up a new VPS.

_If you want to use Digital Ocean feel free to use my [referral link](https://m.do.co/c/37d221e6802a) to get $10 for your server_ 😊

## Setup

This is based on the [official guide](https://docs.traefik.io/user-guide/docker-and-lets-encrypt/) but with a few additions. I will show you how to add the web dashboard and API - protected by Basic Auth - mostly because it's fun. If you have no use for it or believe it to be [unsafe](https://docs.traefik.io/configuration/api/#security), you can skip that part.

First start with creating a network for your web-facing containers to connect to.
```
docker network create web
```

Then we create a directory and the necessary files, as sudo if needed.
```
sudo su
mkdir -p /opt/traefik
touch /opt/traefik/docker-compose.yml
touch /opt/traefik/acme.json && chmod 600 /opt/traefik/acme.json
touch /opt/traefik/traefik.toml
```

Now let's add our `docker-compose`...
```
nano /opt/traefik/docker-compose.yml
```

```yaml
version: '2'

services:
  proxy:
    image: traefik:v1.7.12-alpine
    command: --configFile=/traefik.toml
    restart: unless-stopped
    networks:
      - web
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - /opt/traefik/traefik.toml:/traefik.toml
      - /opt/traefik/acme.json:/acme.json
    # REMOVE this section if you don't want the dashboard/API
    labels:
      - "traefik.enable=true"
      - "traefik.frontend.rule=Host:example.com"
      - "traefik.port=8080"

networks:
  web:
    external: true
```

☝️ **Remember to replace `example.com` in `traefik.frontend.rule`** if you keep the API.

... and the config for Traefik...
```
nano /opt/traefik/traefik.toml
```

```yaml
# Change this if needed
logLevel = "ERROR"
defaultEntryPoints = ["https","http"]

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

# REMOVE this section if you don't want the dashboard/API
[api]
entryPoint = "api"
dashboard = true

[retry]

[docker]
endpoint = "unix:///var/run/docker.sock"
domain = "mydomain"
watch = true
# I prefer to expose my containers explicitly
exposedbydefault = false

[acme]
email = "myemail"
storage = "acme.json"
entryPoint = "https"
OnHostRule = true
[acme.httpChallenge]
entryPoint = "http"
```

☝️ **Add your domain and email under `[docker]` and `[acme]`, respectively.**

... and we should be good to go! 

```
cd /opt/traefik/
docker-compose up -d
```

Check the logs (`docker-compose logs`) and head to your configured domain and you should see something like this (screenshot was taken a few versions back, it's been redesigned).
    
![Screen-Shot-2018-03-13-at-22.19.45](https://jonnev.se/content/images/2018/03/Screen-Shot-2018-03-13-at-22.19.45.png)

### Basic auth

Since we have exposed the API of Traefik we'd like to have some authentication. Basic auth is supported so let's add that. Run this for the username you want - for example `admin` - and enter your password.
```
sudo apt install apache2-utils
htpasswd -n username
```

Here's what I got for admin/admin.
```
admin:$apr1$IBj9Hfsd$kf7vXLpY4/9XD365jcp/n1
```

Now that needs to go in the `traefik.toml` but to work any `$` signs have to be escaped with another `$`.
Add this to the `[entrypoints]`-section...
```
  [entryPoints.api]
    address = ":8080"
    [entryPoints.api.auth]
     [entryPoints.api.auth.basic]
       users = [
         "admin:$$apr1$$IBj9Hfsd$$kf7vXLpY4/9XD365jcp/n1"
       ]
```

Now stop and rebuild your service...
```
docker-compose stop
docker-compose up -d
```

...and you should have basic auth!

![Screen-Shot-2018-03-13-at-22.30.58](/content/images/2018/03/Screen-Shot-2018-03-13-at-22.30.58.png)

## Add a container

Now of course to have any use for this we need a container! Why not a blog with `ghost`? 

Create a directory and a `docker-compose.yml`, remember to change the domain and add the hostname to your DNS! 👍

```
mkdir -p /opt/ghost
nano /opt/ghost/docker-compose.yml
```

```yaml
version: '2'
services:
  server:
    image: ghost:alpine
    container_name: ghost
    restart: unless-stopped
    networks:
      - web
    labels:
      # This one is important since we default to not expose
      - "traefik.enable=true"
      - "traefik.frontend.rule=Host:blog.example.com"
      - "traefik.port=2368"
      - "traefik.docker.network=web"
    volumes:
      - /opt/ghost/blog:/var/lib/ghost/content
    environment:
      - NODE_ENV=production
      - url=https://blog.example.com

networks:
  web:
    external: true
```

Run the usual `docker-compose up -d` and _voilà_ blog up with SSL/TLS and all, pure magic 😀


### Updates

2019-07-15: Updated to newest version (`v1.7.12`) and also changed to only using `traefik.toml` instead of mixing with a `command` in `docker-compose.yml`