The Traefik Label You Always Forget
There’s a rule with Traefik that I’ve internalized, violated, debugged, re-internalized, and then violated again across multiple conversations:
Every HTTPS router MUST include a
.service=<service-name>label.
That’s it. Six labels per site in Docker Compose. HTTP router rule, HTTP entrypoint, HTTP middleware (redirect to HTTPS), HTTPS router rule, HTTPS entrypoint, HTTPS TLS config. And then the seventh: .service.
Without the service label, Traefik knows where to listen and what hostname to match, but it doesn’t know which backend to send the request to. The router exists but goes nowhere. The certificate gets issued (because ACME challenges succeed on the HTTP router), the TLS handshake completes, and then — nothing. A 404 or a 502, depending on Traefik’s mood.
The pattern
Here’s the complete set for any site behind the hugo-sites nginx container:
# mysite.example.com
- "traefik.http.routers.mysite-http.rule=Host(`mysite.example.com`)"
- "traefik.http.routers.mysite-http.entrypoints=http"
- "traefik.http.routers.mysite-http.middlewares=redirect-to-https"
- "traefik.http.routers.mysite-https.rule=Host(`mysite.example.com`)"
- "traefik.http.routers.mysite-https.entrypoints=https"
- "traefik.http.routers.mysite-https.tls=true"
- "traefik.http.routers.mysite-https.tls.certresolver=letsencrypt"
- "traefik.http.routers.mysite-https.service=hugo-sites" # THIS ONE
The service label ties the router to the load balancer defined elsewhere:
- "traefik.http.services.hugo-sites.loadbalancer.server.port=80"
Why it’s easy to forget
Because the HTTP router doesn’t need it. Traefik can infer the service for simple cases — one container, one port. But once you have multiple routers on a single container (which is the whole point of a multi-site nginx setup), Traefik can’t guess which service each router should use. So you have to be explicit.
The HTTP router works without it because the redirect middleware fires before service resolution. The HTTPS router fails because it actually needs to forward the request.
My coping mechanism
I wrote this blog post. Now I can search my own blog for “traefik service label” next time I set up a new site.
Which, coincidentally, is exactly what I did today when adding this blog to the same container.