Traefik: обратный прокси для Docker без головной боли
Настройка обратного прокси в Docker-окружении - задача, с которой рано или поздно сталкивается каждый, кто запускает больше одного сервиса на сервере. Классический подход: Nginx с ручным редактированием конфигов, перезагрузка после каждого изменения, отдельный Certbot для сертификатов. Это работает, но когда контейнеров становится десятки, а деплои происходят несколько раз в день - ручное управление конфигами превращается в источник ошибок и потерянного времени.
Traefik решает эту проблему через автообнаружение сервисов. Вместо того чтобы писать конфиг под каждый контейнер, достаточно добавить несколько лейблов в docker-compose.yml - и Traefik сам настроит маршрутизацию, получит TLS-сертификат и начнёт проксировать трафик. Остановил контейнер - маршрут исчез автоматически. Запустил новый - появился без перезагрузки прокси. Это не маркетинговый тезис, а буквальное описание того, как это работает.
В этой статье разберём Traefik v3 (актуальная версия на 2026 год) с нуля: архитектуру, установку, подключение реальных сервисов (WordPress, FastAPI, Portainer), настройку middleware, автоматические сертификаты Let's Encrypt, балансировку нагрузки и мониторинг. Плюс честное сравнение с Nginx и Caddy - со случаями, когда Traefik не лучший выбор.
Что такое Traefik и зачем он нужен
Traefik - это облачно-нативный обратный прокси и балансировщик нагрузки, написанный на Go компанией Traefik Labs. Первая версия вышла в 2016 году; актуальная стабильная версия - v3.3 (февраль 2026). Основное отличие от классических прокси - встроенный Service Discovery: Traefik умеет читать метаданные контейнеров Docker, подов Kubernetes, сервисов Consul и других оркестраторов, автоматически строить конфигурацию маршрутизации и обновлять её без перезапуска.
Ключевые возможности Traefik v3:
- Автообнаружение через провайдеры: Docker, Docker Swarm, Kubernetes (CRD и Ingress), файловый провайдер, Consul, etcd
- Автоматические TLS-сертификаты через Let's Encrypt (ACME) с поддержкой HTTP-01, DNS-01 и TLS-ALPN-01 challenge
- Встроенный dashboard с реальным состоянием маршрутов
- Middleware-пайплайн: аутентификация, rate limiting, заголовки, редиректы, circuit breaker
- Маршрутизация TCP и UDP (не только HTTP)
- Нативная поддержка HTTP/2 и HTTP/3 (QUIC)
- Метрики для Prometheus, трассировки для Jaeger/Zipkin/OTLP
- Plugin-система для расширения функциональности
Когда Traefik реально полезен: микросервисная архитектура с динамическим составом сервисов, CI/CD окружения где контейнеры поднимаются и падают автоматически, мультиарендные платформы. Когда он избыточен: один статический сайт на сервере, конфигурация которого меняется раз в месяц - там хватит Nginx за пять минут.
Как Traefik отличается от Nginx и Caddy
Все три инструмента решают задачу обратного проксирования, но с принципиально разными философиями. Nginx - статическая конфигурация, максимальная гибкость, высокая производительность. Caddy - автоматический HTTPS по умолчанию, простой синтаксис. Traefik - динамическое обнаружение, нативная интеграция с Docker и Kubernetes.
| Критерий | Traefik v3 | Nginx | Caddy v2 |
|---|---|---|---|
| Конфигурация Docker | Лейблы в docker-compose.yml, автообнаружение | Ручной nginx.conf или nginx-proxy образ | Ручной Caddyfile или API |
| Авто-TLS (Let's Encrypt) | Встроено, настраивается через лейблы | Через Certbot + cron + reload | Встроено, автоматически для любого домена |
| Перезагрузка при изменениях | Не нужна, hot reload | Нужна (nginx -s reload) | Не нужна через API |
| Производительность | Высокая, но выше latency чем Nginx | Максимальная, эталон | Сопоставима с Traefik |
| Потребление памяти | ~50-80 MB в покое | ~10-30 MB в покое | ~30-60 MB в покое |
| Kubernetes Ingress | Нативная поддержка CRD | Через nginx-ingress controller | Через Gateway API (экспериментально) |
| TCP/UDP маршрутизация | Встроена | Через stream блок | Ограничена |
| Dashboard/UI | Встроен | Нет (только /stub_status) | Нет |
| Middleware экосистема | Богатая, + плагины | Через модули (часто требует компиляции) | Встроенные директивы |
| Когда выбирать | Docker/K8s с динамическими сервисами | Высокие нагрузки, статические конфиги, legacy | Простые сценарии с авто-HTTPS без Docker |
Важный момент по производительности: в большинстве реальных сценариев разница между Traefik и Nginx незаметна до 10 000+ RPS на один инстанс. При высоких нагрузках (50 000+ RPS) Nginx действительно выигрывает за счёт event-driven архитектуры и минимальных накладных расходов.
Архитектура Traefik: EntryPoints, Routers, Services, Middleware
Traefik обрабатывает трафик через четыре концепции. Разберём их по порядку - от входящего запроса до бэкенда.
EntryPoints - точки входа: сетевые порты, на которых Traefik слушает трафик. Обычно порт 80 (HTTP) и 443 (HTTPS).
Routers - маршрутизаторы. Каждый определяет правило: какие запросы он обрабатывает. Правила пишутся на DSL: Host(`example.com`), PathPrefix(`/api`), Host(`app.com`) && PathPrefix(`/v2`).
Services - описание бэкенда: один или несколько серверов, алгоритм балансировки, параметры health check. Для Docker-контейнера Traefik создаёт Service автоматически.
Middleware - цепочка преобразований запроса/ответа между роутером и сервисом. Примеры: BasicAuth, RateLimit, Headers, RedirectScheme, StripPrefix.
Схема прохождения запроса:
Клиент
|
EntryPoint (:443, HTTPS)
|
Router (правило: Host(`app.example.com`))
|
Middleware цепочка (auth -> rate-limit -> security-headers)
|
Service (load balancer -> backend контейнеры)
|
Ваш контейнер (:8080)
Конфигурация делится на два уровня:
- Статическая - загружается при старте, не меняется на ходу. Содержит EntryPoints, провайдеры, настройки ACME. Задаётся через
traefik.yml. - Динамическая - меняется без перезапуска. Содержит Routers, Services, Middleware. Поступает от провайдеров (Docker лейблы, файловый провайдер).
Установка Traefik через Docker Compose
Минимальная рабочая конфигурация Traefik v3 для production-окружения. Предполагается домен с A-записью, указывающей на IP сервера.
mkdir -p /opt/traefik/data
cd /opt/traefik
touch data/acme.json
chmod 600 data/acme.json
Файл статической конфигурации /opt/traefik/data/traefik.yml:
api:
dashboard: true
insecure: false
log:
level: INFO
format: json
accessLog:
format: json
entryPoints:
web:
address: ":80"
http:
redirections:
entryPoint:
to: websecure
scheme: https
permanent: true
websecure:
address: ":443"
http:
tls:
certResolver: letsencrypt
http3:
advertisedPort: 443
certificatesResolvers:
letsencrypt:
acme:
email: admin@example.com
storage: /data/acme.json
httpChallenge:
entryPoint: web
providers:
docker:
endpoint: "unix:///var/run/docker.sock"
exposedByDefault: false
network: traefik-public
file:
filename: /data/dynamic.yml
watch: true
metrics:
prometheus:
addEntryPointsLabels: true
addServicesLabels: true
Файл /opt/traefik/docker-compose.yml:
services:
traefik:
image: traefik:v3.3
container_name: traefik
restart: unless-stopped
security_opt:
- no-new-privileges:true
ports:
- "80:80"
- "443:443/tcp"
- "443:443/udp"
volumes:
- /etc/localtime:/etc/localtime:ro
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./data/traefik.yml:/traefik.yml:ro
- ./data/acme.json:/data/acme.json
- ./data/dynamic.yml:/data/dynamic.yml:ro
networks:
- traefik-public
labels:
- "traefik.enable=true"
- "traefik.http.routers.traefik-dashboard.rule=Host(`traefik.example.com`)"
- "traefik.http.routers.traefik-dashboard.entrypoints=websecure"
- "traefik.http.routers.traefik-dashboard.service=api@internal"
- "traefik.http.routers.traefik-dashboard.middlewares=dashboard-auth"
- "traefik.http.middlewares.dashboard-auth.basicauth.users=admin:$$apr1$$xyz$$hashedpassword"
networks:
traefik-public:
external: true
docker network create traefik-public
docker compose up -d
docker compose logs -f traefik
Обратите внимание: exposedByDefault: false - важная настройка безопасности. Контейнер становится доступным только если у него есть лейбл traefik.enable=true.
Подключение сервисов через лейблы
Разберём три реальных примера подключения сервисов.
WordPress
services:
wordpress:
image: wordpress:6.7-php8.3-apache
restart: unless-stopped
environment:
WORDPRESS_DB_HOST: db
WORDPRESS_DB_NAME: wordpress
WORDPRESS_DB_USER: wpuser
WORDPRESS_DB_PASSWORD: secretpassword
volumes:
- wp_data:/var/www/html
networks:
- traefik-public
- internal
labels:
- "traefik.enable=true"
- "traefik.http.routers.wordpress.rule=Host(`blog.example.com`)"
- "traefik.http.routers.wordpress.entrypoints=websecure"
- "traefik.http.routers.wordpress.tls.certresolver=letsencrypt"
- "traefik.http.services.wordpress.loadbalancer.server.port=80"
- "traefik.http.routers.wordpress.middlewares=security-headers@file"
db:
image: mariadb:11.4
restart: unless-stopped
environment:
MYSQL_DATABASE: wordpress
MYSQL_USER: wpuser
MYSQL_PASSWORD: secretpassword
MYSQL_ROOT_PASSWORD: rootpassword
volumes:
- db_data:/var/lib/mysql
networks:
- internal
volumes:
wp_data:
db_data:
networks:
traefik-public:
external: true
internal:
internal: true
FastAPI приложение
services:
fastapi-app:
image: myapp:latest
restart: unless-stopped
networks:
- traefik-public
- internal
labels:
- "traefik.enable=true"
- "traefik.http.routers.fastapi.rule=Host(`api.example.com`)"
- "traefik.http.routers.fastapi.entrypoints=websecure"
- "traefik.http.routers.fastapi.tls.certresolver=letsencrypt"
- "traefik.http.services.fastapi.loadbalancer.server.port=8000"
- "traefik.http.routers.fastapi.middlewares=api-rate-limit@docker,security-headers@file"
- "traefik.http.middlewares.api-rate-limit.ratelimit.average=100"
- "traefik.http.middlewares.api-rate-limit.ratelimit.burst=200"
- "traefik.http.middlewares.api-rate-limit.ratelimit.period=1m"
Portainer
services:
portainer:
image: portainer/portainer-ce:2.21.5
restart: unless-stopped
security_opt:
- no-new-privileges:true
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- portainer_data:/data
networks:
- traefik-public
labels:
- "traefik.enable=true"
- "traefik.http.routers.portainer.rule=Host(`portainer.example.com`)"
- "traefik.http.routers.portainer.entrypoints=websecure"
- "traefik.http.routers.portainer.tls.certresolver=letsencrypt"
- "traefik.http.services.portainer.loadbalancer.server.port=9000"
volumes:
portainer_data:
networks:
traefik-public:
external: true
Важные правила при работе с лейблами:
- Имена роутеров, сервисов и middleware должны быть уникальными в пределах всего Traefik-инстанса.
- Лейбл
traefik.http.services.NAME.loadbalancer.server.portнужен, если контейнер слушает не на стандартном порту. - При нескольких сетях в контейнере укажите явно:
traefik.docker.network=traefik-public. - Символ
$в лейблах (в BasicAuth хеше) экранируется как$$в docker-compose.yml.
Middleware: аутентификация, rate limiting, заголовки безопасности
BasicAuth
apt install apache2-utils
htpasswd -nb admin mypassword
# Вывод: admin:$apr1$xyz$somehashedvalue
# Или через Docker
docker run --rm httpd:alpine htpasswd -nb admin mypassword
Определение middleware в файловом провайдере dynamic.yml:
http:
middlewares:
basic-auth:
basicAuth:
users:
- "admin:$apr1$xyz$somehashedvalue"
removeHeader: true
Rate Limiting
http:
middlewares:
rate-limit-strict:
rateLimit:
average: 50
burst: 100
period: 1m
sourceCriterion:
ipStrategy:
depth: 1
rate-limit-standard:
rateLimit:
average: 200
burst: 400
period: 1m
Заголовки безопасности
http:
middlewares:
security-headers:
headers:
stsSeconds: 31536000
stsIncludeSubdomains: true
stsPreload: true
forceSTSHeader: true
frameDeny: true
contentTypeNosniff: true
browserXssFilter: true
referrerPolicy: "strict-origin-when-cross-origin"
customResponseHeaders:
X-Powered-By: ""
Server: ""
compression:
compress:
minResponseBodyBytes: 1024
Применение нескольких middleware через запятую:
labels:
- "traefik.http.routers.myapp.middlewares=security-headers@file,rate-limit-standard@file,basic-auth@file"
Суффикс @file означает, что middleware определён в файловом провайдере. @docker - в лейблах Docker.
TLS и автоматические сертификаты Let's Encrypt
Traefik поддерживает три ACME challenge:
- HTTP-01 - самый простой. Требует открытый порт 80. Не работает для wildcard.
- TLS-ALPN-01 - проверка через порт 443. Полезно, если 80 закрыт.
- DNS-01 - единственный способ для wildcard (
*.example.com). Требует интеграцию с DNS-провайдером.
Конфигурация нескольких резолверов в traefik.yml:
certificatesResolvers:
letsencrypt:
acme:
email: admin@example.com
storage: /data/acme.json
httpChallenge:
entryPoint: web
letsencrypt-dns:
acme:
email: admin@example.com
storage: /data/acme-dns.json
dnsChallenge:
provider: cloudflare
delayBeforeCheck: 30
resolvers:
- "1.1.1.1:53"
letsencrypt-staging:
acme:
email: admin@example.com
storage: /data/acme-staging.json
caServer: "https://acme-staging-v02.api.letsencrypt.org/directory"
httpChallenge:
entryPoint: web
Для DNS-01 через Cloudflare - переменные окружения в compose:
environment:
- CF_API_EMAIL=admin@example.com
- CF_DNS_API_TOKEN=your_cloudflare_api_token
Wildcard-сертификат в лейблах:
labels:
- "traefik.http.routers.myapp.tls.certresolver=letsencrypt-dns"
- "traefik.http.routers.myapp.tls.domains[0].main=example.com"
- "traefik.http.routers.myapp.tls.domains[0].sans=*.example.com"
Важные детали: файл acme.json должен иметь права 600. Используйте staging-резолвер при отладке - Let's Encrypt ограничивает до 5 дублирующих сертификатов в неделю на домен. Traefik автоматически обновляет сертификаты за 30 дней до истечения.
Минимальная версия TLS и наборы шифров:
tls:
options:
modern:
minVersion: VersionTLS13
intermediate:
minVersion: VersionTLS12
cipherSuites:
- TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
- TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
- TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256
Статическая конфигурация через файлы YAML
Файловый провайдер позволяет описывать роутеры и сервисы в YAML вместо лейблов. Это удобно для внешних серверов (вне Docker) или переиспользуемых middleware.
Пример dynamic.yml с маршрутизацией к внешним серверам и TCP:
http:
routers:
legacy-app:
rule: "Host(`legacy.example.com`)"
entryPoints:
- websecure
tls:
certResolver: letsencrypt
middlewares:
- security-headers
service: legacy-backend
services:
legacy-backend:
loadBalancer:
servers:
- url: "http://192.168.1.50:8080"
healthCheck:
path: /health
interval: 30s
timeout: 5s
grpc-backend:
loadBalancer:
servers:
- url: "h2c://grpc-service:50051"
tcp:
routers:
postgres:
rule: "HostSNI(`postgres.example.com`)"
entryPoints:
- postgres-entry
tls:
passthrough: true
service: postgres-service
services:
postgres-service:
loadBalancer:
servers:
- address: "postgres-container:5432"
TCP EntryPoint добавить в статическую конфигурацию:
entryPoints:
postgres-entry:
address: ":5432"
При watch: true Traefik перечитывает файл на лету без перезапуска.
Балансировка нагрузки и health check
Балансировка между несколькими репликами в Docker Compose:
services:
webapp:
image: mywebapp:latest
deploy:
replicas: 3
networks:
- traefik-public
labels:
- "traefik.enable=true"
- "traefik.http.routers.webapp.rule=Host(`app.example.com`)"
- "traefik.http.routers.webapp.entrypoints=websecure"
- "traefik.http.routers.webapp.tls.certresolver=letsencrypt"
- "traefik.http.services.webapp.loadbalancer.server.port=8000"
- "traefik.http.services.webapp.loadbalancer.sticky.cookie=true"
- "traefik.http.services.webapp.loadbalancer.sticky.cookie.name=lb_session"
- "traefik.http.services.webapp.loadbalancer.sticky.cookie.secure=true"
- "traefik.http.services.webapp.loadbalancer.healthcheck.path=/health"
- "traefik.http.services.webapp.loadbalancer.healthcheck.interval=10s"
- "traefik.http.services.webapp.loadbalancer.healthcheck.timeout=3s"
Circuit breaker - автоматическое отключение нездорового бэкенда:
http:
middlewares:
circuit-breaker:
circuitBreaker:
expression: "ResponseCodeRatio(500, 600, 0, 600) > 0.30 || NetworkErrorRatio() > 0.10"
checkPeriod: 10s
fallbackDuration: 30s
recoveryDuration: 10s
Это выражение открывает circuit breaker, если более 30% ответов - 5xx ошибки, или более 10% - сетевые ошибки. После открытия - 30 секунд возврата ошибки клиенту, затем 10 секунд на восстановление.
Мониторинг и dashboard
Traefik предоставляет три уровня наблюдаемости: встроенный dashboard, метрики для Prometheus и структурированные логи.
Dashboard показывает в реальном времени: все активные роутеры, сервисы и их health, middleware цепочки, сертификаты TLS и срок их действия. Доступен по домену из лейблов.
Ключевые метрики Prometheus:
traefik_entrypoint_requests_total- общее число запросов по entrypointtraefik_service_requests_total- запросы по сервисам с кодами ответаtraefik_service_request_duration_seconds- задержки (histogram)traefik_service_server_up- состояние бэкенда (0/1)traefik_tls_certs_not_after- срок истечения сертификатов
Полный стек мониторинга (Prometheus + Grafana):
services:
prometheus:
image: prom/prometheus:v3.1.0
restart: unless-stopped
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml:ro
- prometheus_data:/prometheus
command:
- "--config.file=/etc/prometheus/prometheus.yml"
- "--storage.tsdb.retention.time=15d"
networks:
- traefik-public
- monitoring
labels:
- "traefik.enable=true"
- "traefik.http.routers.prometheus.rule=Host(`prometheus.example.com`)"
- "traefik.http.routers.prometheus.entrypoints=websecure"
- "traefik.http.routers.prometheus.tls.certresolver=letsencrypt"
- "traefik.http.routers.prometheus.middlewares=basic-auth@file"
grafana:
image: grafana/grafana:11.4.0
restart: unless-stopped
environment:
GF_SECURITY_ADMIN_PASSWORD: changeme
GF_USERS_ALLOW_SIGN_UP: "false"
volumes:
- grafana_data:/var/lib/grafana
networks:
- traefik-public
- monitoring
labels:
- "traefik.enable=true"
- "traefik.http.routers.grafana.rule=Host(`grafana.example.com`)"
- "traefik.http.routers.grafana.entrypoints=websecure"
- "traefik.http.routers.grafana.tls.certresolver=letsencrypt"
volumes:
prometheus_data:
grafana_data:
networks:
monitoring:
internal: true
Готовый Grafana дашборд для Traefik - ID 12250 (Traefik 2 for Docker) или ID 17346 (Traefik Official). Импорт: Dashboards - Import - введите ID.
OpenTelemetry трассировка в Traefik v3:
tracing:
otlp:
grpc:
endpoint: "jaeger:4317"
insecure: true
sampleRate: 0.1
Типичные ошибки и troubleshooting
| Симптом | Вероятная причина | Решение |
|---|---|---|
| 404 от Traefik | Нет лейбла traefik.enable=true или контейнер не в сети traefik-public | docker inspect CONTAINER | grep Networks |
| 502 Bad Gateway | Неверный порт в лейблах или приложение не запущено | Проверить loadbalancer.server.port, docker logs CONTAINER |
| Сертификат не получается | Домен не ведёт на IP сервера, порт 80 закрыт, rate limit | dig A domain.com, открыть порт 80, использовать staging |
| Самоподписанный сертификат | Не указан tls.certresolver | Добавить traefik.http.routers.NAME.tls.certresolver=letsencrypt |
| acme.json permission denied | Неверные права на файл | chmod 600 /opt/traefik/data/acme.json |
| Middleware не применяется | Неверный суффикс (@docker/@file), опечатка в имени | Проверить dashboard, включить log.level: DEBUG |
| Бесконечный редирект | Приложение само делает редирект или не понимает X-Forwarded-Proto | Добавить customRequestHeaders: X-Forwarded-Proto: https |
| Конфликт имён роутеров | Два контейнера с одинаковым именем роутера | Traefik логирует "duplicate router name" - исправить уникальность |
Полезные команды для диагностики:
# Состояние роутеров через API
curl http://localhost:8080/api/http/routers | jq .
# Конкретный роутер
curl http://localhost:8080/api/http/routers/myapp@docker | jq .
# Все сервисы
curl http://localhost:8080/api/http/services | jq .
# Логи с фильтрацией ошибок
docker logs traefik -f 2>&1 | grep -i "error\|warn\|acme"
# Сеть и лейблы контейнера
docker inspect webapp | jq '.[0].NetworkSettings.Networks'
docker inspect webapp | jq '.[0].Config.Labels'
FAQ
Можно ли использовать Traefik без Docker, для обычных процессов на сервере?
Да. Файловый провайдер позволяет описывать бэкенды как статические URL: url: "http://127.0.0.1:3000". Traefik проксирует к ним трафик и выдаёт TLS-сертификаты так же, как для Docker-контейнеров. Удобно при миграции, когда часть сервисов уже в контейнерах.
Как Traefik v3 отличается от v2?
Основные изменения: нативная поддержка HTTP/3 без экспериментальных флагов, переход с Jaeger/Zipkin на OTLP для трассировки, новый синтаксис правил маршрутизации (совместимость сохранена), улучшенная поддержка Kubernetes Gateway API. Миграция с v2 на v3 в большинстве случаев - только обновление образа и проверка deprecated-параметров по официальному migration guide.
Что делать при нескольких инстансах Traefik для высокой доступности?
При нескольких инстансах каждый независимо запрашивает сертификаты у Let's Encrypt, что быстро приводит к превышению rate limit. Решение - централизованное хранилище: Redis или общий NFS-том для acme.json. В Traefik v3 Redis поддерживается как хранилище ACME через acme.storage: "redis://...".
Traefik умеет проксировать TCP без TLS (MySQL, Redis)?
Да. TCP-роутер с HostSNI(`*`) перехватывает весь TCP-трафик на EntryPoint. Без SNI нельзя маршрутизировать по имени хоста - только по порту. Для каждого TCP-сервиса без TLS выделяют отдельный порт в EntryPoints.
Насколько безопасно монтировать Docker socket в Traefik?
Монтирование /var/run/docker.sock даёт Traefik root-эквивалентный доступ к хосту. Снизить риск: использовать socket proxy (Tecnativa Docker Socket Proxy), ограничивающий API-вызовы только на чтение; запускать с no-new-privileges:true; в production рассмотреть Kubernetes, где Traefik работает без прямого доступа к socket.
Как отладить правило маршрутизации, которое не срабатывает?
Первый шаг - открыть dashboard и найти роутер. Нет роутера - проблема в провайдере (лейблы не читаются). Роутер есть, статус красный - проблема в сервисе или health check. Включите log.level: DEBUG и выполните тестовый запрос - в логах будет точная информация о том, какое правило сработало.
Заключение
Traefik v3 - зрелый инструмент, который за 10 лет прошёл путь от эксперимента до де-факто стандарта для обратного проксирования в Docker-окружениях. Автообнаружение сервисов, встроенный ACME, горячая перезагрузка конфигурации и богатая система middleware решают 90% задач без ручного написания конфигов.
Главные сильные стороны: нулевые операционные затраты на добавление нового сервиса (только лейблы в compose-файле), автоматическое управление сертификатами, прозрачность через dashboard и метрики. Слабые стороны: выше порог вхождения по сравнению с Nginx для простых сценариев, Docker socket создаёт поверхность атаки, при нагрузках выше 50k RPS стоит тестировать производительность.
Если вы запускаете несколько сервисов в Docker и тратите время на ручное управление Nginx-конфигами - Traefik окупит время на изучение за первые же несколько деплоев. Если же у вас один статический сайт или требования к производительности на уровне 100k+ RPS - оставайтесь на Nginx.
Хотите попробовать на изолированном сервере без риска для production? Подойдёт VPS с NVMe и KVM - поднять тестовое окружение с нуля займёт 15 минут.