Traefik Dashboard 404 Error

I'm trying to get a minimal setup to work with Docker Compose.
The problem is, attempting to access the dashboard returns a "404 page not found" and acme.json remains empty.

traefik.yml

providers:
  docker:
    network: public
    watch: true
    exposedByDefault: false
entryPoints:
  web:
    address: ":80"
  websecure:
    address: ":443"
    http:
      tls:
        certResolver: le
certificatesResolvers:
  le:
    acme:
      email: admin@*******.ca
      storage: /certificates/acme.json
      tlsChallenge: {}
api: {}
logs:
  level: DEBUG

compose.yml

services:
  traefik:
    image: traefik:v2.10
    container_name: traefik
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - traefik-certificates:/certificates
    configs:
      - source: traefik-static
        target: /etc/traefik/traefik.yml
    networks:
      - public
    deploy:
      labels:
        traefik.enable: "true"
        traefik.http.routers.dashboard.entryPoints: "websecure"
        traefik.http.routers.dashboard.rule: "Host(`traefik.*******.ca`)"
        traefik.http.routers.dashboard.tls: "true"
        traefik.http.routers.dashboard.service: "api@internal"

configs:
  traefik-static:
    file: ./traefik.yml

volumes:
  traefik-certificates:
    name: traefik-certificates

networks:
  public:
    name: public

service logs:

traefik  | time="2024-01-29T23:25:26Z" level=info msg="Configuration loaded from file: /etc/traefik/traefik.yml"
traefik  | time="2024-01-29T23:25:26Z" level=info msg="Traefik version 2.10.7 built on 2023-12-06T15:54:59Z"
traefik  | time="2024-01-29T23:25:26Z" level=debug msg="Static configuration loaded {\"global\":{\"checkNewVersion\":true},\"serversTra
traefik  | time="2024-01-29T23:25:26Z" level=info msg="\nStats collection is disabled.\nHelp us improve Traefik by turning this feature
traefik  | time="2024-01-29T23:25:26Z" level=info msg="Starting provider aggregator aggregator.ProviderAggregator"
traefik  | time="2024-01-29T23:25:26Z" level=debug msg="Starting TCP Server" entryPointName=websecure
traefik  | time="2024-01-29T23:25:26Z" level=info msg="Starting provider *traefik.Provider"
traefik  | time="2024-01-29T23:25:26Z" level=debug msg="*traefik.Provider provider configuration: {}"
traefik  | time="2024-01-29T23:25:26Z" level=info msg="Starting provider *docker.Provider"
traefik  | time="2024-01-29T23:25:26Z" level=debug msg="*docker.Provider provider configuration: {\"watch\":true,\"endpoint\":\"unix://
traefik  | time="2024-01-29T23:25:26Z" level=info msg="Starting provider *acme.Provider"
traefik  | time="2024-01-29T23:25:26Z" level=debug msg="*acme.Provider provider configuration: {\"email\":\"admin@*******.ca\",\"caServer\":\"https://acme-v02.api.letsencrypt.org/directory\",\"storage\":\"/certificates/acme.json\",\"keyType\":\"RSA4096\",\"certificatesDuration\":2160,\"tlsChallenge\":{},\"ResolverName\":\"le\",\"store\":{},\"TLSChallengeProvider\":{},\"HTTPChallengeProvider\":{}}"
traefik  | time="2024-01-29T23:25:26Z" level=debug msg="Attempt to renew certificates \"720h0m0s\" before expiry and check every \"24h0m0s\"" ACME CA="https://acme-v02.api.letsencrypt.org/directory" providerName=le.acme
traefik  | time="2024-01-29T23:25:26Z" level=info msg="Testing certificate renew..." ACME CA="https://acme-v02.api.letsencrypt.org/directory" providerName=le.acme
traefik  | time="2024-01-29T23:25:26Z" level=info msg="Starting provider *acme.ChallengeTLSALPN"
traefik  | time="2024-01-29T23:25:26Z" level=debug msg="*acme.ChallengeTLSALPN provider configuration: {}"
traefik  | time="2024-01-29T23:25:26Z" level=debug msg="Starting TCP Server" entryPointName=web
traefik  | time="2024-01-29T23:25:26Z" level=debug msg="Configuration received: {\"http\":{\"services\":{\"api\":{},\"dashboard\":{},\"noop\":{}},\"models\":{\"websecure\":{\"tls\":{\"certResolver\":\"le\"}}},\"serversTransports\":{\"default\":{\"maxIdleConnsPerHost\":200}}},\"tcp\":{},\"udp\":{},\"tls\":{}}" providerName=internal
traefik  | time="2024-01-29T23:25:26Z" level=debug msg="Configuration received: {\"http\":{},\"tcp\":{},\"udp\":{},\"tls\":{}}" providerName=le.acme
traefik  | time="2024-01-29T23:25:26Z" level=debug msg="Provider connection established with docker 25.0.1 (API 1.44)" providerName=docker
traefik  | time="2024-01-29T23:25:26Z" level=debug msg="Filtering disabled container" providerName=docker container=traefik-test-9ad00a40fd618cef626208c7a73030ab35abf01592ef70c1c378d981f1c952c9
traefik  | time="2024-01-29T23:25:26Z" level=debug msg="Configuration received: {\"http\":{},\"tcp\":{},\"udp\":{}}" providerName=docker
traefik  | time="2024-01-29T23:25:26Z" level=debug msg="No default certificate, fallback to the internal generated certificate" tlsStoreName=default
traefik  | time="2024-01-29T23:25:26Z" level=debug msg="No default certificate, fallback to the internal generated certificate" tlsStoreName=default
traefik  | time="2024-01-29T23:25:48Z" level=debug msg="Serving default certificate for request: \"traefik.*******.ca\""
traefik  | time="2024-01-29T23:25:48Z" level=debug msg="http: TLS handshake error from *******:35230: remote error: tls: unknown certificate"
traefik  | time="2024-01-29T23:25:51Z" level=debug msg="Serving default certificate for request: \"traefik.*******.ca\""
traefik  | time="2024-01-29T23:25:51Z" level=debug msg="http: TLS handshake error from *******:35232: remote error: tls: unknown certificate"
traefik  | time="2024-01-29T23:25:51Z" level=debug msg="Serving default certificate for request: \"traefik.*******.ca\""

Turns out the issue was with my compose.yml: the "deploy" key is redundant when working with Docker Compose!

You need the labels beneath deploy in your compose file when deploying to Docker Swarm with docker stack deploy.