Traefik Setup with Docker Compose
Traefik
Traefik is an edge router, reverse proxy, and load balancer that allows you — among other
things — to expose docker containers (web services) to the Web in a very easy way.
Once set up, you never have to adjust its configuration again. Whenever you have a new service,
you simply set some labels on that docker container and Traefik will forward requests as required/
configured. Automatic Let's Encrypt certificate generation and renewal included :)
This post briefly explains you how to setup Traefik with Docker Compose and make some basic
configuration.
More details can be found in the official Traefik Docs. So look
there, if you need more information about any topic or want to know what else is possible (there's
a lot possible besides the things we configure in this post)!
The following diagram (taken from the Traefik docs) gives a very clear overview of what Traefik actually does:
We'll start with some static configuration, followed by the simple docker compose setup, dynamic configuration and, at the end, we'll setup SSL encryption.
Static Configuration
There are three ways how to provide configuration for Traefik. Either via a configuration file, command line options, or via environment variables. We go with the first option, as it is the clearest in my opinion.
Additionally, you can provide dynamic configuration (which may, as the name suggests, dynamically change during runtime). We'll come back to that later.
For the basic configuration, create a file called traefik.yml
. Important parts are explained
by comments:
# Api and, additionally, a monitoring dashboard (checkout docs for more,
# disable in production if not required!).
api:
dashboard: true
insecure: true
# Entrypoints (ports) Traefik should listen to; here we define two: "http"
# for unencrypted traffic, and "https" for SSL-encrypted traffic (port 443).
entryPoints:
http:
address: ":80"
https:
address: ":443"
# We define two providers
providers:
# First, a docker provider, which allows us to enable routing to any Docker
# container by setting some specific labels on the container. Example will
# follow below ;).
docker:
endpoint: "unix:///var/run/docker.sock"
# This ensures, that we manually have to request containers to be "added"
# to Traefik.
exposedByDefault: false
# Second, a dynamic configuration file - we'll come back to that file below.
file:
filename: "/dynamic_conf.yml"
# For automatic Let's Encrypt certificate generation, we define a "letsencrypt"
# resolver. It may store the certificates in the defined file/storage and should
# use the http endpoint (defined above) for the http challenge (i.e., for
# generating/ requesting the certificates).
certificatesResolvers:
letsencrypt:
acme:
email: yourname@yourmail.com
storage: "/letsencrypt/acme.json"
httpChallenge:
entryPoint: http
Docker Compose File
Now, we need a very simple docker-compose.yml
(same directory as traefik.yml
) to create a
Traefik instance:
version: '3'
services:
# one service: Traefik
traefik:
# Checkout Docker Hub or the Traefik docs for the latest version (or use :latest)!
image: "traefik:v2.5"
restart: unless-stopped
# Port mapping: required for http and https traffic, as wells as for the
# Traefik Dashboard (8080), but this is optional (disable in production, if
# not required)!
ports:
- "80:80"
- "443:443"
- "8080:8080" # optional!
# A Docker network is used to connect Traefik with other Docker containers.
networks:
- traefik_proxy
volumes:
# Storage for SSL certificates:
- "./letsencrypt:/letsencrypt"
# Required for the Docker provider:
- "/var/run/docker.sock:/var/run/docker.sock:ro"
# Our configuration file:
- "./traefik.yml:/traefik.yml:ro"
# Our dynamic configuration, which can be adjusted during runtime:
- "./dynamic_conf.yml:/dynamic_conf.yml"
networks:
traefik_proxy:
external: true
You can now already start Traefik (create an empty dynamic_conf.yml
file):
docker compose up -d
# check logs
docker compose logs -f traefik
Next, we want to finally use our proxy and let it forward traffic to some docker containers.
Using Traefik with Docker Containers
Configuration takes place via labels on Docker Containers. These labels tell Traefik to route traffic to this specific container and they also tell traffic how to do that (e.g., which domain, which entrypoint, etc.).
A simple whoami service (you can put this, for testing, in the same file as the Traefik service):
version: '3'
services:
whoami:
image: "traefik/whoami"
labels:
# We want to use Traefik!
- "traefik.enable=true"
# This container receives requests, send to the specified domain. For that, we create a
# router called "whoami" with a rule specifying this host.
- "traefik.http.routers.whoami.rule=Host(`whoami.your-domain.com`)"
# The "whoami" router should listen on the http entrypoint
- "traefik.http.routers.whoami.entrypoints=http"
For this to work, you obviously need to control the whoami.your-domain.com
domain and make it
point to the IP Traefik is running on.
After starting the service, Traefik will automatically detect the new container and route any
requests to the specified domain to this container. Well done :)
Dynamic Configuration
As mentioned before, the dynamic configuration allows you to configure stuff on demand during
runtime. It is applied without any restart required. We'll do two things: specify the TLS version
to use and setup a "secureHeaders" middleware.
Create the dynamic_conf.yml
file, if you didn't already.
Specify SSL version
To set TLS 1.2 as a minimal version used (among other stuff), add the following:
tls:
options:
default:
minVersion: VersionTLS12
cipherSuites:
- TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
- TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
- TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305
- TLS_AES_128_GCM_SHA256
- TLS_AES_256_GCM_SHA384
- TLS_CHACHA20_POLY1305_SHA256
curvePreferences:
- CurveP521
- CurveP384
sniStrict: true
These options are taken from the Mozilla SSL Configuration Generator.
Secure Headers
Add the following to the dynamic_conf.yml
:
http:
middlewares:
secureHeaders:
headers:
browserXssFilter: true
contentTypeNosniff: true
frameDeny: true
sslRedirect: true
# HSTS Configuration
stsIncludeSubdomains: true
stsPreload: true
stsSeconds: 31536000
customFrameOptionsValue: "SAMEORIGIN"
This basically defines a middleware, that — when used by a Traefik router — sets a secure set of headers by default on responses. Checkout MDN Docs for more information about each header.
You can apply this middleware by adding the following label (last in the example) to a Docker service:
version: '3'
services:
whoami:
image: "traefik/whoami"
labels:
- "traefik.enable=true"
- "traefik.http.routers.whoami.rule=Host(`whoami.your-domain.com`)"
- "traefik.http.routers.whoami.entrypoints=http"
- "traefik.http.routers.whoami.middlewares=secureHeaders@file"
We tell the whoami router to use the secureHeaders middleware, which is provided by the file provider (we set this name in our static configuration).
File/Directory Structure
At the end, your file structure should look like this:
marvinweber@server:/app/traefik$ ll
total 24
drwxrwxr-x 3 marvinweber marvinweber 4096 Jan 5 16:16 ./
drwxr-xr-x 10 marvinweber marvinweber 4096 Apr 2 2021 ../
-rw-rw-r-- 1 marvinweber marvinweber 458 Nov 29 2020 docker-compose.yml
-rw-rw-r-- 1 marvinweber marvinweber 743 Nov 29 2020 dynamic_conf.yml
drwxr-xr-x 2 root root 4096 Jan 15 2021 letsencrypt/
-rw-rw-r-- 1 marvinweber marvinweber 396 Nov 29 2020 traefik.yml
Advanced
HTTPS / SSL Encryption
Listening on SSL (404) Traffic is easy. We just need a router listening on the https entrypoint:
version: '3'
services:
whoami:
image: "traefik/whoami"
labels:
- "traefik.enable=true"
- "traefik.http.routers.whoami.rule=Host(`whoami.your-domain.com`)"
- "traefik.http.routers.whoami.entrypoints=http"
- "traefik.http.routers.whoami.middlewares=secureHeaders@file"
# New router for the SSL traffic (note, that is has a different name: "whoami-https")!
- "traefik.http.routers.whoami-https.entrypoints=https" # it listens on the https entrypoint
# use the same domain...
- "traefik.http.routers.whoami-https.rule=Host(`whoami.your-domain.com`)"
- "traefik.http.routers.whoami-https.tls=true"
# We use the certresolver defined in our static configuration
- "traefik.http.routers.whoami-https.tls.certresolver=letsencrypt"
Traefik will now automatically request a Let's encrypt certificate for the specified domain. You
will then be able to open: https://whoami.your-domain.com
HTTPS Redirect (SSL only traffic)
For this, we dynamically (via labels) create a new middleware whoami-https-redirect which uses the https redirect scheme (i.e., it redirects to https://, if http:// is used). Then, we tell the whoami router (the one accepting http requests (non-ssl)) to use this middleware.
version: '3'
services:
whoami:
image: "traefik/whoami"
labels:
- "traefik.enable=true"
- "traefik.http.routers.whoami.rule=Host(`whoami.your-domain.com`)"
- "traefik.http.routers.whoami.entrypoints=http"
- "traefik.http.routers.whoami.middlewares=secureHeaders@file"
- "traefik.http.routers.whoami-https.entrypoints=https" # it listens on the https entrypoint
- "traefik.http.routers.whoami-https.rule=Host(`whoami.your-domain.com`)"
- "traefik.http.routers.whoami-https.tls=true"
- "traefik.http.routers.whoami-https.tls.certresolver=letsencrypt"
# Middleware for HTTPS forwarding:
- "traefik.http.middlewares.whoami-https-redirect.redirectscheme.scheme=https"
- "traefik.http.routers.whoami.middlewares=whoami-https-redirect"
That's it!