An illustration of a secure lock icon merged with graphic elements which represents a cloud network, representing the establishment of a secure connection in a local setup.

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

  1. Generate locally-trusted certificates with mkcert
  2. Setting up the Root CA
  3. Generating a Service-specific Certificate
  4. Setting Up HTTPS with Traefik and Docker Compose
    1. Transferring the Certificate File
    2. Defining the Certificate in Traefik's Dynamic Configuration
    3. Configuring the Router
    4. Complete Configuration
  5. Initiating the Setup
  6. Troubleshooting
    1. Browser Error: "Your connection is not private"
    2. Service Error: "404 page not found"
  7. 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:

  1. check the volume mount in your docker-compose.yml file, see Transferring the Certificate File
  2. 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