Traefik is routing both hosts to the same service

So, I'm using traefik 3 with the docker provider and I essentially have 3 routable services:
Traefik dashboard (http; port 8080)
My website (https; port 443)
phpmyadmin (http; port 80)

Here is the relevant parts of my docker-compose.yml:

services:
  traefik:
    # The official v3 Traefik docker image
    image: traefik:v3.1
    # Enables the web UI and tells Traefik to listen to docker
    command:
      - "--log.level=DEBUG"
      - "--api.insecure=true"
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      - "--providers.file.directory=/etc/traefik/dynamic"
      - "--providers.file.watch=true"
      - "--entrypoints.web.address=:80"
      - "--entrypoints.websecure.address=:443"
    ports:
      - "8080:8080"
      - "80:80"
      - "443:443"
    volumes:
      # So that Traefik can listen to the Docker events
      - /var/run/docker.sock:/var/run/docker.sock
      - ./certs:/etc/certs
      - /.docker/local/traefik.config.yml:/etc/traefik/dynamic/traefik.yml
  ttp-php:
    hostname: ttp.dev
    container_name: ttp-cli-php
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.ttp.rule=Host(`ttp.dev`)"
      - "traefik.http.routers.ttp.entrypoints=websecure"
      - "traefik.http.routers.ttp.tls.domains[0].main=ttp.dev"
    platform: linux/x86_64
    build:
      context: .
    env_file:
      - ./secrets/app_secrets.env
    environment:
      PHP_POST_MAX_SIZE: "500M"
      PHP_UPLOAD_MAX_FILE_SIZE: "500M"
      HTTPS: "on"
  phpmyadmin:
    image: phpmyadmin
    env_file:
      - ./secrets/db_secrets.env
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.phpmyadmin.rule=Host(`phpmyadmin.dev`)"
      - "traefik.http.routers.phpmyadmin.entrypoints=web"

In my /etc/hosts file I have:

127.0.0.1 ttp.dev phpmyadmin.dev

And finally in my traefik.config.yml:

tls:
  certificates:
    - certFile: /etc/certs/bundle.crt
      keyFile: /etc/certs/bundle.key
      stores: default

The traefik dashboard at http://localhost:8080 loads up just fine.
My website at https://ttp.dev loads up just fine with the correct certificate.
But http://phpmyadmin.dev redirects to https and traefik then tries to serve up a default generated certificate. Whereas it should just serve the phpmyadmin service over http on port 80. It feels like it is trying to send the requests to my ttp-php service. Any ideas??

Thanks in Advance,
Jon

a) check the Traefik panel (:8080) for how the actual configuration for your phpmyadmin.dev is configured and check it for any other routes on your web entrypoint - perhaps you've configured some default redirect?
b) can you share more of the traefik.yml?

Hey, thanks for getting back to me.

That is literally everything I have in the traefik.yml file.

In the traefik portal, everything looks perfect.

I've added an additional service; lpc.dev. Same thing, redirects to https.

Can you go into that http service? The routers tab doesn't list used middlewares, and you seem to have 2 defined...

EDIT: NVM: those two middlewares are probably just the two that come by default with Traefik. My mistake.

I've only just realised you have a bit of a strange configuration - where you seem to provide the static configuration in the docker command part itself, and then have a dynamic setup in your traefic.yml file. While not necessarily an error it'll likely confuse people (like myself :stuck_out_tongue:). I'd recommend having a static configuration traefik.yml file and have that provided in the command, and then keep a dynamic location for anything else (named accordingly). But that shouldn't be the cause of your issue.

Ok, so I've figured out my issue:

Google Chrome redirects .dev domains to HTTPS by default as part of its initiative to secure website connections. This is because Google owns .dev domains and forces HTTPS at the registry level, so there's no way to override the redirect.

It's purely my use of .dev domains locally that is the issue!

However, I'm interested by your comment around my config.
Ideally, I'd like to keep everything in my docker-compose.yml file, but it seems that the certificates have to be configured in a file. So I've created a yml file with only the certificates in it and configured everything else via docker-compose labels. Does that make sense or would you suggest doing it differently?

1 Like

Regarding configuration, here's what I suggest based on my current setup.

docker-compose.yml - don't use command: at all, rather bind a static configuration file instead:

  traefik:
    image: "traefik:v3.1"
    container_name: "traefik"
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock:ro"
      - "./letsencrypt/:/letsencrypt/" #this is where I keep LE certificates; possibly use a docker volume so these don't get generated on each restart
      - "./traefik.yml:/etc/traefik/traefik.yml" #static Traefik config
      - "./services/:/services/" #dynamic configuration files
      - "./certs/:/certs/" #any other certificates folder
      - "/var/log/traefik/:/var/log/traefik/" #logs if you need them
    labels: #if you're fine using port :8080 then the labels can be ignored (just add the 8080 port mapping!)
            #note that this is just based on my config and may not apply!
      - "traefik.enable=true"
      - "traefik.http.routers.traefik.rule=Host(`traefik.domain.local`)"
      - "traefik.http.routers.traefik.entrypoints=web"
      - "traefik.http.routers.traefik.middlewares=redirect_to_https@file" #I've defined a https redirect middleware in a file for re-use
      - "traefik.http.routers.traefik-https.rule=Host(`traefik.domain.local`)"
      - "traefik.http.routers.traefik-https.entrypoints=websecure"
      - "traefik.http.routers.traefik-https.service=api@internal"
      - "traefik.http.routers.traefik-https.middlewares=local-limit@file" #I've defined an IP whitelist middleware for re-use
    healthcheck:
      test: "traefik healthcheck"
      retries: 12
      interval: 5s

Then you have your static configuration in traefik.yml:

global:
  checkNewVersion: true
  sendAnonymousUsage: false

entryPoints:
  web:
    address: :80

  websecure:
#do you want SSL as default?
    asDefault: true
    address: :443
#example of defaults for TLS for public domains, using a wildcard cert
    http:
      tls:
        certResolver: le
        domains:
          - main: "*.domain.com"
            sans: 
              - "domain.com"

# most reverse-proxies I've worked with don't validate internal SSL certificates, so I use a similar setting here
serversTransport:
  insecureSkipVerify: true

log:
  level: INFO
accessLog:
  filePath: /var/log/traefik/access.log
  bufferingSize: 100

api:
  insecure: true

ping: {}

providers:
  file:
    directory: /services/
    watch: true
  docker:
    network: traefik-proxy
    endpoint: "unix:///var/run/docker.sock"
    exposedByDefault: false

certificatesResolvers:
  le:
    acme:
      email: admin@domain.com
      storage: /letsencrypt/acme.json
      caserver: "https://acme-v02.api.letsencrypt.org/directory"
      dnsChallenge:
        provider: azuredns  #remember that dnsChallenge is needed for wildcards!

  le-staging:
    acme:
      email: admin@domain.com
      storage: /letsencrypt/acme-staging.json
      caserver: "https://acme-staging-v02.api.letsencrypt.org/directory"
      dnsChallenge:
        provider: azuredns

Finally you put anything else into the ./services/ folder; in your case it could be the definition of your pre-generated certificates for your local domain.

./services/localCertificates.yml

tls:
  certificates:
    - certFile: /certs/bundle.crt
      keyFile: /certs/bundle.key
      stores: default

The only downside is that changes to the traefik.yml static configuration require the container to be forcefully restarted, as docker compose up -d won't detect any changes (i.e. need to add --force-recreate to the command).

PS. Since I don't expose port 8080 in my docker-compose I can probably simplify the config a bit more, since I would also not need the insecure: true definition. In that case I should probably use a more dedicated setup for Traefik's panel.

1 Like

Have a look at simple Traefik example. It will handle TLS, route by sub-domain, use labels on target service to configure.

Just duplicate whoami for the services. Make sure to use a different router names in the labels for each service.