Does Traefik not handle labels on its own containers?

Hi everyone, my Traefik docker-compose build looks like this:

I found that it doesn't self-sign my domain name and

the dashboard won't open. AI tells me that

Traefik doesn't process labels on its own containers by default.

What are the best practices?

Or is there something wrong with my configuration?

services:
  traefik:
    image: traefik:v3.6.12
    container_name: traefik
    restart: always
    environment:
      - TZ=Asia/Shanghai
    labels:
      traefik.enable: "true"
      traefik.docker.network: "web-services"
      traefik.http.routers.dashboard.entrypoints: websecure
      traefik.http.routers.dashboard.rule: "Host(`xx.com`) && (PathPrefix(`/api`) || PathPrefix(`/dashboard`))"
      traefik.http.routers.dashboard.tls: "true"
      traefik.http.routers.dashboard.tls.certresolver: myresolver
      traefik.http.routers.dashboard.service: api@internal
    command:
      - --log.level=DEBUG
      - --api=true
      - --api.dashboard=true
      - --providers.docker=true
      - --providers.docker.exposedbydefault=false
      - --providers.docker.network=web-services
      #- --providers.docker.allowEmptyServices=true
      - --entrypoints.web.address=:80
      - --entrypoints.web.http.redirections.entrypoint.to=websecure
      - --entrypoints.web.http.redirections.entrypoint.scheme=https
      - --entrypoints.websecure.address=:443
      - --entrypoints.websecure.http.tls.options=default
      - --certificatesresolvers.myresolver.acme.dnschallenge=true
      - --certificatesresolvers.myresolver.acme.dnschallenge.resolvers=1.1.1.1:53,223.5.5.5:53
      - --certificatesresolvers.myresolver.acme.dnschallenge.provider=cloudflare
      - --certificatesresolvers.myresolver.acme.email=${CF_API_EMAIL}
      - --certificatesresolvers.myresolver.acme.storage=/data/ssl/acme.json
    ports:
      - "80:80"
      - "443:443"
      #- "18443:18443"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./data:/data
    env_file:
      - .env
    networks:
      - web-services
    logging:
      driver: "json-file"
      options:
        max-size: "100m"
        max-file: "3"
    healthcheck:
      test: ["CMD", "traefik", "healthcheck"]
      interval: 30s
      timeout: 10s
      retries: 3

networks:
  web-services:
    name: web-services
    driver: bridge
    driver_opts:
      com.docker.network.bridge.name: br-web-services

Maybe check simple Traefik example.

Maybe your network re-naming disables functionality.

The AI advice is a bit misleading here. Traefik absolutely can process labels on its own container, but there are a few required pieces missing from your config.

Three things need to be added:

First, entrypoints are never defined. Your labels reference "websecure" but that entrypoint doesn't exist yet because it's not declared in the command section. Add these:

- --entrypoints.web.address=:80
- --entrypoints.websecure.address=:443

Second, the certificate resolver "myresolver" is referenced in your TLS labels but never configured. If you just want Traefik to use its self-signed fallback cert for testing, remove the tls.certresolver label entirely and keep only tls: "true". That will get you HTTPS with a self-signed cert without needing ACME config. For a real Let's Encrypt cert you would add an ACME section to the command, but start without it to get the dashboard working first.

Third, your compose file needs the actual port mappings and network defined. Without the ports section, nothing is exposed externally:

ports:
  - "80:80"
  - "443:443"

And the web-services network needs to exist in the compose or be declared as external.

One extra tip: add --api.insecure=true temporarily and check if the dashboard loads on port 8080. That bypasses TLS entirely and will tell you immediately whether your routing is correct before you troubleshoot the certificate side.

Hello everyone, I've found the problem, and you'd never guess where it is.
The log is as follows:

docker logs -f traefik | grep dashboard
2026-03-30T23:12:19+08:00 DBG github.com/traefik/traefik/v3/cmd/traefik/traefik.go:119 > Static configuration loaded [json] staticConfiguration={"api":{"basePath":"/","dashboard":true},"certificatesResolvers":{"myresolver":{"acme":{"caServer":"https://acme-v02.api.letsencrypt.org/directory","certificatesDuration":2160,"clientResponseHeaderTimeout":"30s","clientTimeout":"2m0s","dnsChallenge":{"provider":"cloudflare","resolvers":["1.1.1.1:53","223.5.5.5:53"]},"email":"chenjiagen88@gmail.com","keyType":"RSA4096","storage":"/data/ssl/acme.json"}}},"entryPoints":{"web":{"address":":80","forwardedHeaders":{},"http":{"maxHeaderBytes":1048576,"redirections":{"entryPoint":{"permanent":true,"priority":9223372036854775806,"scheme":"https","to":"websecure"}},"sanitizePath":true},"http2":{"maxConcurrentStreams":250,"maxDecoderHeaderTableSize":4096,"maxEncoderHeaderTableSize":4096},"transport":{"lifeCycle":{"graceTimeOut":"10s"},"respondingTimeouts":{"idleTimeout":"3m0s","readTimeout":"1m0s"}},"udp":{"timeout":"3s"}},"websecure":{"address":":443","forwardedHeaders":{},"http":{"maxHeaderBytes":1048576,"sanitizePath":true,"tls":{"options":"default"}},"http2":{"maxConcurrentStreams":250,"maxDecoderHeaderTableSize":4096,"maxEncoderHeaderTableSize":4096},"transport":{"lifeCycle":{"graceTimeOut":"10s"},"respondingTimeouts":{"idleTimeout":"3m0s","readTimeout":"1m0s"}},"udp":{"timeout":"3s"}}},"global":{"checkNewVersion":true},"log":{"format":"common","level":"DEBUG"},"providers":{"docker":{"defaultRule":"Host({{ normalize .Name }})","endpoint":"unix:///var/run/docker.sock","network":"web-services","watch":true},"providersThrottleDuration":"2s"},"serversTransport":{"maxIdleConnsPerHost":200},"tcpServersTransport":{"dialKeepAlive":"15s","dialTimeout":"30s"}}
2026-03-30T23:12:19+08:00 DBG github.com/traefik/traefik/v3/pkg/server/configurationwatcher.go:227 > Configuration received config={"http":{"middlewares":{"redirect-web-to-websecure":{"redirectScheme":{"permanent":true,"port":"443","scheme":"https"}}},"models":{"websecure":{"observability":{},"tls":{"options":"default"}}},"routers":{"web-to-websecure":{"entryPoints":["web"],"middlewares":["redirect-web-to-websecure"],"priority":9223372036854775806,"rule":"HostRegexp(^.+$)","ruleSyntax":"default","service":"noop@internal"}},"serversTransports":{"default":{"maxIdleConnsPerHost":200}},"services":{"api":{},"dashboard":{},"noop":{}}},"tcp":{"serversTransports":{"default":{"dialKeepAlive":"15s","dialTimeout":"30s"}}},"tls":{},"udp":{}} providerName=internal
2026-03-30T23:13:49+08:00 DBG github.com/traefik/traefik/v3/pkg/provider/docker/pdocker.go:103 > Provider event received {Status:health_status: unhealthy ID:fc3caf47862f99e445c81e0f921e228fb80d7c82c3e30cb7ab05876a307535dd From:traefik:v3.6.12 Type:container Action:health_status: unhealthy Actor:{ID:fc3caf47862f99e445c81e0f921e228fb80d7c82c3e30cb7ab05876a307535dd Attributes:map[com.docker.compose.config-hash:09dfc23e980cf698badc63ee8a1b6775aae36b9d0b95626a5d81ce96379225f4 com.docker.compose.container-number:1 com.docker.compose.depends_on: com.docker.compose.image:sha256:346bc28a43a529fc272f5a52b2853a8ad9acc33522047a59b15b017591a30cb7 com.docker.compose.oneoff:False com.docker.compose.project:traefik com.docker.compose.project.config_files:/opt/traefik/docker-compose.yaml com.docker.compose.project.working_dir:/opt/traefik com.docker.compose.replace:0c21b03e02bbd9856d56074494c6505b4ed3207b99d366ec87c82845e207468f com.docker.compose.service:traefik com.docker.compose.version:2.26.1 image:traefik:v3.6.12 name:traefik org.opencontainers.image.description:A modern reverse-proxy org.opencontainers.image.documentation:https://docs.traefik.io org.opencontainers.image.source:https://github.com/traefik/traefik org.opencontainers.image.title:Traefik org.opencontainers.image.url:https://traefik.io org.opencontainers.image.vendor:Traefik Labs org.opencontainers.image.version:v3.6.12 traefik.docker.network:web-services traefik.enable:true traefik.http.routers.dashboard.entrypoints:websecure traefik.http.routers.dashboard.rule:Host(traefik.ecs.deconf.xyz) && (PathPrefix(/api) || PathPrefix(/dashboard)) traefik.http.routers.dashboard.service:api@internal traefik.http.routers.dashboard.tls:true traefik.http.routers.dashboard.tls.certresolver:myresolver]} Scope:local Time:1774883629 TimeNano:1774883629577935228} providerName=docker

Key log:

health_status: unhealthy

In other words,

in Traefik's Docker provider:

unhealthy containers are not added to service discovery by default.

That is to say:

even though I wrote traefik.enable=true :white_check_mark:

even though the labels are read :white_check_mark: (visible in the logs)

:red_exclamation_mark: However:

:backhand_index_pointing_right: This router will not be activated at all.

The problem is:
Traefik v3 does not enable the healthcheck endpoint by default.

After canceling the health check, all my settings took effect.