Setting up HTTPS using Traefik as a Reverse Proxy with Docker Compose
For those opting to use HTTPS locally, integrating Traefik as a reverse proxy with Docker Compose provides a straightforward approach. Here's a brief guide.
Prerequisites
The following tools will be used in this guide:
- Docker Compose: A tool for defining and managing multi-container Docker applications in a single file. Starting with Compose version 2, Docker Compose is automatically included in the Docker installation and can be accessed using
docker compose <your-command>
. - Traefik: A modern reverse proxy and load balancer designed to simplify the routing of application traffic and its integration with platforms like Docker.
Table of Contents
- Generate locally-trusted certificates with mkcert
- Setting up the Root CA
- Generating a Service-specific Certificate
- Setting Up HTTPS with Traefik and Docker Compose
- Initiating the Setup
- Troubleshooting
- Bonus: Traefik configuration with Docker Compose labels
Step 1: Generate locally-trusted certificates with mkcert
mkcert is a utility that enables you to generate locally-trusted certificates. Unlike some other certificate tools which produce self-signed certificates, mkcert offers certificates that are inherently trusted by your computer, facilitating local HTTPS development.
Installation: Please see the official installation guide for instructions specific to your platform.
Step 2: Setting up the Root CA
Run the command:
mkcert -install
This action establishes a local CA (Certificate Authority) in the system trust store, ensuring certificates made by mkcert are automatically recognized as trustworthy.
Step 3: Generating a Service-specific Certificate
To create a certificate for a designated service, use:
mkcert whoami.localhost
This command results in the creation of two files: whoami.localhost.pem
(containing the certificate) and whoami.localhost-key.pem
(containing the private key). These files will be utilized in subsequent steps.
Verify that the hostname is linked to your system (typically 127.0.0.1), e.g. change your
/etc/hosts
file if necessary. For hostnames ending with *.localhost (e.g.whoami.localhost
), your system should automatically resolve the hostname to your machine, no further configuration required (see RFC 6761).
Step 4: Setting Up HTTPS with Traefik and Docker Compose
For secure communication using HTTPS, it's essential to configure Traefik with the necessary TLS settings. To achieve this, you need both the certificate file and its definition in Traefik's dynamic configuration.
1. Transferring the Certificate File
In your docker-compose.yml
, the certificate and its corresponding private key are mounted into the Traefik container using volumes:
volumes:
- "./whoami.localhost.pem:/whoami.localhost.pem"
- "./whoami.localhost-key.pem:/whoami.localhost-key.pem"
This ensures that Traefik has access to the necessary files for establishing a secure HTTPS connection.
2. Defining the Certificate in Traefik's Dynamic Configuration
In your traefik_dynamic_conf.yml
, specify the certificate file and the private key file for TLS:
tls:
certificates:
- certFile: /whoami.localhost.pem
keyFile: /whoami.localhost-key.pem
3. Configuring the Router
In this section, we will configure our router to connect to the whoami service, a straightforward web service available on Docker Hub. It serves as a suitable example for this demonstration.
For the router to handle HTTPS requests, set tls: true
and use the websecure
entrypoint:
http:
routers:
whoamiRouter:
rule: "Host(`whoami.localhost`)"
service: whoamiService
tls: true
entrypoints:
- websecure
Since we are not utilizing the Docker provider in this setup, the whoamiService
must be explicitly defined:
services:
whoamiService:
loadBalancer:
servers:
- url: http://whoami:80
While the whoamiService
runs unencrypted on port 80 internally, it's safeguarded within Docker's internal network. Traefik serves as a reverse proxy, only exposing its service to the external environment, thus maintaining secure internal operations.
For an approach using the Docker provider with Docker labels, refer to the concluding section of this post.
Complete Configuration:
For a holistic view, refer to the configuration snippets provided:
Docker Compose (docker-compose.yml
):
version: "3.3"
services:
traefik:
image: "traefik:v2.10.5"
command:
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
- "--providers.file.filename=/traefik_dynamic_conf.yml"
ports:
- "80:80"
- "443:443"
volumes:
- "./traefik_dynamic_conf.yml:/traefik_dynamic_conf.yml"
- "./whoami.localhost.pem:/whoami.localhost.pem"
- "./whoami.localhost-key.pem:/whoami.localhost-key.pem"
whoami:
image: "traefik/whoami"
Traefik Dynamic Configuration (traefik_dynamic_conf.yml
):
tls:
certificates:
- certFile: /whoami.localhost.pem
keyFile: /whoami.localhost-key.pem
http:
routers:
whoamiRouter:
rule: "Host(`whoami.localhost`)"
service: whoamiService
tls: true
entrypoints:
- websecure
services:
whoamiService:
loadBalancer:
servers:
- url: http://whoami:80
Initiating the Setup
With the configurations appropriately set, you can initiate the services using Docker Compose. Simply run the following command:
docker compose up
Once the services are up and running, navigate to https://whoami.localhost
in your browser. Given the correct setup of the TLS certificates, your browser should automatically trust the certificate, allowing for a secure HTTPS connection.
Troubleshooting
Browser Error: "Your connection is not private"
If you encounter the following error in your browser: "Your connection is not private", it's likely that the certificate is not trusted by your system or that traefik does not use your certificate at all.
Check which certificate is used by Traefik by navigating to https://whoami.localhost
and inspecting the certificate in your browser (click on the lock icon in the address bar).
If the name shows something like "TRAEFIK DEFAULT CERT", Traefik is not using your certificate. In this case, check your configuration and ensure that the certificate is mounted correctly:
- check the volume mount in your
docker-compose.yml
file, see Transferring the Certificate File - Check that you are using the same name and path in your
traefik_dynamic_conf.yml
file, see Defining the Certificate in Traefik's Dynamic Configuration
If the browser shows correctly your generated certificate, make sure that your mkcert setup is correct and that the certificate is trusted by your system. See Step 1: Generating the Certificate or the official mkcert documentation for more information.
Service Error: "404 page not found"
When you want to visit your service (e.g. https://whoami.localhost) and get a "404 page not found" error, it's likely that the router is not configured correctly. Make sure that you added the websecure
entrypoint to your router and added the tls: true
option in your dynamic traefik config.
traefik_dynamic_conf.yml:
http:
routers:
# ...
whoamiRouter:
# ...
tls: true
entrypoints:
- websecure
OR if you are using docker labels:
services:
whoami:
# ...
labels:
# ...
- "traefik.http.routers.whoamiRouter.tls=true"
- "traefik.http.routers.whoamiRouter.entrypoints=websecure"
Make sure to replcae whoamiRouter
with the name of your router or service.
See Complete Configuration: for a complete example.
Bonus: Traefik configuration with Docker Compose labels
While Docker labels offer simplicity, they introduce additional concepts that might require further understanding, such as the integration of docker.sock through a volume and Traefik's automatic port and URL recognition. A direct approach, though slightly verbose, provides clarity and explicitness. Notably, the TLS configuration mandates a separate file, irrespective of the method used.
For reference, here's a configuration using Docker Compose labels:
docker-compose.yml:
version: "3.3"
services:
traefik:
image: "traefik:v2.10.5"
command:
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--providers.file.filename=/traefik_dynamic_conf.yml"
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
ports:
- "80:80"
- "443:443"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock"
- "./traefik_dynamic_conf.yml:/traefik_dynamic_conf.yml"
- "./whoami.localhost.pem:/whoami.localhost.pem"
- "./whoami.localhost-key.pem:/whoami.localhost-key.pem"
whoami:
image: "traefik/whoami"
labels:
- "traefik.enable=true"
- "traefik.http.routers.whoami.rule=Host(`whoami.localhost`)"
- "traefik.http.routers.whoami.entrypoints=websecure"
- "traefik.http.routers.whoami.tls=true"
traefik_dynamic_conf.yml:
tls:
certificates:
- certFile: /whoami.localhost.pem
keyFile: /whoami.localhost-key.pem