Traefik Proxy (20240908)

This guide details how to deploy Traefik using Docker compose (with Dockge) and Cloudflare as our DNS Challenge provider to generate Let’s Encrypt certificates for local infrastructure (not internet-accessible) and use basic authentication to protect the Traefik Dashboard. The configuration files presented can also be used with other deployment methods, such as Unraid.

Aug 31, 2024
This guide details how to deploy Traefik using Docker compose (with Dockge) and Cloudflare as our DNS Challenge provider to generate Let’s Encrypt certificates for local infrastructure (not internet-accessible) and use basic authentication to protect the Traefik Dashboard. The configuration files presented can also be used with other deployment methods, such as Unraid.
 
Revision: 20240908-0 (init: 20240831)
 
This post details installing the Traefik Proxy using docker compose (with Dockge). We perform a deployment using a traefik.yaml configuration file and dynamic independent configurations to provide proxying for docker containers and external services.
 

Preamble

Traefik is an open-source reverse proxy and load balancer designed for cloud-native applications. It is particularly suited for microservices and containerized environments like Docker and Kubernetes.
Traefik serves several key functions:
  • Automatic Service Discovery for Docker services and routes traffic to them without requiring manual configuration (when enabled)
  • Load Balancing for HTTP, TCP, and UDP traffic.
  • It supports SSL/TLS termination and automatically generates certificates using ACME providers like Let's Encrypt.
  • Dynamic Configuration: It continuously updates its configuration in real time, eliminating the need for restarts or manual updates when services are added or removed.
  • It includes various middleware options for tasks such as authentication or rate limiting.
Traefik is based on the concept of EntryPoints, Routers, Middlewares and Services.
The main features include dynamic configuration, automatic service discovery, and support for multiple backends and protocols.
  1. EntryPoints: EntryPoints are the network entry points into Traefik. They define the port which will receive the packets, and whether to listen for TCP or UDP.
  1. Routers: A router is in charge of connecting incoming requests to the services that can handle them.
  1. Middlewares: Attached to the routers, middlewares can modify the requests or responses before they are sent to your service
  1. Services: Services are responsible for configuring how to reach the actual services that will eventually handle the incoming requests.

Acknowledgments

The configuration files for this guide were developed thanks to the many other great primers, in particular:

How to use this guide

We will use a set of common values that we invite you to update to match your setup:
  • example.com is our domain (zone), and its DNS provider is Cloudflare.
  • 192.168.222.11 is the static IP of the host running the Traefik service (among others).

Requirement: Cloudflare DNS Challenge Token

👉
Please review the Preamble and Prerequisites section of a previous blog post on Reverse Proxy for some of the concepts we will use here as well.
To use this guide you will need to have a DNS hosted at Cloudflare.
Pre-requisite:
  • An A record for *.example.com (wildcard) setup at Cloudflare pointing to the private IP on the host that will host Traefik: 192.168.22.11
    • Type: A / Name: * / Content: 192.168.22.11
      Type: A / Name: * / Content: 192.168.22.11
  • A custom Cloudflare Token:
    • From the Cloudflare Dashboard, on the “Overview” section, look for “Get your API token” (on the left side), then “Create Token → Create Custom Token”
    • Custom Token: Zone Read + Zone DNS Edit + only specific zone
      Custom Token: Zone Read + Zone DNS Edit + only specific zone
    • Give it a name, for example traefik (example.com)
    • For “Permissions”, select:
      • “Zone - Zone Settings - Read” and “Zone - Zone - Read” to allow the token to Read DNS Zones
      • “Zone - DNS - Edit” to allow the token to edit DNS records (required for the DNS-01 challenge)
    • For “Zone Resources”, select:
      • “Include - Specific Zone” select the example.com zone.
    • After the token is created, make sure to store it in a safe place, it will not be displayed again.

Docker compose setup

👉
We will use Dockge to deploy Traefik. Please refer to a previous Dockge post for details on setting it up.
From the Dockge dashboard “+Compose” a new traefik stack and “Save” it (do not deploy just yet).
We need to prepare a few files in the /opt/stacks/traefik directory (make sure you are able to write in the directory):
# Create the configuration base directory (config) # and the dynamic configuration directory (conf) mkdir -p config/conf # Create an empty acme.json file to store our Certificates details touch config/acme.json # Change the file permission. An error will occur if this is not done chmod 600 config/acme.json
 
📁
Most of the files mentioned in the next sections are downloadable from https://github.com/mmartial/geekierblog-artifacts/tree/main/20240831-traefik
♻️
Although we are deploying this setup using Dockge, it is possible to use the following files with other deployment methods, such as from Unraid’s Community Applications — in which case we would not make use of the provided compose.yaml file and would use the Unraid template’s environment variables to pass some values.

traefik.yaml

Download our traefik.yaml file and:
  • replace the example.com entries (3 of those), and
  • select a resolver.
 
We added comments to the file to help you review its content and adapt some sections accordingly. Among the settings the file sets:
  • the global: section checks for updates and disables usage reporting
  • the api: section enables the dashboard (we will specify its url on the command line)
  • the entrypoints: section sets the http and https ”entrypoints” which will be used by our “routers” configuration.
    • The http settings always redirects entries to the https.
    • The https settings specify the certResolver name we will use as well as the domains used, including our *.example.com wildcard certificate.
      • the middlewares section specifies some @file rules; those will be defined in the config/conf directory.
  • the providers: section sets
    • the file ”providers” as the content of the conf directory
    • the docker ”provider” for
      • containers that are on the traefik_default network (this network will be created by Dockge when the stack is started)
      • container are not exposed by default: a traefik.enable label must be set to true for the container to be seen by Traefik (see the whoami example in the compose.yaml file)
      • will automatically name the resulting URL as the container’s name (ex: dozzle.example.com). In case of potential service collisions, renaming each container is an option or a rule label can be used: traefik.http.routers.my-container.rule=Host(`container-url.example.com`)
  • the certificatesResolvers: section is where the ACME exchange with Let’s Encrypt using Cloudflare for the DNS Challenge is performed.
    • the resolvers: section will verify that the _acme TXT entry was added to the Zone (example.com)’s DNS. The recommended entry for Cloudflare is 1.1.1.1. If you have a system that performs DNS over HTTPs upgrades, you will want to point that resolver directly to the IP of that system.

Dynamic Configurations (config/conf destination)

💡
Note that the file name does not matter, only the file content does, although it is recommended to name those files in such a way as to enable you to find those later.
The following files go in the /opt/stacks/traefik/config/conf folder and are all available for download at the same GitHub repo.

tls.yaml

Download the tls.yaml file. This file contains some requirements for minimum supported ciphers and TLS versions for access to the services.

middlewares-securityHeaders_light.yaml

The middlewares-securityHeaders_light.yaml file defines a limited number of restrictions. It is usable for https-upgrading of private accessible networks only, as it will among other allow iFrames embedding. It is the default entry used by the https entrypoints.

middlewares-securityHeaders.yaml

The middlewares-securityHeaders.yaml file defines a more restrictive middleware that can be used instead of the light option.

middlewares-gzip.yaml

The middlewares-gzip.yaml file specifies that connections should use the gzip compression.

middlewares-dashboardauth.yaml

In the middlewares-dashboardauth.yaml is a basicAuth configuration that we will be using to protect the Traefik Dashboard. Modify the value of the user and password by using htpasswd (example command line in the comments).

middlewares-localonly.yaml

Download the middlewares-localonly.yaml file. It defines another “middleware” to limit access to localhost, the LAN subnet (here 192.168.22.1/24, adapt as needed) and commented is the Tailscale 100. range (CGNAT: 100.64.0.0/10). If you have another VPN setup to reach your subnet, add the tunnel’s network pool to that list.
We note that we are not making use of this middleware in any entrypoint.

compose.yaml

Obtain the compose.yaml file and fill the Dockge entry with it. You will need to replace 2x entries with example.com with the value of your domain (for the dashboard and the whoami service).
We need to create an environment variable for this stack (replace with the token you obtained earlier) CF_DNS_API_TOKEN=REPLACE_WITH_YOUR_SECRET_API_KEY
Depending on which guide you follow, you will see most entries that we placed in configuration files within the command: section of the compose.yaml (ours is empty). In the labels: section we have added definitions for the Traefik dashboard configuration: its URL (the rule entry), and its middlewares (here the dashboardauth@file , i.e. dashboardauth middleware found in a file placed in the directory set in the file providers:.
The definition of the whoami stack explains simply how to have Traefik create a reverse proxy for the service, the traefik.enable=true label tells it to create a rule. By default, the URL would be using the container’s name, this is overridden by the rule entry that sets the full URL. This also defines that the routers:’s name is whoami: traefik.http.routers.whoami.rule. This name is again used to specify the entrypoint: traefik.http.routers.whoami.entrypoints=https.
 
After saving and starting the stack, within a few seconds, you will have some content within the /opt/stacks/traefik/acme.json file, among which the email address you specified for Let’s Encrypt, and the Certificates for your domain.
Once this is confirmed, the whoami service will be available at https://whoami.example.com/. When accessing it, we will see details of the connection, confirming that Traefik is up and running and upgrading connections to HTTPs for our example.com domain.
Accessing the Traefik Dashboard is done by going to the URL set in the rule entry of the compose.yaml: https://traefik.example.com/ with the caveat that you will be prompted for the username and password that you set in the middlewares-dashboardauth.yaml file (the defaults in that file are admin and password).

Adding services

Dockge containers

To add a container existing within Dockge to the list of url proxied by Traefik, we need to extend that stack’s compose.yaml with additional content.
We will use the Dozzle service as an example, as detailed in the Dockge post. Stop that stack before editing it.
The current’s service compose.yaml is as follows:
services: dozzle: container_name: dozzle image: amir20/dozzle:latest volumes: - /var/run/docker.sock:/var/run/docker.sock - ./data:/data ports: - 8008:8080 environment: DOZZLE_AUTH_PROVIDER: simple DOZZLE_ENABLE_ACTIONS: true labels: com.centurylinklabs.watchtower.enable: true
To have the service being recognized by Traefik, we need to
  • Make the service use the traefik_default network
networks: - traefik_default
, and recognize the network as external to the container:
networks: traefik_default: external: true
  • Add labels for the container to be seen by Traefik, specify its url (rule) and specifying the https entrypoint (as defined in our traefik.yaml's entryPoints: section):
labels: - traefik.enable=true - traefik.http.routers.dozzle.rule=Host(`dozzle.example.com`) - traefik.http.routers.dozzle.entrypoints=https
↗️
Note that we have to define the “name” of the service, here dozzle (where it was whoami for that service as included in our compose.yaml file)
 
The final compose.yaml is as follows:
services: dozzle: container_name: dozzle image: amir20/dozzle:latest volumes: - /var/run/docker.sock:/var/run/docker.sock - ./data:/data ports: - 8008:8080 environment: DOZZLE_AUTH_PROVIDER: simple DOZZLE_ENABLE_ACTIONS: true networks: - traefik_default labels: - traefik.enable=true - traefik.http.routers.dozzle.rule=Host(`dozzle.example.com`) - traefik.http.routers.dozzle.entrypoints=https - com.centurylinklabs.watchtower.enable=true networks: traefik_default: external: true
After restarting the stack, we can now access it at https://dozzle.example.com/
💡
Because the container_name is dozzle we do not need to add the rule label, as the service url would have been the value of that variable (per the defaultRule for docker providers: entry in the traefik.yaml). We recommend the habit of using all three labels.

Adding an external service

To add a proxy for a service that is not on the Dockge stack or on another host, we have to define specific routers: and services:. Because we are using files to store our configuration, create a new yaml file in the config/conf directory that is being watched by Traefik. We have added an exampleroute-ollama.yaml in the GitHub with all its content commented (to avoid the content from being used if you copied all the files).
Its uncommented content would be as follows:
http: # Routers and Services -- https://doc.traefik.io/traefik/routing/overview/ routers: ollama: # name entryPoints: - https # use the "https" entrypoint rule: 'Host(`ollama.example.com`)' # Destination URL to resolve service: ollama # name for services matching entry # middlewares: # specify middleware(s) if any # - "securityHeaders@file" # the default "https" entrypoint follows the "light" security headers version, uncomment this section to enble the more restrictive version services: ollama: loadBalancer: servers: - url: http://192.168.22.12:11434/
This define a new ollama.example.com URL the would reverse-proxy to http://192.168.22.12:11434/ (notice the different IP on our subnet).
Simply adding the file in the folder will enable the new proxied entry.
We have commented the more restrictive security headers as a demonstration of how to include the additional middlewares to the default one. If using those as labels, this ollama example would be added using traefik.http.routers.ollama.middlewares=securityHeaders@file

Docker labels

The following are useful for copy→adapt→paste.

Generic labels

When using Traefik, adding a new service to the reverse proxy can be as simple as extending the labels: section of the compose.yaml file with something akin to:
labels: - traefik.enable=true - traefik.http.routers.SLUG.entrypoints=https - traefik.http.routers.SLUG.rule=Host(`SLUG.example.com`)
  • replace example.com with your domain (1x replacement)
  • update SLUG to reflect the base URL of the service you are exposing (3x replacements)

Unraid labels

(This is only usable if Unraid was Traefik’s deployment method)
Although it is possible to manually “Add” “labels” for each required label using the Unraid WebUI, it is also possible to modify the Docker service’s XML file.
Those are in the /boot/config/plugins/dockerMan/templates-user folder and are named my-<SERVICE>.xml . Once we have found the match entry, we can edit it and copy the adapted lines before the final </Container> line:
<Config Name="traefik.enable=true" Target="traefik.enable" Default="" Mode="" Description="" Type="Label" Display="always" Required="false" Mask="false">true</Config> <Config Name="traefik.http.routers.SLUG.entrypoints=https" Target="traefik.http.routers.SLUG.entrypoints" Default="" Mode="" Description="" Type="Label" Display="always" Required="false" Mask="false">https</Config> <Config Name="traefik.http.routers.SLUG.rule=Host(`SLUG.example.com`)" Target="traefik.http.routers.SLUG.rule" Default="" Mode="" Description="" Type="Label" Display="always" Required="false" Mask="false">Host(`SLUG.example.com`)</Config>
Replace:
  • example.com with your domain (2x replacements)
  • SLUG with the base URL of the service whose port you are exposing — this will replace the values in .routers. and Host( (6x replacements)
💡 After modifying the XML file, from Unraid’s “Docker” tab, Edit the Service then make and undo any change to enable the Save button. Save it to restart the updated service.

Further Reading

Revision History

  • 20240908-0: Added Unraid use-case and copy→adapt→paste content
  • 20240903-0: Added a “light” security headers file as an option for private network.
  • 20240901-0: Added direct links to each file + additional link in Further Reading section
  • 20240831-0: Initial release