Jesse A. Green / notebook

Live demo: the DocCompiler guide library as a static, read-only snapshot (October 2026). No backend, no accounts, nothing to submit.

Traefik — User Guide

Generated May 2026 from traefik/traefik at 9893e89

AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it.

Traefik (v3) is an open-source HTTP reverse proxy and load balancer designed for microservices. It automatically discovers services through providers — Docker, Kubernetes, Consul, Nomad, ECS, and more — and configures routing dynamically without restarts. Point Traefik at your infrastructure, label your services, and routing just works.

This guide covers everything you need to install, configure, and operate Traefik in production. It is organized by what you want to accomplish, not by internal architecture. For deep-dive internals, deployment topologies, and exhaustive configuration reference, see the Admin Guide.


1. Quick Start

Get Traefik routing traffic to a sample service in under five minutes using Docker Compose.

1.1 Prerequisites

Requirement Minimum Version
Docker 20.10+
Docker Compose v2.0+
A terminal bash, zsh, PowerShell, etc.

1.2 One-File Setup

Create a docker-compose.yml:

services:
  traefik:
    image: "traefik:v3.7"
    command:
      - "--api.insecure=true"
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      - "--entryPoints.web.address=:80"
    ports:
      - "80:80"
      - "8080:8080"
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock:ro"
  whoami:
    image: "traefik/whoami"
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.whoami.rule=Host(`whoami.localhost`)"
      - "traefik.http.routers.whoami.entrypoints=web"

Start it:

docker compose up -d

Test it:

curl -H "Host: whoami.localhost" http://localhost

You should see a response from the whoami container showing request headers and the container's hostname.

1.3 What Just Happened

  1. Traefik started and connected to the Docker daemon via the mounted socket.
  2. It found the whoami container because traefik.enable=true was set.
  3. A router was created with the rule Host(\whoami.localhost`)listening on theweb` entrypoint (port 80).
  4. A service was auto-created pointing to the whoami container's exposed port.
  5. The dashboard became available at http://localhost:8080.

Note

The --api.insecure=true flag exposes the dashboard without authentication. This is fine for local development but must never be used in production. See Section 3.7 for securing the dashboard.

1.4 Explore the Dashboard

Open http://localhost:8080 in your browser. The dashboard is a single-page application showing:

Tab What It Shows
HTTP Routers All active routing rules and their status
HTTP Services Backend services and their health
HTTP Middlewares Request/response transformations in use
TCP Routers TCP-level routing rules
TCP Services TCP backend services
TCP Middlewares TCP-level middleware rules
UDP Routers UDP routing rules
UDP Services UDP backend services
Certificates TLS certificates managed by Traefik

The dashboard paginates at 100 items per page by default. Provider icons are shown for each resource, covering Docker, Kubernetes, Consul, ECS, etcd, File, HTTP, Hub, Knative, Nomad, Plugin, Redis, ZooKeeper, and the internal provider.

1.5 Core Concepts at a Glance

Before diving deeper, here are the five building blocks you will work with throughout this guide:

Concept What It Does Configuration Layer
EntryPoint Defines where Traefik listens (address + port) Static
Router Matches requests by rules, routes to services Dynamic
Service Points to your actual backend servers Dynamic
Middleware Transforms requests/responses between router and service Dynamic
Provider Supplies dynamic configuration (Docker, Kubernetes, File, etc.) Static

Static configuration is loaded once at startup and requires a restart to change. Dynamic configuration is supplied continuously by providers and updates without restarting Traefik.


2. Installation

The official Docker image is the most common deployment method:

docker pull traefik:v3.7

For Docker Compose, see the examples throughout this guide. The image is based on Alpine Linux and includes the Traefik binary at /usr/local/bin/traefik.

Tip

Always pin a specific version tag (e.g., traefik:v3.7) rather than using traefik:latest. This prevents unexpected breaking changes when you recreate containers.

2.2 Binary Download

Download the appropriate binary from the GitHub releases page for your platform (Linux, macOS, Windows, FreeBSD). Extract and place it in your PATH:

# Linux amd64 example
wget https://github.com/traefik/traefik/releases/download/v3.7.0/traefik_v3.7.0_linux_amd64.tar.gz
tar xzf traefik_v3.7.0_linux_amd64.tar.gz
sudo mv traefik /usr/local/bin/
traefik version

2.3 Helm Chart (Kubernetes)

helm repo add traefik https://traefik.github.io/charts
helm repo update
helm install traefik traefik/traefik

See Section 4.5 for Kubernetes-specific configuration.

2.4 Package Managers

Traefik is available through various package managers depending on your OS. Check your distribution's repositories for availability. The binary is self-contained with no external dependencies.

2.5 Verifying the Installation

traefik version

This prints the version, build date, and Go version used to compile the binary.

2.6 Healthcheck Command

Traefik includes a built-in healthcheck subcommand that queries the /ping endpoint:

traefik healthcheck

This is useful for Docker HEALTHCHECK directives and systemd health monitoring.


3. Configuration

Traefik uses two layers of configuration:

Layer What It Controls When It Loads How to Set It
Static EntryPoints, providers, certificate resolvers, logging, API At startup only File, CLI flags, or environment variables
Dynamic Routers, services, middlewares, TLS options Continuously (hot-reload) Providers (Docker labels, Kubernetes CRDs, file, etc.)

Changing static configuration requires a restart. Dynamic configuration is picked up automatically.

3.1 Static Configuration Methods

You can provide static configuration through three methods. Configuration loaders are applied in this order:

  1. Deprecation loader (handles deprecated settings)
  2. File loader (configuration file)
  3. Flag loader (CLI flags)
  4. Environment variable loader (env vars — highest priority)

Later loaders override earlier ones.

3.1.1 Configuration File (YAML)

Create traefik.yml:

api:
  insecure: true

entryPoints:
  web:
    address: ":80"
  websecure:
    address: ":443"

providers:
  docker:
    exposedByDefault: false

Start Traefik pointing to this file:

traefik --configFile=/etc/traefik/traefik.yml

3.1.2 Configuration File (TOML)

Create traefik.toml:

[api]
  insecure = true

[entryPoints]
  [entryPoints.web]
    address = ":80"
  [entryPoints.websecure]
    address = ":443"

[providers]
  [providers.docker]
    exposedByDefault = false

3.1.3 CLI Flags

traefik \
  --api.insecure=true \
  --entryPoints.web.address=:80 \
  --entryPoints.websecure.address=:443 \
  --providers.docker=true \
  --providers.docker.exposedbydefault=false

3.1.4 Environment Variables

Environment variables use the prefix TRAEFIK_ and replace dots/camelCase with underscores:

export TRAEFIK_API_INSECURE=true
export TRAEFIK_ENTRYPOINTS_WEB_ADDRESS=:80
export TRAEFIK_ENTRYPOINTS_WEBSECURE_ADDRESS=:443
export TRAEFIK_PROVIDERS_DOCKER=true
export TRAEFIK_PROVIDERS_DOCKER_EXPOSEDBYDEFAULT=false
traefik

3.2 EntryPoints

EntryPoints are the network addresses where Traefik listens for incoming traffic. They are static configuration.

entryPoints:
  web:
    address: ":80"
  websecure:
    address: ":443"
  custom:
    address: ":9000"

Default EntryPoints and asDefault

When at least one entrypoint has asDefault: true, only entrypoints with that flag are used as defaults for routers that do not specify their entrypoints explicitly. The internal traefik entrypoint (used for the dashboard/API) is never included as a default. UDP entrypoints are also excluded from defaults.

entryPoints:
  web:
    address: ":80"
    asDefault: true
  websecure:
    address: ":443"
    asDefault: true
  metrics:
    address: ":9100"
    # Not default — routers won't bind here unless explicitly configured

Important

If you define any entrypoint with asDefault: true, then all entrypoints WITHOUT that flag will no longer receive traffic from routers that omit entryPoints. This can silently break routing if you add asDefault to one entrypoint without reviewing existing router configurations.

3.3 Providers

Providers are the source of dynamic configuration. Enable at least one.

3.3.1 Docker Provider

providers:
  docker:
    exposedByDefault: false
    endpoint: "unix:///var/run/docker.sock"

When exposedByDefault is false (recommended), only containers with the label traefik.enable=true are discovered.

Mount the Docker socket in your Docker Compose file:

volumes:
  - "/var/run/docker.sock:/var/run/docker.sock:ro"

Important

Mount the Docker socket read-only (:ro) for security. Traefik only needs to read container metadata, not manage containers. Giving write access to the Docker socket effectively grants root access to the host.

3.3.2 Docker Swarm Provider

For Docker Swarm mode deployments:

providers:
  swarm:
    exposedByDefault: false
    endpoint: "unix:///var/run/docker.sock"

3.3.3 File Provider

Watch a directory of YAML/TOML files for dynamic configuration:

providers:
  file:
    directory: "/etc/traefik/dynamic/"
    watch: true

Or a single file:

providers:
  file:
    filename: "/etc/traefik/dynamic.yml"
    watch: true

The file provider is particularly useful for static backends, custom TLS certificates, and middleware definitions that do not come from a container orchestrator.

3.3.4 HTTP Provider

Poll a remote HTTP endpoint for dynamic configuration:

providers:
  http:
    endpoint: "https://config.example.com/traefik"
    pollInterval: "10s"

3.3.5 Kubernetes Providers

Traefik supports multiple Kubernetes integration methods:

Provider Use Case
Kubernetes CRD (IngressRoute) Traefik-native custom resources, full feature access
Kubernetes Ingress Standard Kubernetes Ingress resources
Kubernetes Gateway API Standards-based Gateway API resources
Knative Knative serving integration
providers:
  kubernetesCRD:
    allowCrossNamespace: true
  # OR
  kubernetesIngress: {}
  # OR
  kubernetesGateway: {}

3.3.6 Consul Catalog

providers:
  consulCatalog:
    endpoint:
      address: "http://consul:8500"
    exposedByDefault: false

3.3.7 Nomad

providers:
  nomad:
    endpoint:
      address: "http://nomad:4646"
    exposedByDefault: false

3.3.8 Amazon ECS

providers:
  ecs:
    clusters:
      - "my-cluster"
    region: "us-east-1"
    exposedByDefault: false

3.3.9 KV Store Providers

Traefik supports etcd, Redis, Consul KV, and ZooKeeper as key-value store providers for dynamic configuration:

providers:
  redis:
    endpoints:
      - "redis:6379"
  # OR
  etcd:
    endpoints:
      - "etcd:2379"
  # OR
  consulCatalog:
    endpoint:
      address: "http://consul:8500"
  # OR
  zooKeeper:
    endpoints:
      - "zk:2181"

3.3.10 REST Provider

Push configuration to Traefik via its REST API:

providers:
  rest: {}

3.3.11 Tailscale Provider

Traefik can integrate with Tailscale for service discovery and certificate management:

providers:
  tailscale: {}

3.4 HTTPS and Automatic Certificates

Traefik can automatically obtain and renew TLS certificates from Let's Encrypt (or any ACME provider) using certificate resolvers.

3.4.1 HTTP Challenge (Port 80 Required)

The HTTP challenge proves domain ownership by serving a token on port 80. This is the simplest ACME setup:

services:
  traefik:
    image: "traefik:v3.7"
    command:
      - "--api.insecure=true"
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      - "--entryPoints.web.address=:80"
      - "--entryPoints.websecure.address=:443"
      - "--certificatesresolvers.myresolver.acme.httpchallenge=true"
      - "--certificatesresolvers.myresolver.acme.httpchallenge.entrypoint=web"
      - "--certificatesresolvers.myresolver.acme.email=[email removed]"
      - "--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json"
    ports:
      - "80:80"
      - "443:443"
      - "8080:8080"
    volumes:
      - "./letsencrypt:/letsencrypt"
      - "/var/run/docker.sock:/var/run/docker.sock:ro"
  whoami:
    image: "traefik/whoami"
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.whoami.rule=Host(`whoami.example.com`)"
      - "traefik.http.routers.whoami.entrypoints=websecure"
      - "traefik.http.routers.whoami.tls.certresolver=myresolver"

Warning

The acme.json file stores private keys. Ensure it has restrictive permissions (chmod 600 acme.json) and is persisted via a Docker volume or bind mount. Losing this file means all certificates must be re-issued, which can hit Let's Encrypt rate limits.

3.4.2 TLS-ALPN Challenge (No Port 80 Needed)

The TLS-ALPN challenge proves ownership via a special TLS handshake on port 443. This is useful when port 80 is blocked or unavailable:

command:
  - "--certificatesresolvers.myresolver.acme.tlschallenge=true"
  - "--certificatesresolvers.myresolver.acme.email=[email removed]"
  - "--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json"

Only ports 443 and 8080 need to be published (port 80 is not required).

3.4.3 DNS Challenge (Wildcard Certificates)

The DNS challenge proves ownership via DNS TXT records. This is the only method that supports wildcard certificates and works even when no ports are publicly accessible:

command:
  - "--certificatesresolvers.myresolver.acme.dnschallenge=true"
  - "--certificatesresolvers.myresolver.acme.dnschallenge.provider=ovh"
  - "--certificatesresolvers.myresolver.acme.email=[email removed]"
  - "--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json"

DNS provider credentials are passed via environment variables. For example, with OVH:

environment:
  - OVH_ENDPOINT=ovh-eu
  - OVH_APPLICATION_KEY=xxx
  - OVH_APPLICATION_SECRET=xxx
  - OVH_CONSUMER_KEY=xxx

Each DNS provider has its own set of required environment variables. Dozens of providers are supported.

3.4.4 Tailscale Certificate Resolver

If you run Traefik within a Tailscale network, Traefik can obtain certificates from Tailscale automatically:

certificatesResolvers:
  tailscale: {}

3.4.5 ACME Challenge Comparison

Challenge Port Required Wildcard Support Use When
HTTP 80 (public) No Port 80 is open, simplest setup
TLS-ALPN 443 (public) No Port 80 is blocked
DNS None Yes Need wildcards, or no public ports
Tailscale None N/A Running inside a Tailscale network

3.5 HTTP to HTTPS Redirect

A common pattern is to redirect all HTTP traffic to HTTPS.

Option A: Entrypoint-level redirection (recommended)

entryPoints:
  web:
    address: ":80"
    http:
      redirections:
        entryPoint:
          to: websecure
          scheme: https
  websecure:
    address: ":443"

Option B: Middleware-based redirection

# Docker labels on a catch-all router
labels:
  - "traefik.http.routers.http-catchall.rule=HostRegexp(`.+`)"
  - "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.redirect-to-https.redirectscheme.permanent=true"

3.6 Logging

3.6.1 Application Logs

log:
  level: "INFO"    # TRACE, DEBUG, INFO, WARN, ERROR (default), FATAL, PANIC
  format: "json"   # or console (default)
  filePath: "/var/log/traefik/traefik.log"

The default log level is ERROR, which means you see very little output during normal operation. Set to DEBUG when troubleshooting.

Log rotation is handled automatically via the lumberjack library when writing to a file:

log:
  filePath: "/var/log/traefik/traefik.log"
  maxSize: 100      # MB before rotation
  maxBackups: 5     # rotated files to keep
  maxAge: 30        # days to retain
  compress: true    # gzip rotated files

3.6.2 Access Logs

accessLog:
  format: "json"       # "common" (extended CLF, default), "genericCLF" (standard CLF), "json"
  filePath: "/var/log/traefik/access.log"
Access Log Format Description
common Extended Common Log Format (default) — includes extra fields beyond standard CLF
genericCLF Standard Common Log Format
json JSON format — best for log aggregation systems

Tip

Use format: "json" for access logs if you plan to ingest them into a log aggregation system (ELK, Loki, Splunk). The JSON format is much easier to parse programmatically than the CLF formats.

3.6.3 OTLP Logging (Experimental)

Send logs via OpenTelemetry Protocol:

experimental:
  otlpLogs: true

This requires experimental.otlpLogs=true in static configuration.

3.7 Dashboard Security

The dashboard should never be exposed publicly without authentication. Here are two approaches:

Option A: BasicAuth middleware (simple)

# Dynamic config file
http:
  routers:
    dashboard:
      rule: "Host(`traefik.example.com`)"
      service: "api@internal"
      entryPoints:
        - websecure
      middlewares:
        - dashboard-auth
      tls:
        certResolver: myresolver

  middlewares:
    dashboard-auth:
      basicAuth:
        users:
          - "admin:$apr1$xyz$hashedpassword"

Generate the password hash with:

htpasswd -nb admin your-password

Option B: ForwardAuth (SSO/OAuth)

http:
  middlewares:
    dashboard-auth:
      forwardAuth:
        address: "http://authelia:9091/api/verify?rd=https://auth.example.com"
        trustForwardHeader: true
        authResponseHeaders:
          - "Remote-User"
          - "Remote-Groups"

3.8 Ping Endpoint

Enable the ping endpoint for load balancer health checks:

ping:
  entryPoint: "traefik"          # default
  terminatingStatusCode: 503     # returned during graceful shutdown

The /ping endpoint returns 200 OK when Traefik is healthy. During graceful shutdown, it returns the terminatingStatusCode (default 503) so upstream load balancers can stop sending traffic.

3.9 Version Checking and Telemetry

By default, Traefik checks for new versions every 24 hours (first check 10 minutes after startup) and can send anonymous usage statistics:

global:
  checkNewVersion: true          # default: true
  sendAnonymousUsage: false      # opt out of usage reporting

4. Core Usage

4.1 Routing with Rules

Routers match incoming requests using rules and forward them to services. The rule syntax supports these matchers:

Matcher Example Description
Host Host(\example.com`)` Match by hostname (exact)
HostRegexp HostRegexp(\.+.example.com`)` Match hostname by regex
PathPrefix PathPrefix(\/api`)` Match URL path prefix
Path Path(\/exact`)` Match exact URL path
Headers Headers(\X-Custom`, `value`)` Match by request header
HeadersRegexp HeadersRegexp(\Content-Type`, `application/.*`)` Match header by regex
Method Method(\GET`, `POST`)` Match by HTTP method
Query Query(\debug`, `true`)` Match by query parameter
ClientIP ClientIP(\192.168.1.0/24`)` Match by client IP/CIDR

Combine matchers with && (AND) and || (OR):

# Docker label
- "traefik.http.routers.myapp.rule=Host(`app.example.com`) && PathPrefix(`/api`)"

Router Fields

Field Purpose
entryPoints Which entrypoints this router listens on
rule The matching expression
service Target service name
middlewares List of middleware names to apply
priority Explicit priority (higher wins)
tls TLS settings (options, certResolver, domains)
observability Per-router access logs, metrics, tracing settings

Router Priority

When multiple routers match a request, the one with the highest priority wins. If no priority is set explicitly, Traefik calculates it based on rule length (longer rules get higher priority).

# Docker label — explicit priority
- "traefik.http.routers.myapp.priority=100"

4.2 Docker Labels Reference

All dynamic configuration for Docker-based services is set via container labels.

Basic Routing

labels:
  - "traefik.enable=true"
  - "traefik.http.routers.myapp.rule=Host(`myapp.example.com`)"
  - "traefik.http.routers.myapp.entrypoints=websecure"
  - "traefik.http.routers.myapp.tls.certresolver=myresolver"

Multiple Routers per Container

labels:
  - "traefik.http.routers.myapp-web.rule=Host(`myapp.example.com`)"
  - "traefik.http.routers.myapp-web.entrypoints=websecure"
  - "traefik.http.routers.myapp-api.rule=Host(`api.myapp.example.com`)"
  - "traefik.http.routers.myapp-api.entrypoints=websecure"

Specifying the Container Port

When a container exposes multiple ports, tell Traefik which one to use:

labels:
  - "traefik.http.services.myapp.loadbalancer.server.port=8080"

Attaching Middlewares

labels:
  - "traefik.http.routers.myapp.middlewares=rate-limit@docker,compress@docker"

Specifying the Docker Network

When the container is connected to multiple networks:

labels:
  - "traefik.docker.network=my-network"

4.3 Load Balancing

Traefik distributes traffic across backend servers using configurable strategies.

Load Balancing Strategies

Strategy Key Description
Weighted Round Robin wrr Default. Distributes requests proportionally by weight
Power of Two Choices p2c Picks two random servers, sends to the least loaded
Highest Random Weight hrw Consistent hashing — same client tends to reach same server
Least Time leasttime Sends to the server with the lowest response time

Configure via dynamic configuration:

http:
  services:
    my-service:
      loadBalancer:
        strategy: "p2c"
        servers:
          - url: "http://server1:8080"
          - url: "http://server2:8080"
          - url: "http://server3:8080"

Weighted Round Robin with Weights

http:
  services:
    my-service:
      loadBalancer:
        servers:
          - url: "http://server1:8080"
            weight: 3
          - url: "http://server2:8080"
            weight: 1

Server 1 receives 75% of traffic, server 2 receives 25%.

4.4 Health Checks

Health checks verify that backend servers are able to handle requests.

Active Health Checks

Traefik periodically sends probe requests to your backends:

http:
  services:
    my-service:
      loadBalancer:
        healthCheck:
          path: "/health"
          interval: "10s"
          timeout: "5s"
          scheme: "http"
          method: "GET"
          status: 200
          followRedirects: true
Parameter Default Description
path — URL path to probe
interval 30s Time between probes
unhealthyInterval — Interval when server is unhealthy (overrides interval)
timeout 5s Maximum time to wait for probe response
scheme — Override scheme (http/https)
mode http Protocol mode (http or grpc)
method GET HTTP method for probe
status — Expected status code
followRedirects true Follow redirects during probe
hostname — Override Host header in probe request
port — Override port for probe
headers — Custom headers to include in probe

Passive Health Checks

Passive checks monitor real traffic for failures without sending extra probe requests:

http:
  services:
    my-service:
      loadBalancer:
        healthCheck:
          path: "/health"
        passiveHealthCheck:
          failureWindow: "10s"
          maxFailedAttempts: 3
Parameter Default Description
failureWindow 10s Time window for counting failures
maxFailedAttempts 1 Failures within the window before marking unhealthy

4.5 Kubernetes Usage

IngressRoute (CRD — Full Feature Access)

The Traefik CRD provides access to all Traefik features:

apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
  name: myapp
spec:
  entryPoints:
    - websecure
  routes:
    - match: Host(`myapp.example.com`)
      kind: Rule
      services:
        - name: myapp-svc
          port: 80
      middlewares:
        - name: rate-limit
  tls:
    certResolver: myresolver

Standard Kubernetes Ingress

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: myapp
  annotations:
    traefik.ingress.kubernetes.io/router.entrypoints: websecure
    traefik.ingress.kubernetes.io/router.tls: "true"
spec:
  rules:
    - host: myapp.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: myapp-svc
                port:
                  number: 80

Gateway API

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: myapp
spec:
  parentRefs:
    - name: traefik-gateway
  hostnames:
    - "myapp.example.com"
  rules:
    - matches:
        - path:
            type: PathPrefix
            value: /
      backendRefs:
        - name: myapp-svc
          port: 80

4.6 Sticky Sessions

Sticky sessions ensure a client always reaches the same backend server. Traefik implements this via cookies:

http:
  services:
    my-service:
      loadBalancer:
        sticky:
          cookie:
            name: "server_id"
            secure: true
            httpOnly: true
            sameSite: "lax"
            maxAge: 3600
            path: "/"
Parameter Default Description
name auto-generated Cookie name
secure false Secure flag (send only over HTTPS)
httpOnly false HttpOnly flag (no JavaScript access)
sameSite — none, lax, or strict
maxAge — Cookie lifetime in seconds
path / Cookie path scope
domain — Cookie domain scope

Docker label equivalent:

labels:
  - "traefik.http.services.myapp.loadbalancer.sticky.cookie.name=server_id"
  - "traefik.http.services.myapp.loadbalancer.sticky.cookie.secure=true"
  - "traefik.http.services.myapp.loadbalancer.sticky.cookie.httponly=true"
  - "traefik.http.services.myapp.loadbalancer.sticky.cookie.samesite=lax"

4.7 TCP and UDP Routing

Traefik can route raw TCP and UDP traffic in addition to HTTP.

TCP Router

tcp:
  routers:
    my-tcp:
      entryPoints:
        - "tcp-ep"
      rule: "HostSNI(`db.example.com`)"
      service: "my-tcp-service"
      tls: {}

  services:
    my-tcp-service:
      loadBalancer:
        servers:
          - address: "db-server:5432"

UDP Router

udp:
  routers:
    my-udp:
      entryPoints:
        - "udp-ep"
      service: "my-udp-service"

  services:
    my-udp-service:
      loadBalancer:
        servers:
          - address: "dns-server:53"

4.8 Service Types

Beyond the standard loadBalancer, Traefik supports advanced service composition:

Mirroring (Shadow Traffic)

Send a copy of traffic to one or more mirrors for testing or monitoring:

http:
  services:
    mirrored-service:
      mirroring:
        service: "main-service"
        mirrors:
          - name: "shadow-service"
            percent: 10

Only responses from the main service are returned to the client. Mirror responses are discarded.

Failover

Route to a fallback service when the primary is unhealthy:

http:
  services:
    failover-service:
      failover:
        service: "primary-service"
        fallback: "backup-service"

Weighted Round Robin (Service-Level Traffic Splitting)

Distribute traffic across services by weight — commonly used for canary deployments and A/B testing:

http:
  services:
    canary:
      weighted:
        services:
          - name: "v1"
            weight: 90
          - name: "v2"
            weight: 10

4.9 Response Forwarding

Control how Traefik forwards responses from backends to clients:

http:
  services:
    my-service:
      loadBalancer:
        responseForwarding:
          flushInterval: "100ms"

The default flushInterval is 100ms. Set to a negative value (e.g., -1ms) to flush after every write — essential for Server-Sent Events (SSE) and streaming responses.

4.10 Encoded Path Characters

By default, Traefik rejects requests containing certain encoded characters in the URL path for security:

Character Encoded Form Attack Vector
/ %2F Path traversal
\ %5C Path traversal (Windows)
NUL %00 Null byte injection
; %3B Parameter injection
% %25 Double encoding
? %3F Query string injection
# %23 Fragment injection

If your application legitimately needs encoded characters (e.g., REST APIs with encoded slashes in resource identifiers), individual characters can be allowed via router-level or middleware-level configuration using the EncodedCharacters middleware or per-router settings such as allowEncodedSlash and allowEncodedBackSlash. See the Admin Guide for full configuration reference.


5. Common Tasks

5.1 HTTP Middlewares

Middlewares sit between routers and services, transforming requests and responses. Multiple middlewares can be chained in order.

Here is the complete list of available HTTP middlewares:

Middleware Purpose
AddPrefix Prepend a path prefix to requests
StripPrefix Remove a path prefix from requests
StripPrefixRegex Remove a path prefix matching a regex
ReplacePath Replace the entire request path
ReplacePathRegex Replace path components matching a regex
Chain Combine multiple middlewares into a reusable group
IPAllowList Allow only specific IP ranges
IPWhiteList Deprecated — use IPAllowList
Headers Add/modify headers, configure CORS and security headers
EncodedCharacters Control which encoded URL characters are allowed
Errors (ErrorPage) Serve custom error pages
RateLimit Limit request rate per client
RedirectRegex Redirect by regex pattern
RedirectScheme Redirect to a different scheme (e.g., HTTP to HTTPS)
BasicAuth HTTP Basic authentication
DigestAuth HTTP Digest authentication
ForwardAuth Delegate authentication to an external service
InFlightReq Limit simultaneous in-flight requests
Buffering Buffer request/response bodies
CircuitBreaker Stop sending traffic to failing backends
Compress Compress responses (gzip, br, zstd)
PassTLSClientCert Forward client certificate info to backends
Retry Retry failed requests
ContentType Auto-detect Content-Type headers
GrpcWeb Convert gRPC-Web to standard gRPC
Plugin Custom middleware from plugins

5.1.1 Rate Limiting

Protect services from excessive requests:

http:
  middlewares:
    rate-limit:
      rateLimit:
        average: 100     # requests per period
        period: "1s"     # default: 1s
        burst: 50        # max burst above average (default: 1)

The sourceCriterion controls how clients are identified for rate limiting:

http:
  middlewares:
    rate-limit:
      rateLimit:
        average: 10
        burst: 20
        sourceCriterion:
          ipStrategy:
            depth: 1     # use X-Forwarded-For, skip 1 proxy

Other source criteria options:

  • requestHeaderName — rate limit by a specific header value
  • requestHost — rate limit by the Host header

Distributed Rate Limiting with Redis:

For multi-instance Traefik deployments, rate limit state must be shared. Without Redis, each Traefik instance maintains its own independent counter:

http:
  middlewares:
    rate-limit:
      rateLimit:
        average: 100
        burst: 50
        redis:
          endpoints:
            - "redis:6379"
          password: "secret"
          db: 0
          poolSize: 10
          readTimeout: "3s"
          writeTimeout: "3s"
Redis Parameter Default Description
endpoints ["localhost:6379"] Redis server addresses
tls — TLS configuration for Redis connection
username — Redis username
password — Redis password
db 0 Redis database number
poolSize — Connection pool size
minIdleConns — Minimum idle connections
maxActiveConns — Maximum active connections
readTimeout 3s Read timeout
writeTimeout 3s Write timeout

Docker labels:

labels:
  - "traefik.http.middlewares.my-ratelimit.ratelimit.average=100"
  - "traefik.http.middlewares.my-ratelimit.ratelimit.burst=50"

5.1.2 Headers, Security Headers, and CORS

The Headers middleware is versatile — it handles custom headers, security headers, and CORS in a single configuration block.

Custom request/response headers:

http:
  middlewares:
    custom-headers:
      headers:
        customRequestHeaders:
          X-Forwarded-Proto: "https"
          X-Custom-Header: "value"
        customResponseHeaders:
          X-Response-Custom: "value"
          Server: ""   # empty string removes the header

Security headers:

http:
  middlewares:
    secure-headers:
      headers:
        stsSeconds: 31536000
        stsIncludeSubdomains: true
        stsPreload: true
        forceSTSHeader: true
        contentTypeNosniff: true
        browserXssFilter: true
        frameDeny: true
        contentSecurityPolicy: "default-src 'self'"
        referrerPolicy: "strict-origin-when-cross-origin"
        permissionsPolicy: "camera=(), microphone=(), geolocation=()"
        allowedHosts:
          - "example.com"
          - "www.example.com"
Security Header Effect
stsSeconds Strict-Transport-Security max-age
stsIncludeSubdomains Include subdomains in HSTS
stsPreload Signal HSTS preload eligibility
forceSTSHeader Send STS even without TLS
contentTypeNosniff X-Content-Type-Options: nosniff
browserXssFilter X-XSS-Protection: 1; mode=block
frameDeny X-Frame-Options: DENY
customFrameOptionsValue Custom X-Frame-Options value (overrides frameDeny)
contentSecurityPolicy Content-Security-Policy header
referrerPolicy Referrer-Policy header
permissionsPolicy Permissions-Policy header
allowedHosts Restrict which Host header values are accepted

CORS configuration:

http:
  middlewares:
    cors:
      headers:
        accessControlAllowMethods:
          - "GET"
          - "POST"
          - "PUT"
          - "DELETE"
          - "OPTIONS"
        accessControlAllowHeaders:
          - "Content-Type"
          - "Authorization"
        accessControlAllowOriginList:
          - "https://app.example.com"
          - "https://www.example.com"
        accessControlAllowOriginListRegex:
          - "https://.*\\.example\\.com"
        accessControlExposeHeaders:
          - "X-Custom-Header"
        accessControlMaxAge: 86400
        accessControlAllowCredentials: true
        addVaryHeader: true

Docker labels for common security headers:

labels:
  - "traefik.http.middlewares.secure.headers.stsSeconds=31536000"
  - "traefik.http.middlewares.secure.headers.stsIncludeSubdomains=true"
  - "traefik.http.middlewares.secure.headers.contentTypeNosniff=true"
  - "traefik.http.middlewares.secure.headers.frameDeny=true"
  - "traefik.http.middlewares.secure.headers.browserXssFilter=true"

5.1.3 Compression

Compress responses to reduce bandwidth:

http:
  middlewares:
    compress:
      compress:
        minResponseBodyBytes: 1024     # default: 1024
        encodings:                      # default: gzip, br, zstd
          - "br"
          - "gzip"
          - "zstd"
        excludedContentTypes:
          - "text/event-stream"
        defaultEncoding: "gzip"
Parameter Default Description
minResponseBodyBytes 1024 Minimum body size to compress
encodings ["gzip","br","zstd"] Supported compression algorithms
defaultEncoding — Fallback encoding when client does not specify preference
excludedContentTypes — Content types to never compress
includedContentTypes — Only compress these content types (exclusive with excluded)

5.1.4 Authentication

BasicAuth:

http:
  middlewares:
    auth:
      basicAuth:
        users:
          - "admin:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/"
        usersFile: "/etc/traefik/users"    # alternative: load from file
        realm: "traefik"                    # default
        removeHeader: true                  # strip Authorization before forwarding
        headerField: "X-WebAuth-User"       # forward username in this header

Generate hashed passwords:

# Standard htpasswd
htpasswd -nb admin strongpassword

# For Docker labels, double-escape $ signs:
echo $(htpasswd -nb admin strongpassword) | sed -e s/\\$/\\$\\$/g

ForwardAuth (SSO/OAuth integration):

http:
  middlewares:
    sso:
      forwardAuth:
        address: "http://authelia:9091/api/verify"
        trustForwardHeader: true
        authResponseHeaders:
          - "Remote-User"
          - "Remote-Groups"
          - "Remote-Email"
        authResponseHeadersRegex: "^Remote-"
        authRequestHeaders:
          - "Accept"
          - "Cookie"
ForwardAuth Parameter Default Description
address — URL of the auth service (required)
trustForwardHeader false Trust existing X-Forwarded-* headers
authResponseHeaders — Headers to copy from auth response to request
authResponseHeadersRegex — Regex to match auth response headers to copy
authRequestHeaders — Headers to forward to auth service
maxResponseBodySize — Max auth response body to read
addAuthCookiesToResponse — Cookies from auth response to add to client response
headerField — Header to store auth user info
forwardBody false Forward request body to auth service
maxBodySize -1 (unlimited) Max body size to forward to auth service
preserveLocationHeader false Preserve Location header from auth response
preserveRequestMethod false Use original HTTP method for auth request
authSigninURL — Redirect URL when authentication fails

DigestAuth:

http:
  middlewares:
    auth:
      digestAuth:
        users:
          - "admin:traefik:hashed"

5.1.5 IP Allowlisting

Restrict access to specific IP ranges:

http:
  middlewares:
    ip-allow:
      ipAllowList:
        sourceRange:
          - "10.0.0.0/8"
          - "172.16.0.0/12"
          - "192.168.0.0/16"
        rejectStatusCode: 403     # default: 403

Use ipStrategy when behind other proxies to extract the real client IP:

http:
  middlewares:
    ip-allow:
      ipAllowList:
        sourceRange:
          - "203.0.113.0/24"
        ipStrategy:
          depth: 1    # skip 1 proxy in X-Forwarded-For

Note

The ipWhiteList middleware is deprecated. Use ipAllowList instead. Existing configurations using ipWhiteList will continue to work but should be migrated.

5.1.6 Path Manipulation

AddPrefix — prepend a path:

http:
  middlewares:
    add-api:
      addPrefix:
        prefix: "/api/v1"

StripPrefix — remove a path prefix:

http:
  middlewares:
    strip-api:
      stripPrefix:
        prefixes:
          - "/api"

StripPrefixRegex — remove by regex:

http:
  middlewares:
    strip-regex:
      stripPrefixRegex:
        regex:
          - "/api/v[0-9]+"

ReplacePath — replace the entire path:

http:
  middlewares:
    replace:
      replacePath:
        path: "/new-path"

ReplacePathRegex — replace by regex:

http:
  middlewares:
    replace-regex:
      replacePathRegex:
        regex: "^/old/(.*)"
        replacement: "/new/$1"

5.1.7 Circuit Breaker

Protect services from cascading failures:

http:
  middlewares:
    circuit-breaker:
      circuitBreaker:
        expression: "ResponseCodeRatio(500, 600, 0, 600) > 0.30"
        checkPeriod: "100ms"         # default
        fallbackDuration: "10s"      # default — time to stay open after tripping
        recoveryDuration: "10s"      # default — time in half-open state
        responseCode: 503            # default — code returned when open

The circuit breaker transitions through three states:

State Behavior
Closed Normal operation — all requests forwarded, expression evaluated periodically
Open Breaker tripped — all requests immediately get responseCode (503)
Half-Open After fallbackDuration, let some requests through to test recovery

If requests succeed during half-open, the breaker returns to closed after recoveryDuration. If they fail, it goes back to open.

5.1.8 Retry

Automatically retry failed requests:

http:
  middlewares:
    retry:
      retry:
        attempts: 3
        initialInterval: "100ms"
        maxRequestBodyBytes: 2097152   # 2MB default (2 * 1024 * 1024)

Caution

Be careful with retries on non-idempotent requests (POST, PUT, DELETE). Retrying these can cause duplicate operations. Use retries primarily for GET requests or idempotent endpoints. The maxRequestBodyBytes limit (default 2MB) prevents unbounded memory usage when buffering request bodies for retries.

5.1.9 Custom Error Pages

Show custom error pages for specific status codes:

http:
  middlewares:
    error-pages:
      errors:
        status:
          - "500-599"
        service: "error-service"
        query: "/{status}.html"

The query field supports these template variables:

Variable Description
{status} The HTTP status code returned
{originalStatus} The original status from the backend
{url} The original request URL

Status rewrites let you change the status code sent to the error service:

http:
  middlewares:
    error-pages:
      errors:
        status:
          - "500-599"
        statusRewrites:
          "500": 200
          "503": 200
        service: "error-service"
        query: "/{status}.html"

5.1.10 In-Flight Request Limiting

Limit the number of simultaneous requests being processed:

http:
  middlewares:
    inflight:
      inFlightReq:
        amount: 100
        sourceCriterion:
          requestHost: true

5.1.11 Buffering

Buffer request and response bodies:

http:
  middlewares:
    buffer:
      buffering:
        maxRequestBodyBytes: 10485760    # 10MB
        maxResponseBodyBytes: 10485760
        retryExpression: "IsNetworkError() && Attempts() < 2"

5.1.12 Redirect by Regex

http:
  middlewares:
    redirect:
      redirectRegex:
        regex: "^https://old.example.com/(.*)"
        replacement: "https://new.example.com/${1}"
        permanent: true

5.1.13 Middleware Chains

Combine multiple middlewares into a reusable chain:

http:
  middlewares:
    secured:
      chain:
        middlewares:
          - rate-limit
          - ip-allow
          - secure-headers
          - auth

  routers:
    myapp:
      rule: "Host(`app.example.com`)"
      middlewares:
        - secured
      service: myapp

Docker label for middleware chain:

labels:
  - "traefik.http.routers.myapp.middlewares=rate-limit@docker,compress@docker,auth@docker"

5.1.14 gRPC-Web

Convert gRPC-Web requests to standard gRPC:

http:
  middlewares:
    grpcweb:
      grpcWeb: {}

5.1.15 Pass TLS Client Certificate

Forward TLS client certificate information to backend services:

http:
  middlewares:
    pass-cert:
      passTLSClientCert:
        pem: true

5.1.16 Content-Type Auto-Detection

http:
  middlewares:
    autodetect:
      contentType: {}

5.2 TCP Middlewares

TCP-level middlewares have a smaller feature set:

TCP IP Allowlisting:

tcp:
  middlewares:
    tcp-ipallow:
      ipAllowList:
        sourceRange:
          - "10.0.0.0/8"

TCP In-Flight Connection Limiting:

tcp:
  middlewares:
    tcp-inflight:
      inFlightConn:
        amount: 100

5.3 TLS Configuration

TLS Versions

Constant TLS Version
VersionTLS10 TLS 1.0
VersionTLS11 TLS 1.1
VersionTLS12 TLS 1.2
VersionTLS13 TLS 1.3
tls:
  options:
    default:
      minVersion: "VersionTLS12"
      maxVersion: "VersionTLS13"

Cipher Suites

Custom cipher suites can be specified for TLS 1.2 connections:

tls:
  options:
    default:
      cipherSuites:
        - "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"
        - "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384"

TLS 1.3 cipher suites are fixed by the Go standard library and cannot be customized.

Curve Preferences

tls:
  options:
    default:
      curvePreferences:
        - "X25519"
        - "CurveP256"
        - "CurveP384"
        - "CurveP521"
        - "X25519MLKEM768"

X25519MLKEM768 is a post-quantum hybrid key exchange mechanism combining X25519 with ML-KEM-768 for forward-looking protection against quantum computing threats.

OCSP Stapling

Traefik supports OCSP stapling automatically. When TLS certificates include OCSP responder URLs, Traefik fetches and caches OCSP responses, stapling them during TLS handshakes. No configuration is needed.

Manual Certificate Loading

tls:
  certificates:
    - certFile: "/certs/example.com.crt"
      keyFile: "/certs/example.com.key"
    - certFile: "/certs/wildcard.example.com.crt"
      keyFile: "/certs/wildcard.example.com.key"

5.4 ServersTransport (Backend TLS)

Configure how Traefik connects to backend services over TLS:

http:
  serversTransports:
    my-transport:
      serverName: "backend.example.com"
      insecureSkipVerify: false
      rootCAs:
        - "/certs/ca.pem"
      certificates:
        - certFile: "/certs/client.pem"
          keyFile: "/certs/client-key.pem"
      maxIdleConnsPerHost: 200
      forwardingTimeouts:
        dialTimeout: "30s"
        responseHeaderTimeout: "0s"
        idleConnTimeout: "90s"
        readIdleTimeout: "0s"
        pingTimeout: "15s"
      disableHTTP2: false
Parameter Default Description
serverName — Server name for TLS verification
insecureSkipVerify false Skip TLS certificate verification
rootCAs — Custom CA certificates for verification
certificates — Client certificates for mTLS
maxIdleConnsPerHost — Max idle connections per backend host
dialTimeout 30s Timeout for establishing connection
responseHeaderTimeout — Timeout waiting for response headers
idleConnTimeout 90s Timeout for idle connections
readIdleTimeout — Timeout for idle HTTP/2 connections
pingTimeout 15s Timeout for HTTP/2 pings
disableHTTP2 false Force HTTP/1.1 to backends
peerCertURI — Expected peer certificate URI

SPIFFE Integration

For service mesh environments using SPIFFE:

http:
  serversTransports:
    spiffe-transport:
      spiffe:
        ids:
          - "spiffe://trust-domain/workload"
        trustDomain: "trust-domain"

5.5 Plugins

Traefik supports plugins to extend its functionality with custom middlewares, providers, and more.

Remote Plugins (from Traefik Plugin Registry)

experimental:
  plugins:
    my-plugin:
      moduleName: "github.com/author/my-plugin"
      version: "v1.0.0"

Remote plugins are powered by the Yaegi Go interpreter — they are interpreted Go code, not compiled binaries.

Local Plugins

experimental:
  localPlugins:
    my-local-plugin:
      moduleName: "github.com/author/my-local-plugin"

Local plugins are loaded from ./plugins-local/ relative to Traefik's working directory.

WebAssembly (WASI) Plugins

Traefik also supports WASM-based plugins for portable, sandboxed middleware execution. WASI plugins offer stronger isolation than Yaegi plugins.

Plugin Storage and Failure Handling

Plugin storage is located at ./plugins-storage/ by default. Control startup behavior when plugins fail:

experimental:
  abortOnPluginFailure: true    # Traefik won't start if any plugin fails to load

5.6 Observability

5.6.1 Prometheus Metrics

metrics:
  prometheus: {}

Metrics are exposed on the Traefik entrypoint (default port 8080) at /metrics.

5.6.2 Datadog

metrics:
  datadog:
    address: "127.0.0.1:8125"
    pushInterval: "10s"

5.6.3 StatsD

metrics:
  statsd:
    address: "127.0.0.1:8125"
    pushInterval: "10s"

5.6.4 InfluxDB2

metrics:
  influxDB2:
    address: "http://influxdb:8086"
    token: "my-token"
    org: "my-org"
    bucket: "traefik"
    pushInterval: "10s"

5.6.5 OpenTelemetry (OTLP)

metrics:
  otlp:
    pushInterval: "10s"

tracing:
  otlp: {}

5.6.6 Per-Router Observability

Individual routers can opt in or out of access logs, metrics, and tracing:

http:
  routers:
    myapp:
      rule: "Host(`app.example.com`)"
      service: myapp
      observability:
        accessLogs: true
        metrics: true
        tracing: true
        traceVerbosity: "recommended"

5.7 Systemd Integration

When running as a systemd service, Traefik integrates with systemd's notification protocol:

  • Sends READY=1 via sd_notify when startup is complete
  • Sends WATCHDOG=1 at half the configured watchdog interval (by querying its own /ping endpoint)

Example systemd unit:

[Unit]
Description=Traefik Proxy
After=network-online.target
Wants=network-online.target

[Service]
Type=notify
ExecStart=/usr/local/bin/traefik --configFile=/etc/traefik/traefik.yml
WatchdogSec=30s
Restart=on-failure
RestartSec=5s

[Install]
WantedBy=multi-user.target

5.8 Complete Docker Compose Examples

These are complete, ready-to-use Docker Compose configurations for common deployment patterns.

HTTPS with HTTP Challenge (Most Common)

services:
  traefik:
    image: "traefik:v3.7"
    command:
      - "--api.insecure=true"
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      - "--entryPoints.web.address=:80"
      - "--entryPoints.websecure.address=:443"
      - "--certificatesresolvers.myresolver.acme.httpchallenge=true"
      - "--certificatesresolvers.myresolver.acme.httpchallenge.entrypoint=web"
      - "--certificatesresolvers.myresolver.acme.email=[email removed]"
      - "--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json"
    ports:
      - "80:80"
      - "443:443"
      - "8080:8080"
    volumes:
      - "./letsencrypt:/letsencrypt"
      - "/var/run/docker.sock:/var/run/docker.sock:ro"
  whoami:
    image: "traefik/whoami"
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.whoami.rule=Host(`whoami.example.com`)"
      - "traefik.http.routers.whoami.entrypoints=websecure"
      - "traefik.http.routers.whoami.tls.certresolver=myresolver"

HTTPS with TLS-ALPN Challenge (No Port 80)

services:
  traefik:
    image: "traefik:v3.7"
    command:
      - "--api.insecure=true"
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      - "--entryPoints.websecure.address=:443"
      - "--certificatesresolvers.myresolver.acme.tlschallenge=true"
      - "--certificatesresolvers.myresolver.acme.email=[email removed]"
      - "--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json"
    ports:
      - "443:443"
      - "8080:8080"
    volumes:
      - "./letsencrypt:/letsencrypt"
      - "/var/run/docker.sock:/var/run/docker.sock:ro"
  whoami:
    image: "traefik/whoami"
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.whoami.rule=Host(`whoami.example.com`)"
      - "traefik.http.routers.whoami.entrypoints=websecure"
      - "traefik.http.routers.whoami.tls.certresolver=myresolver"

HTTPS with DNS Challenge (Wildcard Certificates)

services:
  traefik:
    image: "traefik:v3.7"
    command:
      - "--api.insecure=true"
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      - "--entryPoints.websecure.address=:443"
      - "--certificatesresolvers.myresolver.acme.dnschallenge=true"
      - "--certificatesresolvers.myresolver.acme.dnschallenge.provider=ovh"
      - "--certificatesresolvers.myresolver.acme.email=[email removed]"
      - "--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json"
    ports:
      - "443:443"
      - "8080:8080"
    environment:
      - OVH_ENDPOINT=ovh-eu
      - OVH_APPLICATION_KEY=xxx
      - OVH_APPLICATION_SECRET=xxx
      - OVH_CONSUMER_KEY=xxx
    volumes:
      - "./letsencrypt:/letsencrypt"
      - "/var/run/docker.sock:/var/run/docker.sock:ro"
  whoami:
    image: "traefik/whoami"
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.whoami.rule=Host(`whoami.example.com`)"
      - "traefik.http.routers.whoami.entrypoints=websecure"
      - "traefik.http.routers.whoami.tls.certresolver=myresolver"

6. Troubleshooting

6.1 Startup and Configuration Issues

T1. Traefik starts but no routes are active

Symptom: Dashboard shows zero routers or services.

Causes and fixes:

  • Verify a provider is configured. Without at least one provider, there is no source of dynamic configuration.
  • For Docker: ensure providers.docker=true and the Docker socket is mounted.
  • For Docker: if exposedByDefault=false, check that containers have traefik.enable=true.
  • Check Traefik logs at DEBUG level: --log.level=DEBUG.

T2. "Unable to find the IP address" for a container

Symptom: Error in logs about not finding a container's IP.

Fix: Ensure Traefik and the target container share a Docker network. By default, containers on different networks cannot communicate. Add both to the same network or specify the network via label: traefik.docker.network=my-network.

T3. Certificate resolver fails silently

Symptom: HTTPS connections fail with self-signed or missing certificate.

Fixes:

  • Check that the ACME email is set and valid.
  • For HTTP challenge: port 80 must be publicly reachable.
  • For TLS-ALPN challenge: port 443 must be publicly reachable.
  • For DNS challenge: verify environment variables for your DNS provider.
  • Check acme.json permissions — must be 600.
  • Look for errors in logs at DEBUG level.

T4. Dashboard returns 404

Symptom: http://localhost:8080 shows nothing.

Fixes:

  • Ensure --api.insecure=true is set (for local dev) or a proper router pointing to api@internal exists.
  • Verify port 8080 is published in Docker Compose.
  • The dashboard listens on the traefik entrypoint by default.

T5. Static configuration changes not taking effect

Symptom: You changed traefik.yml but nothing happened.

Fix: Static configuration is only loaded at startup. Restart Traefik after any changes to entrypoints, providers, or certificate resolvers. Dynamic configuration (routers, services, middlewares) reloads automatically.

T6. "port is missing" error on startup

Symptom: Traefik logs an error about a missing or invalid port.

Fix: EntryPoint addresses must include a port number. Use the format :80, :443, or 0.0.0.0:80. An address without a colon and port will fail.

T7. Environment variables ignored

Symptom: Settings passed via environment variables are not applied.

Fix: Traefik environment variables must use the prefix TRAEFIK_ with underscores replacing dots and uppercase for all parts. For example, --entryPoints.web.address becomes TRAEFIK_ENTRYPOINTS_WEB_ADDRESS.

6.2 Routing Issues

T8. Wrong service receives traffic

Symptom: Requests go to the wrong backend.

Fixes:

  • Check router priorities. Longer rules get higher auto-priority, but explicit priority values override this. Two routers with the same priority on overlapping rules cause unpredictable behavior.
  • Use the dashboard to inspect which routers are active and their priorities.
  • Remember that if multiple routers match, the highest priority wins.

T9. 404 on a route that should work

Symptom: Router exists in dashboard but returns 404.

Fixes:

  • Verify the Host header matches the rule. Use curl -H "Host: example.com".
  • Check that the service has healthy servers (dashboard shows green status).
  • Ensure the entrypoint in the router matches the port you are hitting.
  • Confirm the container is running and the correct port is exposed.

T10. 502 Bad Gateway

Symptom: Router matches but backend returns 502.

Fixes:

  • The backend service is down or unreachable from Traefik's network.
  • Wrong port specified in loadbalancer.server.port.
  • Backend uses HTTPS but Traefik connects via HTTP (or vice versa).
  • For Kubernetes with PathPrefix: some frameworks (e.g., ASP.NET with UsePathBase) may not handle the forwarded path correctly (GitHub issue #12997).

T11. 400 Bad Request on POST requests via Gateway API

Symptom: GET works but POST returns 400 through Gateway API HTTPRoute.

This is a known issue (GitHub issue #12345) where certain POST requests through Gateway API HTTPRoute resources return 400 Bad Request. Check if the issue has been resolved in your Traefik version.

T12. Encoded characters rejected (403 or 400)

Symptom: URLs containing %2F, %5C, or other encoded characters are blocked.

Fix: Traefik rejects encoded path characters by default for security (%2F, %5C, %00, %3B, %25, %3F, %23). If your application requires them, enable the specific characters you need via the EncodedCharacters middleware or per-router settings.

T13. Path rewriting stopped working after upgrade

Symptom: Path rewriting that previously worked now fails.

This is a known regression (GitHub issue #12982) introduced after #12905. If path rewriting breaks after an upgrade, check the release notes and consider pinning to a version before the regression.

6.3 TLS and Certificate Issues

T14. TLS handshake failures with long ClientHello

Symptom: TCP connections terminated during TLS handshake, especially with large or fragmented ClientHello messages.

This is a known issue (GitHub issue #12871). Long or fragmented TLS ClientHello messages can cause Traefik to terminate the TCP connection. Workaround: check if the client is sending an unusually large ClientHello (e.g., with many cipher suites or extensions) and reduce it if possible.

T15. "acme: error 429" — rate limited by Let's Encrypt

Symptom: Certificate issuance fails with rate limit error.

Fix: Let's Encrypt enforces rate limits (50 certificates per registered domain per week). Use the staging server for testing:

command:
  - "--certificatesresolvers.myresolver.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory"

Tip

Always use the Let's Encrypt staging server during initial setup and testing. Switch to production only once everything works correctly. Staging certificates will show browser warnings but prove your configuration is correct.

T16. Mixed HTTP/HTTPS causes redirect loop

Symptom: Browser shows "too many redirects".

Fix: Ensure the HTTPS router and the HTTP-to-HTTPS redirect router use different entrypoints. The redirect should be on web (port 80) and the actual router on websecure (port 443). If both listen on the same entrypoint, the redirect triggers infinitely.

T17. Self-signed certificate served instead of Let's Encrypt

Symptom: Browser shows certificate warning even though ACME is configured.

Fixes:

  • Verify the router has tls.certresolver=myresolver set.
  • Check that the domain in the Host rule matches the requested domain exactly.
  • Inspect acme.json — if it is empty or missing the domain, the challenge failed. Check logs at DEBUG level.
  • Ensure the required port (80 for HTTP challenge, 443 for TLS-ALPN) is publicly accessible and not blocked by a firewall.

6.4 Middleware Issues

T18. Rate limiter not working across multiple Traefik instances

Symptom: Rate limits are per-instance, not global.

Fix: Configure the Redis backend for the rate limiter. Without Redis, each Traefik instance maintains its own counter independently. See Section 5.1.1 for Redis configuration.

T19. BasicAuth prompts repeatedly

Symptom: Browser asks for credentials on every request.

Fixes:

  • Ensure the password hash format matches what htpasswd produces.
  • In Docker labels, escape $ signs by doubling them: $$. A single $ is interpreted as a variable by Docker Compose.
  • Verify removeHeader is set correctly — if true, the Authorization header won't reach the backend, which is usually desired.
  • Check that the realm value is consistent across requests.

T20. ForwardAuth returns 401 but auth service works directly

Symptom: Auth service responds 200 when called directly but ForwardAuth fails.

Fixes:

  • Check that trustForwardHeader is set appropriately for your proxy chain.
  • Verify the authRequestHeaders includes all headers the auth service needs (e.g., Cookie, Authorization).
  • Check network connectivity between Traefik and the auth service container.
  • Inspect what headers Traefik actually sends to the auth service using DEBUG logging.

T21. Compress middleware not compressing

Symptom: Responses are not compressed despite the middleware being active.

Fixes:

  • Response body must be at least 1024 bytes (default minResponseBodyBytes).
  • The client must send an Accept-Encoding header with a supported encoding.
  • Check excludedContentTypes — some content types may be excluded.
  • Streaming responses (text/event-stream) are typically excluded.
  • Verify the middleware is actually attached to the router (check dashboard).

T22. Circuit breaker trips unexpectedly

Symptom: Service returns 503 even though backends seem healthy.

Fixes:

  • Review the expression. ResponseCodeRatio(500, 600, 0, 600) > 0.30 means the circuit trips when more than 30% of responses are 5xx.
  • The checkPeriod (default 100ms) determines how often the expression is evaluated. During short bursts of errors, a quick check period can trip the breaker prematurely.
  • After tripping, the breaker stays open for fallbackDuration (default 10s) before entering half-open state.

6.5 Backend and Service Issues

T23. Health check marks all servers unhealthy

Symptom: Dashboard shows all servers as unhealthy, traffic returns 503.

Fixes:

  • Verify the health check path returns a 200 status on the backend directly.
  • Check that the health check port matches the backend's actual port.
  • If using mode: grpc, ensure the backend implements the gRPC health checking protocol.
  • Increase the timeout if backends are slow to respond (default is 5s).
  • Verify network connectivity between Traefik and the backend on the health check port.

T24. Sticky sessions not working

Symptom: Requests land on different backends despite sticky cookie configuration.

Fixes:

  • Check that the sticky cookie is being set (inspect response Set-Cookie headers).
  • Ensure secure: true is not set when testing over plain HTTP — the browser will not send the cookie.
  • Verify sameSite setting is compatible with your client's origin.
  • Check that the cookie path matches the request path.

T25. Response body corruption with mirroring + retry

Symptom: Intermittent garbled or incomplete response bodies when using mirroring and retry middlewares together.

This is a known data race issue (GitHub issue #12988). Avoid combining mirroring with retry on the same router until a fix is released.

Warning

Using the retry middleware together with traffic mirroring can cause response body corruption due to a data race (GitHub issue #12988). If you need both features, apply them on separate routers or wait for a fix.

T26. ServiceName shows RouterName in access logs

Symptom: The ServiceName field in access logs or metrics incorrectly reports the router name instead of the actual service name.

This is a known issue (GitHub issue #12951) affecting IngressRoute resources. The service name in logs may show the router name. This is cosmetic and does not affect routing behavior.

6.6 Kubernetes-Specific Issues

T27. Traefik stops serving ingresses when kube-apiserver is down

Symptom: Existing ingress-nginx routes stop working when the Kubernetes API server becomes unreachable.

This is a known issue (GitHub issue #12945). When the kube-apiserver goes down, Traefik may lose its cached configuration. Consider running with a file provider as a fallback for critical routes.

T28. Unnecessary BackendTLSPolicy status updates

Symptom: Kubernetes logs show repeated "object has been modified" errors related to BackendTLSPolicy.

This is a known issue (GitHub issue #12511) where Traefik unnecessarily updates BackendTLSPolicy status, causing conflict errors with the API server. This is cosmetic but generates noisy logs.

T29. Nginx Session Affinity annotations not working

Symptom: Session affinity annotations from ingress-nginx are ignored.

This is a known issue (GitHub issue #12998). Traefik does not recognize nginx-specific annotations for session affinity. Use Traefik-native sticky session configuration instead (see Section 4.6).

6.7 Performance and Operational Issues

T30. High memory usage

Symptom: Traefik process consumes excessive memory.

Fixes:

  • Reduce the number of watched containers/services if possible.
  • Disable providers you don't use.
  • If using file provider with watch: true on a large directory, reduce the number of files.
  • Check for a large number of certificates in acme.json.
  • Review maxIdleConnsPerHost in ServersTransport — high values consume more memory.

T31. Slow startup

Symptom: Traefik takes a long time to become ready.

Fixes:

  • Large numbers of Docker containers or Kubernetes resources slow initial discovery.
  • Plugin loading can delay startup. Set abortOnPluginFailure: true to fail fast if a plugin cannot load.
  • Version checking runs 10 minutes after startup and does not affect startup time, but can generate early network traffic.
  • For Kubernetes, check API server connectivity — a slow or unreachable API server delays provider initialization.

T32. Graceful shutdown timeout

Symptom: Traefik does not shut down cleanly on SIGTERM.

Traefik has a hard 10-second timeout for graceful shutdown. After 10 seconds, if connections have not drained, the process terminates forcefully. The ping endpoint returns the terminatingStatusCode (default 503) during shutdown to signal upstream load balancers. Plan for long-lived connections (WebSockets, SSE) accordingly.


7. FAQ

General

Q1. What is the difference between static and dynamic configuration?

Static configuration is loaded once at startup and cannot change without a restart. It defines entrypoints (ports), providers (where to find services), certificate resolvers, and global settings. Dynamic configuration is loaded continuously from providers and defines routers, services, middlewares, and TLS options. Dynamic configuration updates without restarting Traefik.

Q2. What Go version does Traefik v3 require?

Traefik v3 is built with Go 1.25.0. You only need Go installed if building from source. Docker images and binary releases include everything needed.

Q3. Does Traefik support HTTP/2?

Yes. Traefik supports HTTP/2 for both frontend (client-to-Traefik) and backend (Traefik-to-service) connections. HTTP/2 is enabled by default for HTTPS connections. To force HTTP/1.1 to backends, set disableHTTP2: true in the ServersTransport.

Q4. Does Traefik support HTTP/3 (QUIC)?

HTTP/3 support is available as an experimental feature in Traefik v3.

Q5. Can I use both TOML and YAML for configuration?

You can use either for static configuration (pick one file). For dynamic configuration via the file provider, files can be TOML or YAML independently. You cannot mix formats within a single file.

Q6. How do I check which version I am running?

traefik version

Or check the dashboard header, which displays the version. The /api/version endpoint also returns version information.

Q7. Does Traefik phone home?

By default, Traefik checks for new versions every 24 hours (first check 10 minutes after startup) and can send anonymous usage statistics. Disable both:

global:
  checkNewVersion: false
  sendAnonymousUsage: false

Q8. What happens if Traefik crashes mid-request?

Traefik handles SIGINT and SIGTERM for graceful shutdown with a 10-second drain period. If the process crashes (not a signal), in-flight connections are dropped. Use a process manager (systemd, Docker restart policies) to automatically restart Traefik.

Docker

Q9. Why do I need to mount the Docker socket?

Traefik needs to read container metadata (labels, ports, networks) from the Docker daemon. The socket (/var/run/docker.sock) is the standard way to communicate with Docker. Always mount it read-only (:ro) — Traefik only reads, never writes.

Q10. What does exposedByDefault: false do?

When set to false, Traefik only considers containers that have the traefik.enable=true label. When true (the default), every running container is automatically exposed. Always set this to false in production.

Q11. How do I specify which port to use when a container exposes multiple ports?

Use the label:

traefik.http.services.myapp.loadbalancer.server.port=8080

Q12. How do I route to a container on a different Docker network?

You cannot. Traefik and the target container must share at least one Docker network. Add the label traefik.docker.network=my-network to tell Traefik which network to use when the container is on multiple networks.

Q13. Do I need Docker Swarm mode?

No. Traefik works with standalone Docker (using the Docker provider) and Docker Swarm (using the Swarm provider). Choose the provider that matches your setup.

Q14. How do I escape dollar signs in Docker Compose labels?

Double them: $$. Docker Compose interprets a single $ as a variable reference. This is especially important for password hashes in BasicAuth labels.

Certificates and TLS

Q15. Which ACME challenge should I use?

Challenge When to Use
HTTP Port 80 is publicly accessible. Simplest setup.
TLS-ALPN Port 80 is blocked, only port 443 is available.
DNS Need wildcard certificates, or no public ports available.

Q16. Can I use my own certificates instead of ACME?

Yes. Provide certificates via dynamic configuration:

tls:
  certificates:
    - certFile: "/certs/example.com.crt"
      keyFile: "/certs/example.com.key"

Q17. How do I get wildcard certificates?

Only the DNS challenge supports wildcard certificates. Configure the DNS challenge with your DNS provider's credentials.

Q18. What TLS versions does Traefik support?

TLS 1.0 through TLS 1.3. The recommended minimum is TLS 1.2:

tls:
  options:
    default:
      minVersion: "VersionTLS12"

Q19. What is X25519MLKEM768?

A post-quantum hybrid key exchange mechanism combining X25519 with ML-KEM-768. Traefik supports it as a curve preference for forward-looking protection against quantum computing threats.

Q20. Does Traefik support mTLS (mutual TLS)?

Yes. Configure client certificates in the ServersTransport for backend mTLS, and use TLS options with clientAuth for frontend mTLS. Client certificate information can be forwarded to backends via the PassTLSClientCert middleware.

Q21. Does Traefik handle OCSP stapling?

Yes, automatically. When certificates include OCSP responder URLs, Traefik fetches, caches, and staples OCSP responses during TLS handshakes. No configuration is needed.

Load Balancing

Q22. What is the default load balancing strategy?

Weighted Round Robin (wrr) is the default for ServersLoadBalancer services.

Q23. How do I do canary deployments?

Use a weighted service to split traffic between the current and canary versions:

http:
  services:
    canary:
      weighted:
        services:
          - name: "v1"
            weight: 90
          - name: "v2"
            weight: 10

Adjust weights as confidence grows.

Q24. What happens when a backend fails a health check?

The server is removed from the load balancer pool. Requests are routed only to healthy servers. When the server passes health checks again, it is re-added automatically.

Q25. Can I have active AND passive health checks?

Yes. Configure both on the same service. Active health checks probe periodically while passive health checks monitor real traffic for failures. They complement each other.

Middlewares

Q26. In what order are middlewares applied?

Middlewares are applied in the order they are listed in the router's middlewares field. The first middleware in the list processes the request first.

Q27. Can I share middlewares across providers?

Yes. Reference middlewares from other providers using the @provider suffix: my-middleware@file, my-middleware@docker, my-middleware@kubernetes.

Q28. What is the default rate limit burst?

The default burst is 1. This means if average is 10 and period is 1s, the client can make 10 requests per second with no bursting. Set burst higher to allow short traffic spikes above the average.

Q29. How do I configure CORS?

Use the Headers middleware with the accessControl* fields:

http:
  middlewares:
    cors:
      headers:
        accessControlAllowOriginList:
          - "https://app.example.com"
        accessControlAllowMethods:
          - "GET"
          - "POST"
          - "OPTIONS"
        accessControlAllowHeaders:
          - "Content-Type"
          - "Authorization"
        accessControlMaxAge: 86400
        addVaryHeader: true

Q30. Is ipWhiteList still supported?

It still works but is deprecated. Migrate to ipAllowList, which has identical functionality with a corrected name.

Q31. What status code does IPAllowList return for blocked requests?

The default rejectStatusCode is 403 Forbidden. You can customize it in the middleware configuration.

Operations

Q32. How do I run Traefik as a systemd service?

Create a unit file with Type=notify (Traefik sends sd_notify readiness signals) and set WatchdogSec for automatic restart on hang. See Section 5.7 for a complete example.

Q33. How do I monitor Traefik?

Enable one of the metrics backends (Prometheus, Datadog, StatsD, InfluxDB2, or OTLP). Prometheus is the most common: set metrics.prometheus: {} and scrape the /metrics endpoint on the Traefik entrypoint (default port 8080).

Q34. What should I back up?

Back up these files:

  • traefik.yml / traefik.toml (static configuration)
  • Dynamic configuration files (if using file provider)
  • acme.json (ACME certificates and private keys — critical)
  • Any custom TLS certificates and keys

Q35. Can I reload configuration without downtime?

Dynamic configuration (routers, services, middlewares) reloads automatically with zero downtime. Static configuration (entrypoints, providers) requires a restart. Architect your setup so that changes you make frequently are in dynamic configuration.

Q36. What signals does Traefik handle?

Traefik handles SIGINT and SIGTERM for graceful shutdown. It drains connections for up to 10 seconds before forced termination.

Q37. How do I debug routing issues?

  1. Set log.level: DEBUG for verbose logging.
  2. Check the dashboard for router/service status and health.
  3. Use curl -v to inspect headers and status codes.
  4. Verify that Host headers match router rules.
  5. Check network connectivity between Traefik and backends.
  6. Verify entrypoint port mapping in Docker Compose.

Q38. How do I handle log rotation?

When writing logs to a file, configure the built-in lumberjack-based rotation:

log:
  filePath: "/var/log/traefik/traefik.log"
  maxSize: 100       # MB before rotation
  maxBackups: 5      # rotated files to keep
  maxAge: 30         # days to retain
  compress: true     # gzip rotated files

Plugins

Q39. Where are plugins stored?

Remote plugins are downloaded to ./plugins-storage/ relative to Traefik's working directory. Local plugins are loaded from ./plugins-local/.

Q40. What happens if a plugin fails to load?

By default, Traefik starts anyway and the plugin is simply unavailable. Set experimental.abortOnPluginFailure: true to make Traefik refuse to start if any plugin fails to load. This is recommended for production when plugins are critical to your routing logic.

Architecture

Q41. What providers does Traefik support?

Docker, Docker Swarm, Kubernetes (CRD, Ingress, Gateway API, Knative), Consul Catalog, Nomad, ECS, File, HTTP, REST, KV stores (etcd, Redis, Consul KV, ZooKeeper), Tailscale, and plugin-based custom providers.

Q42. Can I use multiple providers simultaneously?

Yes. Enable multiple providers in static configuration. Traefik merges their dynamic configurations. Use the @provider suffix to reference resources from specific providers (e.g., my-service@docker, my-middleware@file).

Q43. What service types does Traefik support?

LoadBalancer (standard reverse proxy), WeightedRoundRobin (traffic splitting), HighestRandomWeight (consistent hashing), Mirroring (shadow traffic), and Failover (primary/backup).

Q44. How does Traefik handle graceful shutdown?

On receiving SIGINT or SIGTERM:

  1. Stops accepting new connections.
  2. Returns terminatingStatusCode (default 503) on the /ping endpoint.
  3. Drains existing connections for up to 10 seconds.
  4. Force-terminates if connections remain after 10 seconds.

Q45. Where can I find the API documentation?

The API is exposed on the Traefik entrypoint (default port 8080). The dashboard at that port provides a visual interface. See the Admin Guide for the full API endpoint reference.