traefik
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseTraefik 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静态配置 — traefik.yml
traefik.ymlStatic 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
undefinedyaml
undefineddocker-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.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..."
traefik.example.comExample 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()"
- "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"
api.example.comFrontend 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() || Host()"
- "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"
example.comwww.example.comdb:
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.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..."
traefik.example.com示例应用
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()"
- "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"
api.example.com基于路径路由的前端应用
frontend:
image: myapp/frontend:2.1.0
restart: unless-stopped
networks:
- traefik-public
labels:
- "traefik.enable=true"
- "traefik.http.routers.frontend.rule=Host() || Host()"
- "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"
example.comwww.example.comdb:
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
undefinedyaml
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"
```yamlhttp:
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()"
entryPoints:
- websecure
tls:
certResolver: letsencrypt
service: legacy-monolith
middlewares:
- security-headers
- rate-limit
legacy.example.com
---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()"
entryPoints:
- websecure
tls:
certResolver: letsencrypt
service: legacy-monolith
middlewares:
- security-headers
- rate-limit
legacy.example.com
---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
undefinedLabels 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
undefinedyaml
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-Pattern | Problem | Solution |
|---|---|---|
| Dashboard exposed on port 8080 with no auth | Set |
| Every container automatically gets a route, including databases | Always set |
| Wildcard cert with HTTP challenge | HTTP challenge cannot prove DNS control for wildcards | Use |
| Not pinning Traefik version | | Pin to exact version: |
Missing | Traefik picks wrong port arbitrarily | Always specify the service port label explicitly |
Storing | Certificates lost on container restart → rate limit hit | Mount |
| Applying heavy middlewares (auth, rate-limit) only on some routes | Inconsistent security posture | Apply security-headers globally via entrypoint middleware; add auth/rate-limit to sensitive routers |
| Router rules without priority on overlapping paths | Non-deterministic routing when multiple rules match | Add |
| Docker socket mounted as read-write with no protection | Container escape via Docker API | Use socket proxy (e.g., |
| No health checks on services | Traefik routes to unhealthy containers | Configure |
| 反模式 | 问题 | 解决方案 |
|---|---|---|
生产环境中设置 | 仪表盘在8080端口暴露且无认证保护 | 设置 |
Docker提供者中设置 | 所有容器自动获得路由,包括数据库 | 始终设置 |
| 通配符证书使用HTTP挑战 | HTTP挑战无法证明通配符域名的DNS控制权 | 对通配符域名使用 |
| 不固定Traefik版本 | | 固定到具体版本: |
容器暴露多个端口时缺少 | Traefik会随机选择错误的端口 | 始终显式指定服务端口标签 |
| 容器重启后证书丢失,触发Let's Encrypt速率限制 | 将 |
| 仅在部分路由上应用重量级中间件(认证、速率限制) | 安全姿态不一致 | 通过入口点中间件全局应用安全头;在敏感路由上添加认证/速率限制中间件 |
| 路径重叠的路由规则未设置优先级 | 多个规则匹配时路由行为不确定 | 添加 |
| Docker套接字以读写权限挂载且无保护 | 可通过Docker API逃逸容器 | 使用套接字代理(如 |
| 服务未配置健康检查 | Traefik会将请求路由到不健康的容器 | 在负载均衡器上配置 |
Troubleshooting
生产环境检查清单
| Symptom | Likely Cause | Fix |
|---|---|---|
| Router not matching — 404 from Traefik | Label typo, wrong entrypoint name, container not on Traefik network | Check |
| Certificate not issued | Port 80 blocked (HTTP challenge) or DNS credentials wrong (DNS challenge) | |
| Middleware not applied | | Use exact |
| "404 page not found" from upstream app | App routing issue, not Traefik | Bypass Traefik: |
| Dashboard shows router but requests still fail | Middleware blocking (auth, IP whitelist) | Check access log; temporarily remove middlewares to isolate |
| Let's Encrypt client refuses to read file | |
| TLS certificate is self-signed (Traefik default) | ACME not configured, or cert resolver name wrong on router | Ensure |
| Container restarts but keeps same cert | acme.json not updated | Ensure acme.json volume is persistent; delete acme.json and restart to force re-issue (if not rate-limited) |
| High memory usage | Too many access log entries buffered | Increase |
| Sticky sessions not working | Cookie not forwarded by load balancer | Ensure |
- 固定Traefik版本(不使用)
latest - Docker提供者中设置
exposedByDefault: false - — 仪表盘通过路由+basicAuth保护
api.insecure: false - web入口点配置HTTP→HTTPS重定向
- 存储在持久化命名卷上,权限设为
acme.json600 - 全局或在所有路由上应用安全头中间件
- 面向公众的路由配置速率限制中间件
- 所有负载均衡器服务配置健康检查
- 通过套接字代理暴露Docker套接字(而非直接挂载)
/var/run/docker.sock - 启用访问日志并配置日志轮转
- Prometheus指标端点限制为内部网络访问
- 所有中间件名称使用正确的后缀
@provider
Production Checklist
—
- Traefik version pinned (not )
latest - in Docker provider
exposedByDefault: false - — dashboard behind router + basicAuth
api.insecure: false - HTTP → HTTPS redirect on web entrypoint
- on persistent named volume, permissions
acme.json600 - 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 suffix
@provider
—