Problem statement

In the previous article we configured Traefik to use magic domain name like traefik.me. In this article we configure it with your domain and Let’s Encrypt certificated authority.

Prerequisites

First of all you need to obtain (buy) custom domain from any domain registrar like Google Domains, GoDaddy or any other.

NOTE: In this article I’m going to use Azure DNS Zone as a domain registrar. But, the steps should be more or less the same for other registrar. The difference mostly in UI.

NOTE: Azure DNS Zone is not a domain registrar, but it allows you to manage you custom domain if you use Azure cloud platform. You can read about how to configure Azure DNS Zone and Google Domains in this article.

Configure Domain Registrar

First, we need to add service subdomain to domain registrar.

  • Obtain the public IP address for you server.
  • Go to you domain registrar and create A record. If you use Azure DNS Zone steps are following:
    • Go to you Azure DNS Zone.
    • Click + Record set button.
    • Enter Name e.g. whoami.
    • Select Type: A - Alias record.
    • Enter IP address: it is your server IP address. For testing purpose I’m going to use 127.0.0.1. A Record

Configure Traefik

Create a new docker-compose.yml file and add Traefik service with basic configuration (set docker as configuration provider, and configure HTTP and HTTPS endpoints).

version: '3.8'

services:
  reverse.proxy:
    image: traefik:v2.9
    container_name: reverse.proxy
    command:
      - --providers.docker
      - --providers.docker.exposedbydefault=false
      - --log.level=DEBUG
      - --entrypoints.websecure.address=:443
      - --entrypoints.web.address=:80
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    restart:
      unless-stopped

Add basic Let’s Encrypt configuration (set Let’s Encrypt as certificate resolver and create docker volume to store obtained certificates).

  reverse.proxy:
    command:
      - --certificatesresolvers.letsencrypt.acme.email=<YOUR EMAIL ADDRESS>
      - --certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json
      #- --certificatesresolvers.letsencrypt.acme.caServer=https://acme-staging-v02.api.letsencrypt.org/directory
    volumes:
      - traefik:/letsencrypt

volumes:
  traefik:

NOTE: During development it is highly recommended to use Let’s Encrypt staging environment, because production environment has rate limits that you can easily run out of. To use staging environment just uncomment caServer parameter.

NOTE: If you are using Let’s Encrypt staging environment, the browser will show certificate as untrusted.
Untrusted Cert

NOTE: When you switch between Let’s Encrypt environments, do not forget cleanup traefik docker volume. Because Traefik will not re-request certificate, until it is valid.

The next step is to decide which ACME challenge type (HTTP-01 or DNS-01) your are going to use.

Before we continue with ACME challenge configuration. Let’s configure our test service, because it’s configuration will be the same for both challenge types.

For testing purposes let’s create the whoami service.

  whoami:
    image: containous/whoami
    container_name: whoami
    labels:
      - traefik.enable=true
      - traefik.http.routers.whoami.rule=Host(`whoami.libertus.dev`)
      - traefik.http.routers.whoami.entrypoints=websecure
      - traefik.http.routers.whoami.tls.certresolver=letsencrypt
      - traefik.http.routers.whoami.service=whoami
      - traefik.http.services.whoami.loadbalancer.server.port=80

Basically we are telling Traefik to use whoami.libertus.dev host name over websecure (HTTPS) endpoint and to use letsencrypt certificate resolver.

HTTP-01 Challenge

IMPORTANT: the HTTP-01 challenge only works over port 80, so it cannot be used if this port is blocked on your web server.

The configuration is very simple, we just need add two command arguments to our previous configuration. Which are telling Traefik to use HTTP-01 challenge over web (HTTP) endpoint.

services:
  reverse.proxy:
    command:
      - --certificatesresolvers.letsencrypt.acme.httpchallenge=true
      - --certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web

Let’s combine all previous steps together.

version: '3.8'

services:
  reverse.proxy:
    image: traefik:v2.9
    container_name: reverse.proxy
    command:
      - --providers.docker
      - --providers.docker.exposedbydefault=false
      - --log.level=DEBUG
      - --entrypoints.websecure.address=:443
      - --entrypoints.web.address=:80
      - --certificatesresolvers.letsencrypt.acme.email=<YOUR_EMAIL_ADDRESS>
      - --certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json
      #- --certificatesresolvers.letsencrypt.acme.caServer=https://acme-staging-v02.api.letsencrypt.org/directory
      - --certificatesresolvers.letsencrypt.acme.httpchallenge=true
      - --certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - traefik:/letsencrypt
    restart:
      unless-stopped

  whoami:
    image: containous/whoami
    container_name: whoami
    labels:
      - traefik.enable=true
      - traefik.http.routers.whoami.rule=Host(`whoami.<YOUR_DOAMIN_NAME>`)
      - traefik.http.routers.whoami.entrypoints=websecure
      - traefik.http.routers.whoami.tls.certresolver=letsencrypt
      - traefik.http.routers.whoami.service=whoami
      - traefik.http.services.whoami.loadbalancer.server.port=80

volumes:
  traefik:

Basically that is it. Now you can deploy this configuration to your server and test it. In your browser navigate to https://whoami.<YOUR DOMAIN> and in response you should see the whoami response and valid certificate.

DNS-01 Challenge

The DNS challenge a bit more tricky than HTTP, because it requires access to your DNS provider via API. Basically, we need to provide Traefik access to the our DNS provider via API, that it could create DNS TXT record, and then Let’s Encrypt can use it to validate the subdomain ownership.

First of all, let’s configure Traefik to use DNS challenge. To our previous configuration add following command key.

services:
  reverse.proxy:
    command:
      - --certificatesresolvers.letsencrypt.acme.dnschallenge=true

Now, let’s specify which DNS provider we are going to use, in this example we are using azure. You can find the full list of supported providers here.

services:
  reverse.proxy:
    command:
      - --certificatesresolvers.letsencrypt.acme.dnsChallenge.provider=azure

And now, the hard part. We need to configure provider. The azure provider requires us to create azure application that will be used by Traefik to get access to the Azure DNS Zone. So, we need to fill following environment variables.

services:
  reverse.proxy:
    environment:
      - AZURE_ENVIRONMENT=<AZURE_ENVIRONMENT>
      - AZURE_RESOURCE_GROUP=<YOUR_RESOURCE_GROUP_NAME>
      - AZURE_ZONE_NAME=<YOUR_DNS_ZONE_NAME>
      - AZURE_SUBSCRIPTION_ID=<YOUR_AZURE_SUBSCRIPTION_NAME>
      - AZURE_TENANT_ID=<YOUR_AZURE_TENANT_ID>
      - AZURE_CLIENT_ID=<YOUR_APP_CLIENT_ID>
      - AZURE_CLIENT_SECRET=<YOUR_APP_CLIENT_SECRET>

Where:

  • AZURE_ENVIRONMENT
    • Most likely it should be public
  • AZURE_RESOURCE_GROUP, AZURE_ZONE_NAME, AZURE_SUBSCRIPTION_ID
    • Go to your Azure DNS Zone and on the Overview tab you can find the DNS Zone Name, Resource group and Subscription ID fields. Azure DNS Zone
  • AZURE_TENANT_ID
    • Go to Azure Active Directory page.
    • On the Overview tab, copy Tenant ID field. Azure Tenant

Next, we need to create azure app.

  • Go to Azure Active Directory page.
  • Go to App registrations tab.
  • Click New Registration button.
    • Enter Name
    • And click Register button. App Registration
  • On the Overview tab of just created app, copy the Application (client) ID to AZURE_CLIENT_ID variable. App Client ID
  • Go to the Certificates & secrets tab.
    • Click New client secret to generate a secret key.
    • Copy new secret Value to AZURE_CLIENT_SECRET variable. App Client Secret

Almost done. One more thing is left, we need to assign permissions to just created app.

  • Go to your subscription Overview page.
  • Select Access Control (IAM) tab.
  • Click + Add button.
    • Select Add role assignment option. Add Role
    • In the search box, type dns.
    • Select DNS Zone Contributor role. Select Role
    • Click Next button.
    • Click + Select members button.
    • In the search box type app name you created above and select it.
    • Click Review + Assign button. Select App

And that is it 😄. Now we can test it.

Let’s combine all previous steps together.

version: '3.8'

services:
  reverse.proxy:
    image: traefik:v2.9
    container_name: reverse.proxy
    command:
      - --providers.docker
      - --providers.docker.exposedbydefault=false
      - --log.level=DEBUG
      - --entrypoints.websecure.address=:443
      - --entrypoints.web.address=:80
      - --certificatesresolvers.letsencrypt.acme.email=<YOUR_EMAIL_ADDRESS>
      - --certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json
      #- --certificatesresolvers.letsencrypt.acme.caServer=https://acme-staging-v02.api.letsencrypt.org/directory
      - --certificatesresolvers.letsencrypt.acme.dnschallenge=true
      - --certificatesresolvers.letsencrypt.acme.dnsChallenge.provider=azure
    environment:
      - AZURE_ENVIRONMENT=<AZURE_ENVIRONMENT>
      - AZURE_RESOURCE_GROUP=<YOUR_RESOURCE_GROUP_NAME>
      - AZURE_ZONE_NAME=<YOUR_DNS_ZONE_NAME>
      - AZURE_SUBSCRIPTION_ID=<YOUR_AZURE_SUBSCRIPTION_NAME>
      - AZURE_TENANT_ID=<YOUR_AZURE_TENANT_ID>
      - AZURE_CLIENT_ID=<YOUR_APP_CLIENT_ID>
      - AZURE_CLIENT_SECRET=<YOUR_APP_CLIENT_SECRET>      
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - traefik:/letsencrypt
    restart:
      unless-stopped

  whoami:
    image: containous/whoami
    container_name: whoami
    labels:
      - traefik.enable=true
      - traefik.http.routers.whoami.rule=Host(`whoami.<YOUR_DOAMIN_NAME>`)
      - traefik.http.routers.whoami.entrypoints=websecure
      - traefik.http.routers.whoami.tls.certresolver=letsencrypt
      - traefik.http.routers.whoami.service=whoami
      - traefik.http.services.whoami.loadbalancer.server.port=80

volumes:
  traefik:

Run docker.

docker compose up

On Azure DNS Zone page you should see that a new TXT Record was created. DNS Challenge

In your browser navigate to https://whoami.<YOUR DOMAIN> and in response you should see the whoami response and valid certificate. Result

Source Code

The source code for this article you can find here .