Metainformationen zur Seite
  •  

Dies ist eine alte Version des Dokuments!


Traefik

URL
https://traefik.smns-bw.org/
User
smns-tr
Passwort
••••••••••
Production
hetzner:/opt/traefik/
├── traefik
│ ├── configfiles
│ │ ├── config.yml
│ │ ├── middleware-chains.yml
│ │ ├── middlewares.yml
│ │ ├── tls-opts.yml
│ ├── docker-compose.yml
│ ├── .env
│ ├── traefik.log
│ ├── traefik.yml
│ ├── access.log

In .env steht die URL traefik.smns-bw.org sowie die Zugangsdaten für diese Seite für docker-compose.yml

In die einzelnen docker-compose.yml Files der Container kommt dann sowas (Beispiel Sammlungskatalog):

    labels:
      - "traefik.http.routers.webportal.rule=Host(`${URL}`)"
      - "traefik.http.routers.webportal.entrypoints=https"
      - "traefik.http.routers.webportal.tls=true"
      - "traefik.http.routers.webportal.tls.certresolver=leresolver"
      - "traefik.http.routers.webportal.middlewares=secure-collections@file"
      - "traefik.http.services.webportal.loadbalancer.server.port=6543"
      - "traefik.http.services.webportal.loadbalancer.sticky=true"
      - "traefik.http.services.webportal.loadbalancer.sticky.cookie.name=collections.smns-bw.org"
      - "traefik.http.services.webportal.loadbalancer.sticky.cookie.httpOnly=true"
      - "traefik.http.services.webportal.loadbalancer.sticky.cookie.secure=true"
      - "traefik.docker.network=proxy"

Damit der Docker.sock nicht nach außen exposed ist, ist zusammen mit Traefik ein docker-socket aufgesetzt, der von hier stammt: https://github.com/wollomatic/socket-proxy

[1] docker-compose.yml show
services:
  traefik:
    container_name: traefik
    image: „traefik:latest“
    restart: always
#    read_only: true
    command:
      - –configfile=/traefik.yml
    mem_limit: 2G
    cpus: 0.75
    ports:
      - „80:80“
      - „443:443“
    volumes:
      - „./acme.json:/acme.json“
      - „./traefik.yml:/traefik.yml:ro“
      - „./configfiles:/configfiles:ro“
#      - „.logs/traefik.log:/traefik.log“
#      - „.logs/access.log:/access.log“
      - „./logs:/logs:rw“
    depends_on:
      - „dockerproxy“
    security_opt:
      - „no-new-privileges:true“
    networks:
      - „proxy“
      - „docker-proxynet“
    labels:
      - „traefik.http.routers.traefik.entrypoints=https“
      - „traefik.http.routers.traefik.rule=Host(`traefik.smns-bw.org`)“
      - „traefik.http.routers.traefik.tls=true“
      - „traefik.http.routers.traefik.tls.certresolver=leresolver“
      # API service
      - „traefik.http.routers.traefik.service=api@internal“
      - „traefik.http.routers.traefik.middlewares=secure-traefik@file“
      - „traefik.http.services.traefik.loadbalancer.sticky=true“
      - „traefik.http.services.traefik.loadbalancer.sticky.cookie.httpOnly=true“
      - „traefik.http.services.traefik.loadbalancer.sticky.cookie.secure=true“
    healthcheck:
      test: [„CMD“, „wget“, „–spider“, „http://localhost:8082/ping“]
      interval: 30s
      timeout: 5s
      retries: 3
      start_period: 10s

  dockerproxy:
    build:
      context: .
    container_name: socket-proxy
    command:
      - '-loglevel=DEBUG'
      - '-allowfrom=traefik,172.31.0.1'
      - '-listenip=0.0.0.0'
      - '-allowGET=/v1\..{1,2}/(version|containers/.*|events.*)'
      - '-shutdowngracetime=5'
    restart: unless-stopped
    user: „65534:998“
    read_only: true
    mem_limit: 64M
    cap_drop:
      - ALL
    security_opt:
      - no-new-privileges
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
    networks:
      - „proxy“
      - „docker-proxynet“
    healthcheck:
      test: [ „CMD“, „nc“, „-z“, „localhost“, „2375“ ]
      interval: 1m
      timeout: 3s
      retries: 3

  error-pages-webportal:
    container_name: error-webportal
    image: nginx:alpine
    restart: always
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
      - ./mime.types:/etc/nginx/mime.types
      - ./error-pages-webportal:/usr/share/nginx/html:ro
    labels:
      - „traefik.http.routers.error-pages-webportal.entrypoints=https“
      - „traefik.http.routers.error-pages-webportal.tls=true“
      - „traefik.http.routers.error-pages-webportal.tls.certresolver=leresolver“
      - „traefik.http.routers.error-pages-webportal.rule=Host(`error-webportal.smns-bw.org`)“
#      - „traefik.http.routers.error-pages-webportal.middlewares=secure-global@file“
      - „traefik.http.services.error-pages-webportal.loadbalancer.sticky=true“
      - „traefik.http.services.error-pages-webportal.loadbalancer.sticky.cookie.httpOnly=true“
      - „traefik.http.services.error-pages-webportal.loadbalancer.sticky.cookie.secure=true“
      - „traefik.http.services.error-pages-webportal.loadbalancer.server.port=80“
    networks:
      - „proxy“
    healthcheck:
      test: [„CMD“, „curl“, „-f“, „http://localhost/healthz.html“]
      interval: 30s
      timeout: 5s
      retries: 3
      start_period: 10s

networks:
  proxy:
    external: true
  docker-proxynet:
    driver: bridge
    internal: true


[2] traefik.yml show
entryPoints:
  http:
    address: „:80“
    http:
      redirections:
        entryPoint:
          to: ':443'
          scheme: https
  https:
    address: „:443“
#    http3:
#      advertisedPort: 443
  postgres:
    address: „:5434“
  ssh:
    address: „:666“
  healthcheck:
    address: „:8082“
log:
  level: INFO
  filePath: „/logs/traefik.log“
  format: json

accessLog:
  filePath: „/logs/access.log“
  bufferingSize: 100

api: {}

ping:
  entryPoint: healthcheck

providers:
  docker:
#    endpoint: „tcp://socket-proxy:2375“
    endpoint: „tcp://dockerproxy:2375“
#    endpoint: „unix:///var/run/docker.sock“
    watch: true
    network: proxy
#    exposedByDefault: false
  file:
    directory: „/configfiles“
    watch: true

certificatesResolvers:
  leresolver:
    acme:
      email: „it@smns-bw.org“
      storage: „/acme.json“
      caServer: „https://acme-v02.api.letsencrypt.org/directory“
      tlsChallenge: {}

experimental:
  plugins:
    traefik-plugin-cookie-path-prefix:
      moduleName: „github.com/SchmitzDan/traefik-plugin-cookie-path-prefix“
      version: „v0.0.3“


[3] config.yml show
http:
  routers:
    webmin:
      entryPoints:
        - „https“
      rule: Host(`webmin.smns-bw.org`)
      service: webmin
      middlewares:
        - „secure-webmin“
      tls:
        certResolver: leresolver
        options: tls-opts

    traefik:
      rule: Host(`traefik.smns-bw.org`)
      service: cookies

    smns_stats:
      rule: Host(`statistics.smns-bw.org`)
#      rule: „PathPrefix(`/statistics`)“
      service: smns_stats
      entryPoints:
        - „https“
      middlewares:
        - „secure-collections“
      tls:
        certResolver: leresolver
        options: tls-opts

  services:
    webmin:
      loadBalancer:
        servers:
          - url: „http://172.18.0.1:10000/“
        passHostHeader: true
        sticky:
          cookie:
            secure: true
            httpOnly: true
            sameSite: lax

    smns_stats:
      loadBalancer:
        servers:
          - url: „http://172.18.0.1:8050“
        passHostHeader: true

    cookies:
      loadBalancer:
        sticky:
          cookie:
            secure: true
            httpOnly: true
            sameSite: lax


[4] middleware-chains.yml show
http:
  middlewares:
    secure-global:
      chain:
        middlewares:
          - „security-headers-base“
          - „cors-default“
          - „CSP-base“

    secure-traefik:
      chain:
        middlewares:
          - „traefik-auth“               # auth first, so errors still get headers/CSP
          - „security-headers-base“
          - „cors-default“
          - „CSP-base“

    secure-idservice:
      chain:
        middlewares:
          - „security-headers-base“
          - „cors-default“
          - „CSP-idservice“

    secure-biocase:
      chain:
        middlewares:
          - „security-headers-base“
          - „cors-default“
          - „CSP-biocase“

    secure-webmin:
      chain:
        middlewares:
          - „security-headers-base“
          - „cors-default“
          - „CSP-webmin“

    secure-prestashop:
      chain:
        middlewares:
          - „security-headers-base“
          - „cors-default“
          - „CSP-prestashop“

    secure-ent:
      chain:
        middlewares:
          - „security-headers-base“
          - „cors-default“
          - „CSP-ent“

    secure-collections:
      chain:
        middlewares:
          - „webportal-errors“            # moved before headers so error pages get hardened
          - „security-headers-hsts-subdomains“
          - „security-headers-allowframes“
          - „cors-collections“
          - „CSP-collections“

    secure-digiphyll:
      chain:
        middlewares:
          - „security-headers-base“
          - „security-headers-allowframes“
          - „cors-default“
          - „CSP-digiphyll“

    secure-dokuwiki:
      chain:
        middlewares:
          - „security-headers-hsts-subdomains“
          - „security-headers-allowframes“
          - „cors-default“
          - „CSP-dokuwiki“

    secure-geometroidea:
      chain:
        middlewares:
          - „security-headers-base“
          - „cors-default“
          - „CSP-geometroidea“

    secure-h5p:
      chain:
        middlewares:
          - „security-headers-base“
          - „security-headers-allowframes“
          - „cors-default“
          - „CSP-h5p“

    secure-awstats:
      chain:
        middlewares:
          - „security-headers-base“
          - „cors-default“
          - „CSP-awstats“

    secure-librechat:
      chain:
        middlewares:
          - „security-headers-noindex“
          - „cors-default“
          - „CSP-librechat“

    secure-pictures:
      chain:
        middlewares:
          - „security-headers-base“
          - „security-headers-allowframes“
          - „cors-default“
          - „CSP-base“


[5] middlewares.yml show
http:
  middlewares:
    default-whitelist:
      ipWhiteList:
        sourceRange:
          - „127.0.0.1/32“
          - „172.19.0.0/24“

    traefik-auth:
      basicAuth:
        users:
          - „smns-tr:$2y$10$mUF04Rf7852wnP5xGxewte1hW4X/LcEN2c7of5xPOEyzbAXGVrEAi“

    # Core security baseline used by most services
    security-headers-base:
      headers: &SEC_BASE
      addVaryHeader: true

      # Response header hardening
      contentTypeNosniff: true
      browserXssFilter: true
      referrerPolicy: „same-origin“
      permissionsPolicy: fullscreen=(self „https://smns-bw.org“), geolocation=*, midi=(), camera=(), usb=(), magnetometer=(), accelerometer=(), vr=(), speaker=(), ambient-light-sensor=(), gyroscope=(), microphone=(), payment=()

      # HSTS
      forceSTSHeader: true
      stsSeconds: 31536000
      stsPreload: false
      # Keep includeSubdomains off in base to avoid accidental lock-in; use variant below when desired.
      # stsIncludeSubdomains: false

      # X-Frame-Options — deny by default (use CSP frame-ancestors for granular control)
      frameDeny: true
      # customFrameOptionsValue: „SAMEORIGIN“  # optional alternative if you want XFO too

      # TLS/Proxy awareness
      sslRedirect: true                # FIX: belongs at headers root (not inside sslProxyHeaders)
      sslProxyHeaders:
        X-Forwarded-Proto: „https“
      hostsProxyHeaders:
        - „X-Forwarded-Host“

      # Standard response scrubbing
      customResponseHeaders: &RESP_BASE
        Server: „“                     # FIX: unify case
        X-Powered-By: „“               # FIX: unify case
        X-Robots-Tag: „index, follow“  # override via -noindex variant

      # Upstream request adjustments (to your apps)
      customRequestHeaders: &REQ_BASE
        X-Forwarded-Proto: „https“     # OK to keep here

    # Variant: noindex
    security-headers-noindex:
      headers:
      «: *SEC_BASE
      customResponseHeaders:
        «: *RESP_BASE
        X-Robots-Tag: „none,noarchive,nosnippet,notranslate,noimageindex“

    # Variant: allow framing (prefer CSP frame-ancestors to control who can frame you)
    security-headers-allowframes:
      headers:
      «: *SEC_BASE
      frameDeny: false
      # customFrameOptionsValue is mostly ignored by modern browsers except SAMEORIGIN/DENY.
      # „ALLOW-FROM“ is deprecated; rely on CSP if you need granular control.
      customFrameOptionsValue: „SAMEORIGIN“

    # Variant: HSTS include subdomains, longer max-age
    security-headers-hsts-subdomains:
      headers:
      «: *SEC_BASE
      stsIncludeSubdomains: true
      stsSeconds: 63072000

    # Optional: shared Cache-Control for static-ish services
    cache-public:
      headers:
      customResponseHeaders:
        Cache-Control: „public“         # FIX: was (incorrectly) in customRequestHeaders

    # CORS: compose these with the security headers as needed
    cors-default:
      headers: &CORS_DEFAULT
      accessControlAllowMethods:
        - GET
        - OPTIONS
        - PUT
      accessControlAllowHeaders: „*“
      accessControlMaxAge: 100
      addVaryHeader: true

    cors-collections:
      headers:
      «: *CORS_DEFAULT
      accessControlAllowOriginList:
        - https://pydeepzoom.smns-bw.org
        - https://pictures.smns-bw.org

    # Base CSP used by services that only need same-origin + *.smns-bw.org
    CSP-base:
      headers:
      contentSecurityPolicy: &CSP_BASE >
        default-src 'self' 'unsafe-inline' 'unsafe-eval' *.smns-bw.org;
        frame-ancestors 'self' *.smns-bw.org;
        frame-src *.smns-bw.org;
        base-uri 'self';
        form-action 'self';
        img-src 'self' data:;
        connect-src 'self';
        font-src 'self';
        style-src 'self' 'unsafe-inline';
        script-src 'self' 'unsafe-inline' 'unsafe-eval' *.smns-bw.org;

    # If you still reference CSP-global anywhere, just point it to the base:
    CSP-global:
      headers:
      contentSecurityPolicy: *CSP_BASE

    # Deltas below (only where different from base)

    CSP-dokuwiki:
      headers:
      contentSecurityPolicy: >
        default-src 'self' 'unsafe-inline' 'unsafe-eval' *.smns-bw.org;
        frame-ancestors 'self' *.smns-bw.org;
        frame-src *.smns-bw.org *.google.com;
        base-uri 'self';
        form-action 'self';
        img-src 'self' https://www.dokuwiki.org https://www.gravatar.com data:;
        connect-src 'self';
        font-src 'self';
        style-src 'self' 'unsafe-inline';
        script-src 'self' 'unsafe-inline' 'unsafe-eval' *.smns-bw.org;

    CSP-idservice:
      headers:
      contentSecurityPolicy: >
        default-src 'self' 'unsafe-inline' 'unsafe-eval' *.smns-bw.org;
        frame-ancestors 'self' *.smns-bw.org;
        frame-src *.smns-bw.org;
        base-uri 'self';
        form-action 'self';
        img-src 'self' https://physalia.evolution.uni-bonn.de data:;
        connect-src 'self';
        font-src 'self';
        style-src 'self' 'unsafe-inline';
        script-src 'self' 'unsafe-inline' 'unsafe-eval' *.smns-bw.org https://physalia.evolution.uni-bonn.de;

    CSP-librechat:
      headers:
      contentSecurityPolicy: >
        default-src 'self' 'unsafe-inline' 'unsafe-eval' *.smns-bw.org;
        frame-ancestors 'self' *.smns-bw.org;
        frame-src *.smns-bw.org;
        base-uri 'self';
        form-action 'self';
        img-src 'self' blob: data:;
        connect-src 'self';
        font-src 'self';
        style-src 'self' 'unsafe-inline';
        script-src 'self' 'unsafe-inline' 'unsafe-eval' *.smns-bw.org;

    # Fixed: removed invalid 'unsafe-inline' and 'unsafe-eval' from -elem directives
    CSP-awstats:
      headers:
      contentSecurityPolicy: >
        default-src 'self' 'unsafe-inline' 'unsafe-eval' *.smns-bw.org;
        frame-ancestors 'self' *.smns-bw.org;
        frame-src *.smns-bw.org;
        base-uri 'self';
        form-action 'self';
        img-src 'self' https://chart.googleapis.com;
        connect-src 'self' https://www.gstatic.com/charts/;
        font-src 'self';
        style-src 'self' 'unsafe-inline';
        style-src-elem https://www.gstatic.com/charts/;
        script-src 'self' 'unsafe-inline' 'unsafe-eval' *.smns-bw.org;
        script-src-elem https://www.google.com/jsapi https://www.gstatic.com/charts/;

    CSP-h5p:
      headers:
      contentSecurityPolicy: >
        default-src 'self' 'unsafe-inline' 'unsafe-eval' *.smns-bw.org;
        frame-ancestors https://www.naturkundemuseum-bw.de;
        frame-src *.smns-bw.org;
        base-uri 'self';
        form-action 'self';
        img-src 'self' data:;
        connect-src 'self';
        font-src 'self';
        style-src 'self' 'unsafe-inline';
        script-src 'self' 'unsafe-inline' 'unsafe-eval' *.smns-bw.org;

    CSP-biocase:
      headers:
      contentSecurityPolicy: >
        default-src 'self' 'unsafe-inline' 'unsafe-eval' *.smns-bw.org;
        frame-ancestors 'self' *.smns-bw.org;
        frame-src *.smns-bw.org;
        base-uri 'self';
        form-action 'self';
        img-src 'self' https://unpkg.com/ https://*.tile.osm.org data:;
        connect-src 'self';
        font-src 'self';
        style-src 'self' 'unsafe-inline' https://unpkg.com/leaflet@1.3.3/dist/leaflet.css;
        script-src 'self' 'unsafe-inline' 'unsafe-eval' *.smns-bw.org https://unpkg.com/leaflet@1.3.3/dist/leaflet.js https://code.jquery.com/jquery-1.7.min.js;

    CSP-prestashop:
      headers:
      contentSecurityPolicy: >
        default-src 'self' 'unsafe-inline' 'unsafe-eval' *.smns-bw.org;
        frame-ancestors 'self' *.smns-bw.org;
        frame-src *.smns-bw.org;
        base-uri 'self';
        form-action 'self';
        img-src 'self' data:;
        connect-src 'self';
        font-src 'self';
        style-src 'self' 'unsafe-inline';
        style-src-elem https://market.smns-bw.org;
        script-src 'self' 'unsafe-inline' 'unsafe-eval' https://market.smns-bw.org;

    # Base + broader font-src
    CSP-webmin:
      headers:
      contentSecurityPolicy: >
        default-src 'self' 'unsafe-inline' 'unsafe-eval' *.smns-bw.org;
        base-uri 'self';
        img-src 'self' data:;
        font-src 'self' *.smns-bw.org data:;
        style-src 'self' 'unsafe-inline';
        script-src 'self' 'unsafe-inline' 'unsafe-eval' *.smns-bw.org;

    # Fixed: stray „data“ token removed, keep single „data:“ at end
    CSP-collections:
      headers:
      contentSecurityPolicy: >
        default-src 'none';
        frame-ancestors 'self' *.smns-bw.org pictures.smns-bw.org;
        frame-src *.smns-bw.org;
        base-uri 'self';
        form-action 'self';
        style-src 'self' 'unsafe-inline' *.smns-bw.org https://cloud.ccm19.de;
        connect-src 'self' https://cloud.ccm19.de https://matomo.naturkundemuseum-bw.de/;
        font-src 'self' *.smns-bw.org;
        img-src 'self' *.smns-bw.org https://a.tile.openstreetmap.org https://b.tile.openstreetmap.org https://c.tile.openstreetmap.org https://cloud.ccm19.de data:;
        script-src 'self' 'unsafe-inline' 'unsafe-eval' https://pydeepzoom.smns-bw.org https://matomo.naturkundemuseum-bw.de https://cloud.ccm19.de;

    # Fixed: removed invalid 'unsafe-inline' from connect-src and from script/style -elem
    CSP-ent:
      headers:
      contentSecurityPolicy: >
        default-src 'none';
        base-uri 'self';
        form-action 'self';
        style-src-elem https://ent.smns-bw.org/static/css/;
        connect-src 'self' https://matomo.naturkundemuseum-bw.de;
        script-src-elem https://matomo.naturkundemuseum-bw.de https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js https://openseadragon.github.io/openseadragon/openseadragon.js;
        img-src 'self' https://openseadragon.github.io/openseadragon/images/;

    CSP-digiphyll:
      headers:
      contentSecurityPolicy: >
        default-src 'self' 'unsafe-inline' 'unsafe-eval' *.smns-bw.org;
        frame-ancestors 'self' *.smns-bw.org;
        frame-src *.smns-bw.org https://144.41.33.40/;
        base-uri 'self';
        form-action 'self';
        img-src 'self' https://a.tile.openstreetmap.org https://b.tile.openstreetmap.org https://c.tile.openstreetmap.org https://unpkg.com/ data:;
        connect-src 'self';
        font-src 'self' data:;
        style-src 'self' 'unsafe-inline' data:;
        script-src 'self' 'unsafe-inline' 'unsafe-eval' *.smns-bw.org https://mathjax.rstudio.com/latest/ http://144.41.33.40/ data:;

    # Fixed: removed invalid 'unsafe-eval' from style-src
    CSP-geometroidea:
      headers:
      contentSecurityPolicy: >
        default-src 'self';
        style-src 'self' 'unsafe-inline' *.smns-bw.org https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.1/css/all.min.css https://fonts.googleapis.com/css2;
        font-src 'self' https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.1/webfonts/ https://fonts.gstatic.com/s/nunito/v25/;
        img-src 'self' data:;

    my-traefik-plugin-cookie-path-prefix:
      plugin:
        traefik-plugin-cookie-path-prefix:
          prefix: smns

    webportal-errors:
      errors:
        status:
          service: error-pages-webportal@docker
            - „404-503“
          query: „/{status}.html“

#    another-service-errors:
#      errors:
#        status:
#          - „404-503“
#        service: error-pages-another-service
#        query: „/error-pages-another-service/{status}.html“


[6] tls-opts.yml show
tls:
  options:
    tls-opts:
      minVersion: VersionTLS12
      cipherSuites:
        - „TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256“
        - „TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256“
        - „TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384“
        - „TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384“
        - „TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305“
        - „TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305“
      curvePreferences:
        - CurveP521
        - CurveP384
#      sniStrict: true

Traefik v3 Healthcheck (Docker)

Overview

This page describes how to set up a robust Docker healthcheck for Traefik v3.x.

It covers recent Traefik changes, the “gotchas” with TLS, and provides full configuration (compose and YAML) for reliable service monitoring.

Why Do I Need a Special Healthcheck for Traefik 3?

  • As of Traefik 3, the /ping endpoint (Traefik's native health endpoint) can only be bound to a non-TLS (HTTP/plaintext) entrypoint.
  • Any attempt to bind /ping to a TLS entrypoint (e.g., :443) causes it to be unavailable and will not log an error!
  • Many guides and blog posts referencing Traefik 2.x are now out of date.
  • Docker healthchecks are only updated when containers are recreated.

Step-by-Step Setup

1. Add a dedicated HTTP (non-TLS) entrypoint for health

Add this to your traefik.yml:

entryPoints:
  healthcheck:
    address: ":8082"

ping:
  entryPoint: healthcheck
  • Use any unused high port (8082 is common and outside process-bound port ranges).
  • Do not enable TLS or configure HTTP redirection for this entrypoint.
2. Update ''docker-compose.yml'' healthcheck section
healthcheck:
  test: [ "CMD", "wget", "--spider", "http://localhost:8082/ping" ]
  interval: 30s
  timeout: 5s
  retries: 3
  start_period: 10s
3. Recreate the container (!important)

After editing the healthcheck, you must remove and recreate the container to apply the updated check.

 docker compose down docker compose up -d

or for just the traefik service:

 docker compose rm traefik docker compose up -d traefik
4. Confirm it's working
  • Check status with:
 docker inspect traefik | grep Health -A 10
 wget --spider http://localhost:8082/ping
  • and expect “remote file exists” or HTTP 200.

Troubleshooting

  • If you see '404 Not Found' or status stays unhealthy, check:
    • The entryPoint in ping and traefik.yml matches (healthcheck)
    • Logs for ping endpoint registration (grep -i ping <traefik.log>)
    • Healthcheck in the running container is updated (see docker inspect)
  • If the healthcheck is still using the old endpoint (e.g., port 443), the container must be removed and recreated.

FAQ

  • Q: Why not use /ping on :443?
    • A: Traefik 3.x forbids it; /ping only works on a non-TLS (HTTP) entrypoint.
  • Q: Do I need to expose port 8082 externally?
    • A: No; healthchecks run inside the container.
  • Q: Can I combine ping and redirect on the same entrypoint?
    • A: No; keep your healthcheck entrypoint plain.

References