traefik

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Traefik v3 Core Knowledge

Traefik v3 核心知识

Core Concepts

核心概念

┌─────────────────────────────────────────────────────────────────┐
│  PROVIDERS          ENTRYPOINTS        ROUTERS         SERVICES │
│  ──────────         ───────────        ───────         ──────── │
│  Docker labels  →   :80  (web)    →   Host rule   →   LB pool  │
│  File provider  →   :443 (websecure)  PathPrefix       backend  │
│  Kubernetes     →   :8080 (dashboard) Headers          servers  │
└─────────────────────────────────────────────────────────────────┘
  • Provider: Where Traefik reads configuration (Docker, file, Kubernetes, Consul…)
  • Entrypoint: Network port + protocol that Traefik listens on
  • Router: Matches requests by rule (Host, PathPrefix, Header) → sends to a service
  • Middleware: Transforms requests/responses between router and service
  • Service: The upstream backend (load balancer with one or more servers)

┌─────────────────────────────────────────────────────────────────┐
│  PROVIDERS          ENTRYPOINTS        ROUTERS         SERVICES │
│  ──────────         ───────────        ───────         ──────── │
│  Docker labels  →   :80  (web)    →   Host rule   →   LB pool  │
│  File provider  →   :443 (websecure)  PathPrefix       backend  │
│  Kubernetes     →   :8080 (dashboard) Headers          servers  │
└─────────────────────────────────────────────────────────────────┘
  • Provider:Traefik读取配置的来源(Docker、文件、Kubernetes、Consul等)
  • Entrypoint:Traefik监听的网络端口+协议
  • Router:通过规则(Host、PathPrefix、Header)匹配请求,将其转发至Service
  • Middleware:在Router和Service之间转换请求/响应
  • Service:上游后端服务(包含一个或多个服务器的负载均衡器)

Static Config —
traefik.yml

静态配置 —
traefik.yml

Static config defines infrastructure-level settings. Requires restart to change.
yaml
undefined
静态配置定义基础设施级别的设置,修改后需要重启Traefik才能生效。
yaml
undefined

/etc/traefik/traefik.yml (or mounted at /traefik.yml in Docker)

/etc/traefik/traefik.yml (或在Docker中挂载为 /traefik.yml)

Global settings

全局设置

global: checkNewVersion: false sendAnonymousUsage: false
global: checkNewVersion: false sendAnonymousUsage: false

API & dashboard

API与仪表盘

api: dashboard: true insecure: false # NEVER true in production
api: dashboard: true insecure: false # 生产环境绝不能设为true

Entrypoints

入口点

entryPoints: web: address: ":80" http: redirections: entryPoint: to: websecure scheme: https permanent: true websecure: address: ":443" http: tls: certResolver: letsencrypt middlewares: - security-headers@file # Apply to all HTTPS routes metrics: address: ":8082"
entryPoints: web: address: ":80" http: redirections: entryPoint: to: websecure scheme: https permanent: true websecure: address: ":443" http: tls: certResolver: letsencrypt middlewares: - security-headers@file # 应用于所有HTTPS路由 metrics: address: ":8082"

Certificate resolvers

证书解析器

certificatesResolvers: letsencrypt: acme: email: devops@company.com storage: /letsencrypt/acme.json # Persistent volume required # HTTP challenge (default) — requires port 80 open httpChallenge: entryPoint: web
letsencrypt-dns: acme: email: devops@company.com storage: /letsencrypt/acme-dns.json dnsChallenge: provider: cloudflare # Set CF_DNS_API_TOKEN env var delayBeforeCheck: 30 # Wait for DNS propagation
certificatesResolvers: letsencrypt: acme: email: devops@company.com storage: /letsencrypt/acme.json # 需要持久化卷 # HTTP挑战(默认)—— 需要开放80端口 httpChallenge: entryPoint: web
letsencrypt-dns: acme: email: devops@company.com storage: /letsencrypt/acme-dns.json dnsChallenge: provider: cloudflare # 设置CF_DNS_API_TOKEN环境变量 delayBeforeCheck: 30 # 等待DNS生效

Providers

配置提供者

providers: docker: endpoint: "unix:///var/run/docker.sock" exposedByDefault: false # IMPORTANT: require explicit opt-in network: traefik-public # Default network for container comms file: directory: /etc/traefik/dynamic/ # Watch for changes automatically watch: true
providers: docker: endpoint: "unix:///var/run/docker.sock" exposedByDefault: false # 重要:要求显式启用路由 network: traefik-public # 容器通信的默认网络 file: directory: /etc/traefik/dynamic/ # 自动监听配置变更 watch: true

Logging

日志

log: level: INFO # DEBUG | INFO | WARN | ERROR filePath: /var/log/traefik/traefik.log
log: level: INFO # DEBUG | INFO | WARN | ERROR filePath: /var/log/traefik/traefik.log

Access logs

访问日志

accessLog: filePath: /var/log/traefik/access.log bufferingSize: 100 fields: headers: defaultMode: drop names: User-Agent: keep X-Forwarded-For: keep
accessLog: filePath: /var/log/traefik/access.log bufferingSize: 100 fields: headers: defaultMode: drop names: User-Agent: keep X-Forwarded-For: keep

Metrics

指标

metrics: prometheus: entryPoint: metrics addServicesLabels: true addRoutersLabels: true

---
metrics: prometheus: entryPoint: metrics addServicesLabels: true addRoutersLabels: true

---

Docker Compose — Full Example

Docker Compose 完整示例

yaml
undefined
yaml
undefined

docker-compose.yml

docker-compose.yml

version: "3.9"
services: traefik: image: traefik:v3.3 container_name: traefik restart: unless-stopped security_opt: - no-new-privileges:true ports: - "80:80" - "443:443" volumes: - /etc/traefik/traefik.yml:/traefik.yml:ro - /etc/traefik/dynamic/:/etc/traefik/dynamic/:ro - /var/run/docker.sock:/var/run/docker.sock:ro - traefik-letsencrypt:/letsencrypt environment: - CF_DNS_API_TOKEN=${CF_DNS_API_TOKEN} networks: - traefik-public labels: - "traefik.enable=true" # Dashboard router - "traefik.http.routers.dashboard.rule=Host(
traefik.example.com
)" - "traefik.http.routers.dashboard.entrypoints=websecure" - "traefik.http.routers.dashboard.tls.certresolver=letsencrypt" - "traefik.http.routers.dashboard.service=api@internal" - "traefik.http.routers.dashboard.middlewares=dashboard-auth" # Dashboard basic auth: echo $(htpasswd -nbB admin 'password') | sed -e s/\$/\$\$/g - "traefik.http.middlewares.dashboard-auth.basicauth.users=admin:$$2y$$10$$hash..."

Example application

api: image: myapp/api:1.4.2 restart: unless-stopped networks: - traefik-public - internal environment: - DATABASE_URL=postgres://user:pass@db:5432/app labels: - "traefik.enable=true" - "traefik.http.routers.api.rule=Host(
api.example.com
)" - "traefik.http.routers.api.entrypoints=websecure" - "traefik.http.routers.api.tls.certresolver=letsencrypt" - "traefik.http.routers.api.middlewares=rate-limit,security-headers" # Service port (required when container exposes multiple ports) - "traefik.http.services.api.loadbalancer.server.port=3000" # Health check - "traefik.http.services.api.loadbalancer.healthcheck.path=/health" - "traefik.http.services.api.loadbalancer.healthcheck.interval=10s" - "traefik.http.services.api.loadbalancer.healthcheck.timeout=3s"

Frontend app with path-based routing

frontend: image: myapp/frontend:2.1.0 restart: unless-stopped networks: - traefik-public labels: - "traefik.enable=true" - "traefik.http.routers.frontend.rule=Host(
example.com
) || Host(
www.example.com
)" - "traefik.http.routers.frontend.entrypoints=websecure" - "traefik.http.routers.frontend.tls.certresolver=letsencrypt" - "traefik.http.routers.frontend.middlewares=www-redirect,security-headers" - "traefik.http.services.frontend.loadbalancer.server.port=80"
db: image: postgres:16-alpine restart: unless-stopped networks: - internal # Not on traefik-public — no external routing volumes: - postgres-data:/var/lib/postgresql/data environment: - POSTGRES_PASSWORD=${DB_PASSWORD}
networks: traefik-public: external: true # Pre-created: docker network create traefik-public internal: driver: bridge
volumes: traefik-letsencrypt: postgres-data:

---
version: "3.9"
services: traefik: image: traefik:v3.3 container_name: traefik restart: unless-stopped security_opt: - no-new-privileges:true ports: - "80:80" - "443:443" volumes: - /etc/traefik/traefik.yml:/traefik.yml:ro - /etc/traefik/dynamic/:/etc/traefik/dynamic/:ro - /var/run/docker.sock:/var/run/docker.sock:ro - traefik-letsencrypt:/letsencrypt environment: - CF_DNS_API_TOKEN=${CF_DNS_API_TOKEN} networks: - traefik-public labels: - "traefik.enable=true" # 仪表盘路由 - "traefik.http.routers.dashboard.rule=Host(
traefik.example.com
)" - "traefik.http.routers.dashboard.entrypoints=websecure" - "traefik.http.routers.dashboard.tls.certresolver=letsencrypt" - "traefik.http.routers.dashboard.service=api@internal" - "traefik.http.routers.dashboard.middlewares=dashboard-auth" # 仪表盘基础认证:执行 echo $(htpasswd -nbB admin 'password') | sed -e s/\$/\$\$/g 生成 - "traefik.http.middlewares.dashboard-auth.basicauth.users=admin:$$2y$$10$$hash..."

示例应用

api: image: myapp/api:1.4.2 restart: unless-stopped networks: - traefik-public - internal environment: - DATABASE_URL=postgres://user:pass@db:5432/app labels: - "traefik.enable=true" - "traefik.http.routers.api.rule=Host(
api.example.com
)" - "traefik.http.routers.api.entrypoints=websecure" - "traefik.http.routers.api.tls.certresolver=letsencrypt" - "traefik.http.routers.api.middlewares=rate-limit,security-headers" # 服务端口(容器暴露多个端口时必须指定) - "traefik.http.services.api.loadbalancer.server.port=3000" # 健康检查 - "traefik.http.services.api.loadbalancer.healthcheck.path=/health" - "traefik.http.services.api.loadbalancer.healthcheck.interval=10s" - "traefik.http.services.api.loadbalancer.healthcheck.timeout=3s"

基于路径路由的前端应用

frontend: image: myapp/frontend:2.1.0 restart: unless-stopped networks: - traefik-public labels: - "traefik.enable=true" - "traefik.http.routers.frontend.rule=Host(
example.com
) || Host(
www.example.com
)" - "traefik.http.routers.frontend.entrypoints=websecure" - "traefik.http.routers.frontend.tls.certresolver=letsencrypt" - "traefik.http.routers.frontend.middlewares=www-redirect,security-headers" - "traefik.http.services.frontend.loadbalancer.server.port=80"
db: image: postgres:16-alpine restart: unless-stopped networks: - internal # 不在traefik-public网络,无外部路由 volumes: - postgres-data:/var/lib/postgresql/data environment: - POSTGRES_PASSWORD=${DB_PASSWORD}
networks: traefik-public: external: true # 需预先创建:docker network create traefik-public internal: driver: bridge
volumes: traefik-letsencrypt: postgres-data:

---

Dynamic Config — File Provider

动态配置 — 文件提供者

yaml
undefined
yaml
undefined

/etc/traefik/dynamic/middlewares.yml

/etc/traefik/dynamic/middlewares.yml

http: middlewares: # HTTP → HTTPS redirect (also configured at entrypoint level above) redirect-to-https: redirectScheme: scheme: https permanent: true
# HSTS + security headers
security-headers:
  headers:
    stsSeconds: 31536000
    stsIncludeSubdomains: true
    stsPreload: true
    forceSTSHeader: true
    contentTypeNosniff: true
    browserXssFilter: true
    referrerPolicy: "strict-origin-when-cross-origin"
    frameDeny: true
    customResponseHeaders:
      X-Powered-By: ""
      Server: ""

# Rate limiting
rate-limit:
  rateLimit:
    average: 100          # Requests per second (average)
    burst: 50             # Burst allowance
    period: 1m            # Window period

# Strip /api prefix before forwarding
strip-api-prefix:
  stripPrefix:
    prefixes:
      - "/api"

# Add /v1 prefix
add-v1-prefix:
  addPrefix:
    prefix: "/v1"

# Basic auth
internal-auth:
  basicAuth:
    usersFile: /etc/traefik/users.htpasswd
    removeHeader: true    # Strip Authorization before passing to upstream

# IP whitelist (Traefik v3: use ipAllowList)
office-only:
  ipAllowList:
    sourceRange:
      - "10.0.0.0/8"
      - "203.0.113.42/32"

# Retry on failure
retry-middleware:
  retry:
    attempts: 3
    initialInterval: 100ms

# www redirect
www-redirect:
  redirectRegex:
    regex: "^https?://www\\.example\\.com/(.*)"
    replacement: "https://example.com/${1}"
    permanent: true

# Circuit breaker
circuit-breaker:
  circuitBreaker:
    expression: "ResponseCodeRatio(500, 600, 0, 600) > 0.25 || NetworkErrorRatio() > 0.10"

```yaml
http: middlewares: # HTTP → HTTPS 重定向(也可在入口点级别配置) redirect-to-https: redirectScheme: scheme: https permanent: true
# HSTS + 安全头
security-headers:
  headers:
    stsSeconds: 31536000
    stsIncludeSubdomains: true
    stsPreload: true
    forceSTSHeader: true
    contentTypeNosniff: true
    browserXssFilter: true
    referrerPolicy: "strict-origin-when-cross-origin"
    frameDeny: true
    customResponseHeaders:
      X-Powered-By: ""
      Server: ""

# 速率限制
rate-limit:
  rateLimit:
    average: 100          # 每秒请求数(平均值)
    burst: 50             # 突发请求允许量
    period: 1m            # 统计窗口周期

# 转发前移除/api前缀
strip-api-prefix:
  stripPrefix:
    prefixes:
      - "/api"

# 添加/v1前缀
add-v1-prefix:
  addPrefix:
    prefix: "/v1"

# 基础认证
internal-auth:
  basicAuth:
    usersFile: /etc/traefik/users.htpasswd
    removeHeader: true    # 转发给上游服务前移除Authorization头

# IP白名单(Traefik v3:使用ipAllowList)
office-only:
  ipAllowList:
    sourceRange:
      - "10.0.0.0/8"
      - "203.0.113.42/32"

# 失败重试
retry-middleware:
  retry:
    attempts: 3
    initialInterval: 100ms

# www重定向
www-redirect:
  redirectRegex:
    regex: "^https?://www\\.example\\.com/(.*)"
    replacement: "https://example.com/${1}"
    permanent: true

# 熔断器
circuit-breaker:
  circuitBreaker:
    expression: "ResponseCodeRatio(500, 600, 0, 600) > 0.25 || NetworkErrorRatio() > 0.10"

```yaml

/etc/traefik/dynamic/services.yml — external services (not in Docker)

/etc/traefik/dynamic/services.yml — 外部服务(不在Docker中)

http: services: legacy-monolith: loadBalancer: servers: - url: "http://192.168.1.10:8080" - url: "http://192.168.1.11:8080" healthCheck: path: /status interval: 15s timeout: 5s sticky: cookie: name: SERVERID secure: true httpOnly: true
routers: legacy: rule: "Host(
legacy.example.com
)" entryPoints: - websecure tls: certResolver: letsencrypt service: legacy-monolith middlewares: - security-headers - rate-limit

---
http: services: legacy-monolith: loadBalancer: servers: - url: "http://192.168.1.10:8080" - url: "http://192.168.1.11:8080" healthCheck: path: /status interval: 15s timeout: 5s sticky: cookie: name: SERVERID secure: true httpOnly: true
routers: legacy: rule: "Host(
legacy.example.com
)" entryPoints: - websecure tls: certResolver: letsencrypt service: legacy-monolith middlewares: - security-headers - rate-limit

---

Middleware Composition Example

中间件组合示例

Middlewares are applied in the order they are listed on the router label.
Request → rate-limit → ip-allowlist → strip-api-prefix → upstream
Response ← security-headers (applied on response) ←──────────────
yaml
undefined
中间件按照路由标签中的列出顺序生效。
请求 → rate-limit → ip-allowlist → strip-api-prefix → 上游服务
响应 ← security-headers(在响应阶段生效) ←──────────────
yaml
undefined

Labels on a service container

服务容器上的标签

  • "traefik.http.routers.myapp.middlewares=rate-limit@file,office-only@file,strip-api-prefix@file,security-headers@file"

The `@file` suffix means the middleware is defined in the file provider.
Use `@docker` for middlewares defined via labels on another container.

---
  • "traefik.http.routers.myapp.middlewares=rate-limit@file,office-only@file,strip-api-prefix@file,security-headers@file"

`@file`后缀表示中间件定义在文件提供者中。如果中间件通过其他容器的标签定义,使用`@docker`后缀。

---

TCP Routing (non-HTTP)

TCP路由(非HTTP)

yaml
undefined
yaml
undefined

/etc/traefik/dynamic/tcp.yml

/etc/traefik/dynamic/tcp.yml

tcp: routers: postgres: rule: "HostSNI(
*
)" # TCP passthrough (no TLS inspection) entryPoints: - postgres # entryPoint address: ":5432" service: postgres-backend
services: postgres-backend: loadBalancer: servers: - address: "10.0.1.5:5432"

---
tcp: routers: postgres: rule: "HostSNI(
*
)" # TCP透传(不做TLS检查) entryPoints: - postgres # 入口点地址:":5432" service: postgres-backend
services: postgres-backend: loadBalancer: servers: - address: "10.0.1.5:5432"

---

Anti-Patterns

反模式

Anti-PatternProblemSolution
api.insecure: true
in production
Dashboard exposed on port 8080 with no authSet
api.insecure: false
; expose dashboard via router with
basicAuth
middleware
exposedByDefault: true
in Docker provider
Every container automatically gets a route, including databasesAlways set
exposedByDefault: false
; use
traefik.enable=true
label only where needed
Wildcard cert with HTTP challengeHTTP challenge cannot prove DNS control for wildcardsUse
dnsChallenge
provider for wildcard domains
Not pinning Traefik version
traefik:latest
can introduce breaking changes
Pin to exact version:
traefik:v3.3.4
Missing
traefik.http.services.<name>.loadbalancer.server.port
when container exposes multiple ports
Traefik picks wrong port arbitrarilyAlways specify the service port label explicitly
Storing
acme.json
on a non-persistent volume
Certificates lost on container restart → rate limit hitMount
acme.json
on a named Docker volume or bind mount on the host
Applying heavy middlewares (auth, rate-limit) only on some routesInconsistent security postureApply security-headers globally via entrypoint middleware; add auth/rate-limit to sensitive routers
Router rules without priority on overlapping pathsNon-deterministic routing when multiple rules matchAdd
priority
label:
traefik.http.routers.myapp.priority=10
(higher wins)
Docker socket mounted as read-write with no protectionContainer escape via Docker APIUse socket proxy (e.g.,
tecnativa/docker-socket-proxy
) to restrict API access
No health checks on servicesTraefik routes to unhealthy containersConfigure
healthcheck.path
,
interval
, and
timeout
on the loadbalancer

反模式问题解决方案
生产环境中设置
api.insecure: true
仪表盘在8080端口暴露且无认证保护设置
api.insecure: false
;通过路由暴露仪表盘并搭配
basicAuth
中间件
Docker提供者中设置
exposedByDefault: true
所有容器自动获得路由,包括数据库始终设置
exposedByDefault: false
;仅在需要的容器上添加
traefik.enable=true
标签
通配符证书使用HTTP挑战HTTP挑战无法证明通配符域名的DNS控制权对通配符域名使用
dnsChallenge
提供者
不固定Traefik版本
traefik:latest
可能引入破坏性变更
固定到具体版本:
traefik:v3.3.4
容器暴露多个端口时缺少
traefik.http.services.<name>.loadbalancer.server.port
标签
Traefik会随机选择错误的端口始终显式指定服务端口标签
acme.json
存储在非持久化卷上
容器重启后证书丢失,触发Let's Encrypt速率限制
acme.json
挂载到Docker命名卷或主机绑定挂载
仅在部分路由上应用重量级中间件(认证、速率限制)安全姿态不一致通过入口点中间件全局应用安全头;在敏感路由上添加认证/速率限制中间件
路径重叠的路由规则未设置优先级多个规则匹配时路由行为不确定添加
priority
标签:
traefik.http.routers.myapp.priority=10
(值越高优先级越高)
Docker套接字以读写权限挂载且无保护可通过Docker API逃逸容器使用套接字代理(如
tecnativa/docker-socket-proxy
)限制API访问
服务未配置健康检查Traefik会将请求路由到不健康的容器在负载均衡器上配置
healthcheck.path
interval
timeout

Troubleshooting

生产环境检查清单

SymptomLikely CauseFix
Router not matching — 404 from TraefikLabel typo, wrong entrypoint name, container not on Traefik networkCheck
traefik.http.routers.<name>.rule
label; verify container is on
traefik-public
network; check Traefik dashboard
Certificate not issuedPort 80 blocked (HTTP challenge) or DNS credentials wrong (DNS challenge)
curl http://example.com/.well-known/acme-challenge/test
from external; check
CF_DNS_API_TOKEN
Middleware not applied
@file
vs
@docker
suffix mismatch, or middleware name typo
Use exact
name@provider
syntax; restart Traefik after file provider changes
"404 page not found" from upstream appApp routing issue, not TraefikBypass Traefik:
curl http://container_ip:port/path
directly from inside Docker network
Dashboard shows router but requests still failMiddleware blocking (auth, IP whitelist)Check access log; temporarily remove middlewares to isolate
acme.json
has wrong permissions
Let's Encrypt client refuses to read file
chmod 600 acme.json
— file must be readable only by owner
TLS certificate is self-signed (Traefik default)ACME not configured, or cert resolver name wrong on routerEnsure
tls.certresolver=<name>
label matches exactly the
certificatesResolvers.<name>
key in static config
Container restarts but keeps same certacme.json not updatedEnsure acme.json volume is persistent; delete acme.json and restart to force re-issue (if not rate-limited)
High memory usageToo many access log entries bufferedIncrease
bufferingSize
or write to syslog; use log rotation
Sticky sessions not workingCookie not forwarded by load balancerEnsure
sticky.cookie.name
is set and that browsers allow cookies from the domain

  • 固定Traefik版本(不使用
    latest
  • Docker提供者中设置
    exposedByDefault: false
  • api.insecure: false
    — 仪表盘通过路由+basicAuth保护
  • web入口点配置HTTP→HTTPS重定向
  • acme.json
    存储在持久化命名卷上,权限设为
    600
  • 全局或在所有路由上应用安全头中间件
  • 面向公众的路由配置速率限制中间件
  • 所有负载均衡器服务配置健康检查
  • 通过套接字代理暴露Docker套接字(而非直接挂载
    /var/run/docker.sock
  • 启用访问日志并配置日志轮转
  • Prometheus指标端点限制为内部网络访问
  • 所有中间件名称使用正确的
    @provider
    后缀

Production Checklist

  • Traefik version pinned (not
    latest
    )
  • exposedByDefault: false
    in Docker provider
  • api.insecure: false
    — dashboard behind router + basicAuth
  • HTTP → HTTPS redirect on web entrypoint
  • acme.json
    on persistent named volume, permissions
    600
  • Security headers middleware applied globally or on all routers
  • Rate limiting middleware on public-facing routers
  • Health checks on all load balancer services
  • Docker socket exposed via socket proxy (not raw
    /var/run/docker.sock
    )
  • Access logs enabled with rotation
  • Prometheus metrics endpoint restricted to internal network
  • All middleware names use correct
    @provider
    suffix