Exposing Traefik UI on secure local domain

I'm trying to expose Traefik UI on https://traefik.example.local but nothing I do seem to work. I would appreciate pointing out my mistake.

Using:

  • traefik docker image v2.10.7
  • docker 24.0.7
  • docker compose 2.21.0
  1. Add the necessary entries to /etc/hosts:
$ cat /etc/hosts
127.0.0.1       localhost
127.0.0.1       traefik.example.local
127.0.0.1       example.local
  1. Generate self-signed certificates with mkcert:
mkdir -p certs && mkcert -key-file certs/key.pem -cert-file certs/cert.pem "localhost" "0.0.0.0" "127.0.0.1" "*.example.local" "example.local"
mkcert -install
  1. Traefik static config:
global:
  checkNewVersion: false

serversTransport:
  insecureSkipVerify: true

api:
  dashboard: true

log:
  level: DEBUG

ping: {}

providers:
  docker:
    exposedByDefault: false
    watch: true
  file:
    directory: /etc/traefik/dynamic
    watch: true

entryPoints:
  web:
    address: ":80"
    http:
      redirections:
        entryPoint:
          to: websecure
          scheme: https
  websecure:
    address: ":443"
  1. Traefik dynamic config:
tls:
  stores:
    default:
      defaultCertificate:
        certFile: /etc/traefik/certs/cert.pem
        keyFile: /etc/traefik/certs/key.pem
certificates:
  - certFile: /etc/traefik/certs/cert.pem
    keyFile: /etc/traefik/certs/key.pem
    stores:
      - default
  1. docker-compose.yml:
services:
  traefik:
    image: traefik:v2.10.7
    container_name: example-traefik
    init: true
    security_opt:
      - no-new-privileges:true
    ports:
      - 80:80
      - 443:443
    volumes:
      - ./certs:/etc/traefik/certs:ro
      - ./traefik/traefik.yml:/etc/traefik/traefik.yml:ro
      - ./traefik/dynamic:/etc/traefik/dynamic:ro
      - /var/run/docker.sock:/var/run/docker.sock:ro
    networks:
      - example
    healthcheck:
      test: traefik healthcheck
      interval: 3s
      timeout: 2s
      retries: 3
    labels:
      - traefik.docker.network=example
      - traefik.http.routers.dashboard.rule=Host(`traefik.example.local`) && (PathPrefix(`/api`) || PathPrefix(`/dashboard`))"
      - traefik.http.routers.dashboard.service=api@internal
volumes:
  certs:
    driver: local
    driver_opts:
      type: none
      device: ./certs
      o: bind

networks:
  example:
    name: example

With the above, if it try to access the subdomain I'm getting 404:

curl -k https://traefik.example.local/dashboard/ # 404 page not found
curl --header 'Host:traefik.example.local' 'https://localhost/dashboard/' # 404 page not found

I had a look at the official documentation but its not very helpful. Here is a minimal repo i setup to illustrate the issue. Apart from master there are two other branches: trying-with-explicit-https-entrypoint and trying-with-loadbalancer - which didn't work for me either.

Check in debug log if the dynamic config is read and if the certs are read.

Sidenote: I have never seen a configuration like this. Sure it's correct?

volumes:
  certs:
    driver: local
    driver_opts:
      type: none
      device: ./certs
      o: bind