Unable to get Traefik working on docker swarm

Hi, so I am not sure if I am being dense but I can't seem to get traefik working on my swarm. I have 1 manager and 3 workers.
If I attach the labels to the containers and run them only on the manager it'll work no problems, but if I add them to the services and run them on my workers, no mas don't wanna work.

Am I just missing something obvious?

version: '3.8'

services:

  traefik:
    # Use the latest Traefik image
    image: traefik:latest
    ports:
      # Listen on port 80, default for HTTP, necessary to redirect to HTTPS
      - target: 80
        published: 80
        mode: host
      # Listen on port 443, default for HTTPS
      - target: 443
        published: 443
        mode: host
    deploy:
      placement:
        constraints:
          - node.role == manager
      labels:
        # Enable Traefik for this service, to make it available in the public network
        - traefik.enable=true
        # Use the traefik-public network (declared below)
        - traefik.docker.network=traefik-public
        # Use the custom label "traefik.constraint-label=traefik-public"
        # This public Traefik will only use services with this label
        # That way you can add other internal Traefik instances per stack if needed
        - traefik.constraint-label=traefik-public
        # admin-auth middleware with HTTP Basic auth
        # Using the environment variables USERNAME and HASHED_PASSWORD
        - traefik.http.middlewares.admin-auth.basicauth.users=${USERNAME?Variable not set}:${HASHED_PASSWORD?Variable not set}
        # https-redirect middleware to redirect HTTP to HTTPS
        # It can be re-used by other stacks in other Docker Compose files
        - traefik.http.middlewares.https-redirect.redirectscheme.scheme=https
        - traefik.http.middlewares.https-redirect.redirectscheme.permanent=true
        # traefik-http set up only to use the middleware to redirect to https
        - traefik.http.routers.traefik-public-http.rule=Host(`traefik.example.com`)
        - traefik.http.routers.traefik-public-http.entrypoints=http
        - traefik.http.routers.traefik-public-http.middlewares=https-redirect
        # traefik-https the actual router using HTTPS
        - traefik.http.routers.traefik-public-https.rule=Host(`traefik.example.com`)
        - traefik.http.routers.traefik-public-https.entrypoints=https
        - traefik.http.routers.traefik-public-https.tls=true
        # Use the special Traefik service api@internal with the web UI/Dashboard
        - traefik.http.routers.traefik-public-https.service=api@internal
        # Enable HTTP Basic auth, using the middleware created above
        - traefik.http.routers.traefik-public-https.middlewares=admin-auth
        # Define the port inside of the Docker service to use
        - traefik.http.services.traefik-public.loadbalancer.server.port=8080
        # Certs
        - "traefik.http.routers.proxy-https.tls.domains[0].main=.example.com"
        - "traefik.http.routers.proxy-https.tls.domains[0].sans=*.example.com"
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - /mnt/data/docker/data/traefik/data/traefik.yml:/traefik.yml:ro
      - /mnt/data/docker/data/traefik/data/acme.json:/acme.json
      #- /mnt/data/docker/data/traefik/data/config.yml:/config.yml:ro
    command:
      # Enable Docker in Traefik, so that it reads labels from Docker services
      - --providers.docker
      # Add a constraint to only use services with the label "traefik.constraint-label=traefik-public"
      - --providers.docker.constraints=Label(`traefik.constraint-label`, `traefik-public`)
      # Do not expose all Docker services, only the ones explicitly exposed
      - --providers.docker.exposedbydefault=false
      # Enable Docker Swarm mode
      - --providers.docker.swarmMode=true
      # Create an entrypoint "http" listening on address 80
      - --entrypoints.http.address=:80
      # Create an entrypoint "https" listening on address 443
      - --entrypoints.https.address=:443
      # Enable the access log, with HTTP requests
      - --accesslog
      # Enable the Traefik log, for configurations and errors
      - --log
      # Enable the Dashboard and API
      - --api
    environment:
      - CF_API_EMAIL=xxxxxx@gmail.com
      #- CF_DNS_API_TOKEN=YOU_API_TOKEN
      - CF_API_KEY=xxxxxx
      # be sure to use the correct one depending on if you are using a token or key
    networks:
      # Use the public network created to be shared between Traefik and
      # any other service that needs to be publicly available with HTTPS
      - traefik-public

networks:
  traefik-public:
    driver: overlay
    attachable: true
    name: traefik-public

Been here, feel the pain. Love swarm/traefik for home lab. Hopefully one of the admins chimes in soon, but I was looking for something else and noticed this post.

  1. "#certs" labels define tls configs for a router that appears not to have any service defined
  2. I believe updated docs point at defining permanent redirects under the entrypoint(s), not as middlewares.
  3. use of bind mounts instead of named volumes

Caveat to all of it -- I can't explain how/why, but have done all the aforementioned and not had per se issues until adding other/more services later. I'm redoing my entire setup because it led to issues. Summary point -- DockerSwarmRocks is outdated, and is a good way to get caught 95 percent there without a chance at getting to 100 percent.

1 Like

In the command portion of your traefik remove the - --providers.docker leaving only the providers.docker.swarmMode line. I think they are supposed to be mutually exclusive.

  traefik:
      image: docker-dev-artifactory.workday.com/traefik:2.4.9
      command:
          - --providers.docker.swarmMode=true

Insure your labels are declared under the deploy section. It appears that you have done this but pointing it out for others at a later date that may not have noticed this requirement.

      deploy:
          labels:
              # routes for traefik itself
              - traefik.enable=true
              - traefik.http.services.traefik.loadbalancer.server.port=8080

Note that deploy is at the same level as command above.

Docker itself can only "see" container labels on its own host. If you want to examine labels across hosts, you need to associate them with the service. The Swarm plumbing is what enables the communication between hosts within the cluster.

My guess is that the first providers.docker declaration took precedence. Given it will only be a one line change to test it, it is worth a shot.

Cheers, gave it a go and even moved command to just below image and no mas

I know this is an older post, but I am having a similar problem and wondering if you ever determined how to fix it?

This example assumes you only have a single manager node, otherwise LetsEncrypt certificate validation will not work. Use with docker stack deploy.

version: '3'

services:
  traefik:
    image: traefik:v2.10
    hostname: '{{.Node.Hostname}}'
    ports:
      # listen on host ports without ingress network
      - target: 80
        published: 80
        protocol: tcp
        mode: host
      - target: 443
        published: 443
        protocol: tcp
        mode: host
    networks:
      - proxy
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - /var/log:/var/log
      - letsencrypt:/letsencrypt
    deploy:
      mode: global
      placement:
        constraints:
          - node.role==manager
      labels:
        - traefik.enable=true
        - traefik.http.routers.mydashboard.rule=Host(`traefik.example.com`)
        - traefik.http.routers.mydashboard.service=api@internal
        - traefik.http.routers.mydashboard.middlewares=myauth
        - traefik.http.services.mydashboard.loadbalancer.server.port=1337
        - traefik.http.middlewares.myauth.basicauth.users=test:$$apr1$$H6uskkkW$$IgXLP6ewTrSuBkTrqE8wj/
    command:
      - --api.dashboard=true
      - --log.level=INFO
      #- --log.filepath=/var/log/traefik.log
      - --accesslog=true
      #- --accesslog.filepath=/var/log/traefik-access.log
      - --providers.docker.exposedByDefault=false
      - --providers.docker.network=proxy
      - --providers.docker.swarmMode=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.certresolver=myresolver
      - --certificatesresolvers.myresolver.acme.email=mail@example.com
      - --certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json
      - --certificatesresolvers.myresolver.acme.tlschallenge=true

  whoami:
    image: traefik/whoami:v1.10
    hostname: '{{.Node.Hostname}}'
    networks:
      - proxy
    deploy:
      mode: global
      labels:
        - 'traefik.enable=true'
        - 'traefik.http.routers.whoami.rule=Host(`whoami.example.com`) || PathPrefix(`/whoami`)'
        - 'traefik.http.services.whoami.loadbalancer.server.port=80'

networks:
  proxy:
    name: proxy
    driver: overlay
    attachable: true

volumes:
  letsencrypt:
    name: letsencrypt
1 Like

Hi, I'm getting 404 when accessing the traefik dashboard and services on their respected domains? Is there something I'm missing.

version: "3.7"
services:
  traefik:
    image: "traefik:3.1"
    container_name: "traefik"
    command:
      - "--log.level=DEBUG"
      - "--api.insecure=false"
      - "--api.dashboard=true"
      - "--accesslog=true"
      - "--accesslog.filepath=/var/logs/access.log"
      - "--providers.docker=true"
      - "--providers.swarm.endpoint=unix:///var/run/docker.sock"
      - "--providers.docker.exposedbydefault=false"
      - "--entrypoints.web.address=:80"
      - "--entrypoints.websecure.address=:443"
      - "--entrypoints.web.http.redirections.entrypoint.to=websecure"
      - "--entrypoints.web.http.redirections.entrypoint.scheme=https"
      - "--certificatesresolvers.myhttpchallenge.acme.httpchallenge=true"
      - "--certificatesresolvers.myhttpchallenge.acme.httpchallenge.entrypoint=web"
      - "--certificatesresolvers.myhttpchallenge.acme.email=admin@safle.com"
      - "--certificatesresolvers.myhttpchallenge.acme.storage=/letsencrypt/acme.json"
    deploy:
      mode: global
      placement:
        constraints:
          - node.role==manager
      labels:
        - "traefik.enable=true"
        - "traefik.http.routers.traefik-secure.entrypoints=websecure" 
        - "traefik.http.routers.traefik-secure.tls=true"
        - "traefik.http.routers.traefik-secure.tls.certresolver=myhttpchallenge"
        - "traefik.http.routers.traefik-secure.rule=Host(`devops-monitor.example.com`)"
        - "traefik.http.routers.traefik-secure.middlewares=traefik-auth"
        - "traefik.http.middlewares.traefik-auth.basicauth.users=user:MlkmiPKTl0OB7BAS"
        - "traefik.http.routers.traefik-secure.service=api@internal"
        - "traefik.http.routers.http-catchall.rule=hostregexp(`{host:[a-z-.]+}`)"
        - "traefik.http.routers.http-catchall.entrypoints=web"
        - "traefik.http.routers.http-catchall.middlewares=redirect-to-https"
        - "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https"
        - "traefik.http.middlewares.security-headers.headers.frameDeny=true"
        - "traefik.http.middlewares.security-headers.headers.sslRedirect=true"
        - "traefik.http.middlewares.security-headers.headers.STSIncludeSubdomains=true"
        - "traefik.http.middlewares.security-headers.headers.STSPreload=true"
        - "traefik.http.middlewares.security-headers.headers.STSSeconds=31536000"
        - "traefik.http.middlewares.security-headers.headers.contentTypeNosniff=true"
        - "traefik.http.middlewares.security-headers.headers.browserXSSFilter=true"
        - "traefik.http.middlewares.security-headers.headers.forceSTSHeader=true"
        - "traefik.http.middlewares.rate-limit.ratelimit.period=5m"
        - "traefik.http.middlewares.rate-limit.ratelimit.average=10"
        - "traefik.http.middlewares.rate-limit.ratelimit.burst=5"
        - "traefik.http.middlewares.rate-limit.ratelimit.sourcecriterion.ipstrategy.depth=0"
    ports:
      - "80:80"
      - "8080:8080"
      - "443:443"
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock"
      - "./letsencrypt:/letsencrypt"
  
  webapp-devops:
    image: "image:latest"
    container_name: devops
    restart: always
    deploy:
      replicas: 2
      labels:
        - "traefik.enable=true"
        - "traefik.http.routers.devops.rule=Host(`devops.example.com`)"
        - "traefik.http.routers.devops.entrypoints=websecure"
        - "traefik.http.routers.devops.tls=true"
        - "traefik.http.routers.devops.tls.certresolver=myhttpchallenge"
        - "traefik.http.routers.devops.middlewares=security-headers@docker,rate-limit@docker"

You probably shouldn't mix providers.dockerand providers.swarm. From Traefik v3.x all Swarm functionality of providers.docker was moved to providers.swarm.

Ports are opened, entrypoints are defined. You have a traefik.yml file in use?

You run a full Swarm with multiple nodes? Does your domain DNS point to the right node?

Maybe compare to simple Traefik Swarm example. Note that regular LetsEncrypt only works with a single Traefik instance, only Traefik EE supports clustered LE.

1 Like

No I am not using any static config like traefik.yml.
No I am running the docker swarm on a single node only, also when I try to add another node in docker swarm the load doesn't get distributed to the other node for some reason.
My domain DNS points to the manager node.

But thank you so much for the reply, let me try with your example yml and change my configs accordingly.

Will give you an update if this actually worked for me or not.

I updated my configs according to your sample yml provided and downgraded the traefik version to 2.10 since the latest 3.x versions doesn't support swarm flag but am still getting the same 404 error, what do you think why is this happening?

version: "3.7"
services:
  traefik:
    image: "traefik:2.10"
    container_name: "traefik"
    command:
      - "--log.level=DEBUG"
      - "--api.insecure=false"
      - "--api.dashboard=true"
      - "--accesslog=true"
      - "--providers.docker.swarmMode=true"
      - "--providers.docker.exposedbydefault=false"
      - "--entrypoints.web.address=:80"
      - "--entrypoints.websecure.address=:443"
      - "--entrypoints.web.http.redirections.entrypoint.to=websecure"
      - "--entrypoints.web.http.redirections.entrypoint.scheme=https"
      - "--certificatesresolvers.myhttpchallenge.acme.httpchallenge=true"
      - "--certificatesresolvers.myhttpchallenge.acme.httpchallenge.entrypoint=web"
      - "--entrypoints.websecure.http.tls.certresolver=namecheap"
      - "--certificatesresolvers.myhttpchallenge.acme.email=admin@safle.com"
      - "--certificatesresolvers.myhttpchallenge.acme.storage=/letsencrypt/acme.json"
    deploy:
      mode: global
      placement:
        constraints:
          - node.role==manager
      labels:
        - "traefik.enable=true"
        - "traefik.http.routers.traefik-secure.entrypoints=websecure" 
        - "traefik.http.routers.traefik-secure.tls=true"
        - "traefik.http.routers.traefik-secure.tls.certresolver=myhttpchallenge"
        - "traefik.http.routers.traefik-secure.rule=Host(`monitor.example.com`)"
        - "traefik.http.routers.traefik-secure.middlewares=traefik-auth"
        - "traefik.http.middlewares.traefik-auth.basicauth.users=user:M0xOo5OVnaB3JMlkmiPKTl0OB7BAS"
        - "traefik.http.routers.traefik-secure.service=api@internal"
        - "traefik.http.routers.http-catchall.rule=hostregexp(`{host:[a-z-.]+}`)"
        - "traefik.http.routers.http-catchall.entrypoints=web"
        - "traefik.http.routers.http-catchall.middlewares=redirect-to-https"
        - "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https"
        - "traefik.http.middlewares.security-headers.headers.frameDeny=true"
        - "traefik.http.middlewares.security-headers.headers.sslRedirect=true"
        - "traefik.http.middlewares.security-headers.headers.STSIncludeSubdomains=true"
        - "traefik.http.middlewares.security-headers.headers.STSPreload=true"
        - "traefik.http.middlewares.security-headers.headers.STSSeconds=31536000"
        - "traefik.http.middlewares.security-headers.headers.contentTypeNosniff=true"
        - "traefik.http.middlewares.security-headers.headers.browserXSSFilter=true"
        - "traefik.http.middlewares.security-headers.headers.forceSTSHeader=true"
        - "traefik.http.middlewares.rate-limit.ratelimit.period=5m"
        - "traefik.http.middlewares.rate-limit.ratelimit.average=10"
        - "traefik.http.middlewares.rate-limit.ratelimit.burst=5"
        - "traefik.http.middlewares.rate-limit.ratelimit.sourcecriterion.ipstrategy.depth=0"
    ports:
      - target: 80
        published: 80
        protocol: tcp
        mode: host
      - target: 443
        published: 443
        protocol: tcp
        mode: host
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock"
      - "./letsencrypt:/letsencrypt"
      - "/var/log:/var/log"

  devops:
    image: "image:latest"
    container_name: "devops"
    restart: always
    deploy:
      replicas: 5
      labels:
      - "traefik.enable=true"
      - "traefik.http.routers.devops.rule=Host(`devops-example.com`)"
      - "traefik.http.routers.devops.entrypoints=websecure"
      - "traefik.http.routers.devops.tls=true"
      - "traefik.http.routers.devops.tls.certresolver=myhttpchallenge"
      - "traefik.http.routers.devops.middlewares=security-headers@docker,rate-limit@docker" 
      - "traefik.http.services.devops.loadbalancer.server.port=3001"