Tienes Traefik funcionando, con múltiples servicios, certificados SSL, middlewares y seguridad. Pero, ¿cómo saber si todo funciona correctamente? ¿Cómo detectar problemas antes de que los usuarios se quejen?
Para eso está el monitoring. Traefik v3 ofrece varias formas de monitorizar tu infraestructura: métricas con Prometheus, trazabilidad con OpenTelemetry, y logs de acceso detallados. Y te recomiendo que no te saltes este capítulo, porque una vez que despliegues en producción y tengas usuarios de verdad, el monitoring no es un lujo — es una necesidad. Te lo digo por experiencia: el día que algo falla a las 3 de la mañana, te alegrarás de tener dashboards y alertas configuradas.
Logs de acceso
Lo más básico que puedes hacer es activar los logs de acceso. Te dan información detallada de cada petición que pasa por Traefik. Sin ellos, estás volando a ciegas.
En tu traefik.yml,
accessLog:
filePath: /var/log/traefik/access.log
format: json
bufferingSize: 100
fields:
defaultMode: keep
names:
ClientHost: keep
ClientPort: keep
RequestHost: keep
RequestPath: keep
RequestMethod: keep
RequestProtocol: keep
ResponseStatus: keep
ResponseSize: keep
Duration: keep
OriginStatus: drop
headers:
defaultMode: drop
names:
User-Agent: keep
Referer: keep
format: json: el log en JSON es más fácil de procesar con herramientas como Elasticsearch o Loki. En formato texto es legible para un humano, pero un ordenador lo parsea mucho mejor en JSON.bufferingSize: buffer de escritura para no saturar el disco. Si tienes mucho tráfico, esto te puede ahorrar más de un disgusto con el I/O del servidor.fields: controlas qué campos incluir. Puedes ocultar información sensible como cabeceras de autorización o direcciones IP internas. Esto es importante si manejas datos personales y te entra la GDPR.
¿Y qué haces con estos logs? Pues puedes enviarlos a un sistema centralizado como ELK, Loki o Graylog y tener dashboards de todo el tráfico. Te recomiendo que montes Promtail + Loki + Grafana, porque es el stack más ligero y se integra de puta madre con todo lo que ya tienes.
Filtrando logs por status code
A veces no quieres guardar TODO, solo lo que te interesa. Puedes filtrar:
accessLog:
filters:
statusCodes:
- "200-299"
- "400-499"
- "500-599"
retryAttempts: true
minDuration: "100ms"
Con statusCodes decides qué rangos registrar. Con minDuration solo guardas peticiones que tardan más de X milisegundos — ideal para pillar cuellos de botella sin rellenar el disco con peticiones rápidas e irrelevantes.
Formato personalizado
Si JSON no te va, puedes usar un formato personalizado con variables:
accessLog:
format: common
# O formato personalizado:
# format: "$request_protocol - $request_method $request_path -> $response_status en $duration ms"
Las variables disponibles incluyen $client_host, $request_host, $request_method, $request_path, $response_status, $duration, $request_protocol, $response_size, $backend_url, y muchas más. Dale caña y personalízalo a tu gusto.
Rotación de logs
Ojo, que los logs de acceso pueden crecer sin control. Traefik no hace rotación por sí mismo, así que tienes que gestionarlo externamente:
accessLog:
filePath: /var/log/traefik/access.log
format: json
bufferingSize: 100
rotation:
maxSize: 100
maxBackups: 5
maxAge: 7
localTime: true
compress: true
O si usas Docker, puedes configurar log rotation a nivel de contenedor:
services:
traefik:
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "5"
compress: "true"
Esto te puede ahorrar más de un disgusto cuando tu disco se llena a las 3 de la madrugada.
Métricas con Prometheus
Prometheus es el estándar de facto para métricas en el ecosistema Cloud Native. Y Traefik lo soporta de forma nativa, sin necesidad de exporters adicionales ni historias raras.
Activar Prometheus en Traefik
En tu traefik.yml,
metrics:
prometheus:
addEntryPointsLabels: true
addServicesLabels: true
manualRouting: true
entryPoint: metrics
buckets:
- 0.01
- 0.025
- 0.05
- 0.1
- 0.25
- 0.5
- 1.0
- 2.5
- 5.0
- 10.0
Fíjate en buckets: son los intervalos de tiempo que usa Prometheus para el histograma de latencia. Por defecto Traefik usa los buckets estándar de Prometheus, pero te recomiendo que los ajustes según tu caso. Si tu API responde en milisegundos, tener un bucket de 10 segundos no te sirve de nada. Si sirves ficheros grandes, igual sí.
Y creas un entrypoint para las métricas:
entryPoints:
metrics:
address: ":8082"
Pero ojo: este entrypoint no debe ser accesible desde internet. Lo configuras para que solo escuche en localhost o en tu red interna:
entryPoints:
metrics:
address: "127.0.0.1:8082"
Si usas Docker, otra opción es no mapear el puerto y acceder por la red interna de Docker:
services:
traefik:
ports:
- "80:80"
- "443:443"
# No mapees el 8082, solo expónlo en la red interna
Prometheus se conecta usando el nombre del servicio de Docker y el puerto interno.
Todas las métricas que obtienes
Una vez activado, Traefik expone métricas en http://localhost:8082/metrics. Vale la pena que conozcas todas, no solo las típicas. Aquí tienes la lista completa:
Métricas de peticiones:
traefik_requests_total— número total de peticiones. Con labelscode,method,protocol,service,entrypoint.traefik_request_duration_seconds— histograma de tiempos de respuesta. El santo grial para medir rendimiento.traefik_request_duration_seconds_bucket— los buckets individuales del histograma, con el conteo de requests en cada rango.traefik_request_duration_seconds_sum— suma total de tiempo de todas las peticiones.traefik_request_duration_seconds_count— número total de peticiones medidas.traefik_requests_bytes_total— volumen total de datos transferidos en requests (body).traefik_responses_bytes_total— volumen total de datos transferidos en responses.traefik_backend_requests_total— peticiones por servicio backend.traefik_backend_request_duration_seconds— histograma de latencia por backend.traefik_backend_responses_bytes_total— bytes de respuesta por backend.traefik_backend_response_status— códigos de respuesta por servicio.traefik_entrypoint_requests_total— peticiones por entrypoint.traefik_entrypoint_request_duration_seconds— latencia por entrypoint.traefik_entrypoint_requests_bytes_total— bytes por entrypoint.traefik_entrypoint_responses_bytes_total— bytes de respuesta por entrypoint.
Métricas de conexiones:
traefik_open_connections— conexiones activas actualmente. Con labelmethod. Si este número se dispara, tienes un problema.traefik_tcp_connections_total— conexiones TCP totales (para entrypoints TCP).traefik_tcp_open_connections— conexiones TCP abiertas.traefik_tcp_write_bytes_total— bytes escritos en conexiones TCP.traefik_tcp_read_bytes_total— bytes leídos en conexiones TCP.
Métricas de configuración:
traefik_config_reloads_total— recargas de configuración. Si ves muchas, algo está cambiando constantemente.traefik_config_reloads_failures_total— recargas fallidas. Si esto sube, tienes un error en la configuración.traefik_config_last_reload_success— 1 si la última recarga fue exitosa, 0 si falló. Una métrica golang típica que es oro puro para alertas.traefik_config_last_reload_failure— timestamp de la última recarga fallida.
Métricas de TLS y certificados:
traefik_tls_certificates_not_after— timestamp de expiración de cada certificado. Con labelcn(common name).traefik_tls_certificates_not_before— timestamp de inicio de validez.traefik_tls_certificates_secrets_names— nombres de los secretos o rutas de los certificados.traefik_tls_certificate_validation_failures_total— fallos de validación de certificados.
Métricas internas de Traefik:
traefik_health_info— información general de salud del router.traefik_build_info— versión de Traefik que estás ejecutando. Con labelsversion,goversion,goarch.
Con todas estas métricas puedes construir dashboards realmente completos. ¿Ves por qué te digo que no es solo poner Prometheus y ya?
Configuración avanzada de Prometheus
Vale, ya tienes el scrape_configs básico. Pero si quieres hacerlo bien, te recomiendo una configuración más completa:
# prometheus.yml
global:
scrape_interval: 15s
evaluation_interval: 15s
scrape_timeout: 10s
scrape_configs:
- job_name: 'traefik'
scrape_interval: 10s
scrape_timeout: 5s
metrics_path: /metrics
scheme: http
static_configs:
- targets: ['traefik:8082']
labels:
service: 'traefik'
env: 'production'
- job_name: 'traefik-ping'
scrape_interval: 30s
metrics_path: /ping
scheme: http
static_configs:
- targets: ['traefik:8082']
- job_name: 'node'
scrape_interval: 15s
static_configs:
- targets: ['node-exporter:9100']
labels:
host: 'docker-host'
env: 'production'
- job_name: 'docker'
scrape_interval: 15s
static_configs:
- targets: ['cadvisor:8080']
labels:
env: 'production'
Fíjate en los detalles:
- scrape_interval más frecuente para Traefik (10s) que para el resto (15s). Las métricas de tráfico cambian rápido, quieres capturarlas con granularidad fina.
- scrape_timeout de 5s para no bloquear el scrape si Traefik está saturado. Mejor perder una muestra que tener Prometheus esperando.
- Labels adicionales como
serviceyenvpara filtrar en Grafana después. - node_exporter y cAdvisor para métricas del sistema y de los contenedores. Así cuando veas que la latencia sube, puedes mirar si es porque la CPU está al 100%.
Usando service discovery de Docker
Si no quieres configurar targets a mano, Prometheus puede descubrir contenedores automáticamente:
scrape_configs:
- job_name: 'docker'
docker_sd_configs:
- host: "unix:///var/run/docker.sock"
refresh_interval: 15s
relabel_configs:
- source_labels: [__meta_docker_container_name]
regex: '/(.*)'
target_label: 'container'
- source_labels: [__meta_docker_container_label_prometheus_job]
regex: '(.+)'
target_label: 'job'
Esto te permite etiquetar tus contenedores con labels de Docker tipo prometheus.job=traefik y Prometheus los descubre solito. Muy útil cuando tienes muchos servicios y no quieres mantener una lista estática.
Almacenamiento y retención
Prometheus por defecto guarda 15 días de métricas. Si quieres cambiarlo:
# prometheus.yml
global:
scrape_interval: 15s
storage:
tsdb:
retention:
time: 30d
size: 50GB
Puedes configurar retención por tiempo (30d) o por tamaño máximo (50GB). Si tienes mucho tráfico, te recomiendo limitar por tamaño para no reventar el disco.
Consultas útiles en PromQL
Te dejo algunas queries que uso a menudo. Dales caña:
Tasa de peticiones por segundo:
rate(traefik_requests_total[5m])
Tasa de errores HTTP 5xx:
rate(traefik_requests_total{code=~"5.."}[5m]) / rate(traefik_requests_total[5m]) * 100
Percentil 99 de latencia (p99):
histogram_quantile(0.99, sum(rate(traefik_request_duration_seconds_bucket[5m])) by (le))
Peticiones lentas (>1s):
rate(traefik_request_duration_seconds_bucket{le="1.0"}[5m]) / ignoring(le) rate(traefik_request_duration_seconds_count[5m])
Conexiones abiertas ahora mismo:
traefik_open_connections
Certificados que expiran en menos de 30 días:
traefik_tls_certificates_not_after - time() < 2592000
Recargas de configuración fallidas:
increase(traefik_config_reloads_failures_total[1h]) > 0
Con estas queries puedes construir alertas en Prometheus o dashboards en Grafana. Te recomiendo que empieces con la tasa de errores y la latencia p99 — son las dos métricas que más problemas te van a destapar.
Dashboards de Grafana
Tener las métricas en Prometheus está muy bien, pero si no las visualizas, no te sirven para nada. Grafana es el mejor compañero de Prometheus y juntos forman un equipo imbatible.
Importar dashboards predefinidos
Lo más rápido es importar un dashboard ya hecho. Hay varios específicos para Traefik:
- ID 17378: dashboard oficial de Traefik para Prometheus. Tiene paneles de peticiones, latencia, códigos de estado, conexiones activas, recargas de configuración, TLS, y más. Es el que te recomiendo si quieres empezar rápido.
- ID 4475: dashboard más simple pero muy completo. Buena opción si prefieres algo menos abrumador.
- ID 18638: dashboard centrado en rendimiento con percentiles de latencia y throughput.
- ID 15758: dashboard de Traefik con panel de topología de servicios.
Solo tienes que ir a Grafana → Import → pegar el ID → seleccionar la fuente de datos Prometheus → y listo.
Configurar la fuente de datos en Grafana
Si usas el docker-compose que te pongo más abajo, necesitas configurar Grafana para que sepa dónde está Prometheus:
# grafana-datasources.yml
apiVersion: 1
datasources:
- name: Prometheus
type: prometheus
access: proxy
url: http://prometheus:9090
isDefault: true
editable: false
jsonData:
timeInterval: "15s"
queryTimeout: "30s"
httpMethod: "POST"
Y lo montas como volumen en Grafana:
services:
grafana:
image: grafana/grafana:latest
volumes:
- ./grafana-datasources.yml:/etc/grafana/provisioning/datasources/datasources.yml:ro
- ./grafana-dashboards.yml:/etc/grafana/provisioning/dashboards/dashboards.yml:ro
- ./grafana-dashboards:/var/lib/grafana/dashboards
Dashboards provisionados automáticamente
Si no quieres andar importando a mano cada vez que reinicies, puedes provisionar dashboards:
# grafana-dashboards.yml
apiVersion: 1
providers:
- name: 'Traefik'
orgId: 1
folder: 'Traefik'
type: file
disableDeletion: true
editable: true
updateIntervalSeconds: 30
options:
path: /var/lib/grafana/dashboards
Y descargas el JSON del dashboard 17378, lo guardas en ./grafana-dashboards/traefik-official.json, y Grafana lo carga automáticamente al arrancar.
Qué paneles no pueden faltar en tu dashboard
Si prefieres crear tu propio dashboard desde cero, te recomiendo estos paneles mínimos:
- RPS (Requests Per Second) — gráfica de área con la tasa de peticiones. Filtrable por entrypoint y servicio.
- Latencia p50, p95 y p99 — tres líneas superpuestas. Cuando el p99 se separa del p50, tienes un problema de rendimiento.
- Códigos de estado HTTP — stacked bar chart con 2xx, 3xx, 4xx, 5xx. Los 5xx deben ser cero o casi cero.
- Throughput (bytes) — tráfico de entrada y salida.
- Conexiones activas — gauge o time series. Si ves una meseta que no baja, hay clientes colgados.
- Errores TLS — contador de errores de handshake TLS y validación de certificados.
- Estado de recargas — 1/0 según si la última recarga fue exitosa. Si ves un 0, algo está mal en tu configuración.
- Certificados próximos a expirar — tabla con los CN y días restantes.
Te recomiendo que añadas también un panel de health check con las métricas del sistema (CPU, RAM, disco) del host donde corre Traefik. A veces el problema no es Traefik, sino que el servidor se está quedando sin recursos.
Alertas en Prometheus
Las métricas están muy bien, pero si tienes que mirar el dashboard cada 5 minutos, algo estás haciendo mal. Las alertas te avisan cuando algo va mal, y te recomiendo que configures al menos estas:
# alertas.yml
groups:
- name: traefik
rules:
- alert: HighErrorRate
expr: |
rate(traefik_requests_total{code=~"5.."}[5m]) /
rate(traefik_requests_total[5m]) * 100 > 5
for: 5m
labels:
severity: critical
annotations:
summary: "Tasa de error alta en Traefik"
description: "La tasa de errores 5xx es del {{ $value | humanizePercentage }} en los últimos 5 minutos."
- alert: HighLatency
expr: |
histogram_quantile(0.99,
sum(rate(traefik_request_duration_seconds_bucket[5m])) by (le)
) > 3
for: 5m
labels:
severity: warning
annotations:
summary: "Latencia alta en Traefik"
description: "El p99 de latencia es de {{ $value | humanizeDuration }}."
- alert: CertificateExpiringSoon
expr: |
traefik_tls_certificates_not_after - time() < 604800
labels:
severity: critical
annotations:
summary: "Certificado TLS próximo a expirar"
description: "El certificado {{ $labels.cn }} expira en menos de 7 días."
- alert: ConfigReloadFailed
expr: |
increase(traefik_config_reloads_failures_total[15m]) > 0
labels:
severity: critical
annotations:
summary: "Error en recarga de configuración"
description: "Traefik ha fallado al recargar la configuración."
- alert: NoTraffic
expr: |
rate(traefik_requests_total[5m]) == 0
for: 10m
labels:
severity: warning
annotations:
summary: "Sin tráfico en Traefik"
description: "No se detectan peticiones en los últimos 10 minutos."
Y en prometheus.yml añades:
rule_files:
- "alertas.yml"
alerting:
alertmanagers:
- static_configs:
- targets:
- alertmanager:9093
Esto te puede ahorrar más de un disgusto cuando un certificado caduca un domingo por la noche.
OpenTelemetry (OTLP)
Traefik v3 introduce soporte nativo para OpenTelemetry, el estándar moderno para observabilidad. Con OTLP puedes enviar trazas (traces) y métricas a sistemas como Jaeger, Tempo, Grafana, o cualquier backend que soporte OTLP.
Activar OpenTelemetry en Traefik
experimental:
openTelemetry:
grpc:
address: "otel-collector:4317"
insecure: true
O usando HTTP (el puerto 4318 es el estándar HTTP para OTLP):
experimental:
openTelemetry:
http:
address: "otel-collector:4318"
insecure: true
¿La diferencia? gRPC es más eficiente en términos de rendimiento y usa protocol buffers. HTTP es más fácil de depurar y funciona mejor si hay firewalls que bloquean gRPC. Te recomiendo gRPC para producción y HTTP si estás haciendo pruebas.
Configuración completa de OpenTelemetry en Traefik
Puedes ajustar varios parámetros:
experimental:
openTelemetry:
grpc:
address: "otel-collector:4317"
insecure: true
# timeout: 5s
# headers:
# x-api-key: "tu-api-key"
# También puedes usar headers de propagación personalizados
# propagation: "tracecontext,baggage"
El campo propagation define qué formatos de contexto de tracing usar. Por defecto usa W3C Trace Context (tracecontext), que es el estándar moderno. Si tienes servicios legacy que usan Zipkin o Jaeger, puedes añadirlos:
experimental:
openTelemetry:
grpc:
address: "otel-collector:4317"
insecure: true
propagation: "tracecontext,baggage,jaeger"
¿Para qué sirven las trazas?
Cada petición que entra en Traefik genera una traza (trace) que puedes seguir a través de todos los servicios. Una traza está compuesta de spans, cada uno representando una operación: el span de Traefik recibiendo la petición, el span del middleware de autenticación, el span del balanceador pasando la petición al backend, el span del backend procesándola…
Esto es oro puro para depurar problemas de rendimiento. Por ejemplo, si un usuario se queja de que la página va lenta, puedes abrir la traza completa en Jaeger o Tempo y ver:
- Cuánto tardó Traefik en enrutar (normalmente <1ms).
- Cuánto tardó el middleware de rate limiting.
- Cuánto tardó el middleware de autenticación (¿está llamando a un servicio externo?).
- Cuánto tardó el servicio en responder.
- Cuánto tardó la respuesta en volver al cliente.
Sin trazas, solo ves que algo es lento. Con trazas, sabes exactamente qué parte es la culpable.
OpenTelemetry Collector: configuración avanzada
Para aprovechar OpenTelemetry, necesitas un collector que reciba las trazas y las envíe a un backend. El collector es el cerebro de la operación: recibe datos por un lado, los procesa, y los envía a uno o varios destinos.
# docker-compose.yml
services:
otel-collector:
image: otel/opentelemetry-collector-contrib:latest
container_name: otel-collector
command: ["--config=/etc/otelcol-contrib/config.yaml"]
volumes:
- ./otel-collector.yml:/etc/otelcol-contrib/config.yaml:ro
networks:
- traefik-net
Y su configuración. Aquí te pongo un ejemplo completo con exportadores a varios backends:
# otel-collector.yml
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
processors:
batch:
timeout: 1s
send_batch_size: 1024
memory_limiter:
check_interval: 1s
limit_mib: 512
attributes:
actions:
- key: environment
value: production
action: upsert
- key: datacenter
value: mad01
action: upsert
filter:
error_mode: ignore
traces:
span:
- 'attributes["http.status_code"] >= 200 and attributes["http.status_code"] < 400'
exporters:
logging:
verbosity: detailed
otlp:
endpoint: "tempo:4317"
tls:
insecure: true
prometheus:
endpoint: "0.0.0.0:8889"
namespace: traefik_otlp
const_labels:
source: otel-collector
service:
pipelines:
traces:
receivers: [otlp]
processors: [memory_limiter, attributes, batch]
exporters: [logging, otlp]
metrics:
receivers: [otlp]
processors: [memory_limiter, attributes, batch]
exporters: [logging, prometheus]
Vamos por partes:
- receivers: escucha en gRPC (4317) y HTTP (4318) para recibir trazas y métricas de Traefik.
- processors: aquí está la magia.
batchagrupa trazas para mejorar rendimiento,memory_limiterevita que el collector se coma toda la RAM,attributesañade metadatos como entorno y datacenter, yfilterdescarta trazas que no nos interesan (por ejemplo, solo guardar errores). - exporters:
loggingmuestra las trazas en la consola del collector (ideal para debug),otlplas envía a Tempo o Jaeger, yprometheusexpone métricas del propio collector.
Enviando trazas a Grafana Tempo
Grafana Tempo es un backend de trazas escalable y se integra perfectamente con Grafana. Para usarlo:
services:
tempo:
image: grafana/tempo:latest
container_name: tempo
command: ["-config.file=/etc/tempo.yaml"]
volumes:
- ./tempo.yml:/etc/tempo.yaml:ro
- ./tempo-data:/tmp/tempo
networks:
- traefik-net
# Y el collector apunta a tempo
Configuración de Tempo:
# tempo.yml
server:
http_listen_port: 3200
distributor:
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
storage:
trace:
backend: local
local:
path: /tmp/tempo/blocks
wal:
path: /tmp/tempo/wal
compactor:
compaction:
block_retention: 48h
Enviando trazas a Jaeger
Si prefieres Jaeger en lugar de Tempo:
services:
jaeger:
image: jaegertracing/all-in-one:latest
container_name: jaeger
ports:
- "16686:16686" # UI
networks:
- traefik-net
Y en el collector cambias el exportador:
exporters:
otlp:
endpoint: "jaeger:4317"
tls:
insecure: true
Luego abres http://localhost:16686 y ves todas las trazas en la UI de Jaeger. Te recomiendo que pruebes esto aunque luego uses Tempo — Jaeger es más sencillo de configurar para empezar.
Headers de propagación en tus servicios
Para que las trazas atraviesen todos tus servicios, necesitas propagar los headers de OpenTelemetry. Cuando Traefik recibe una petición, añade estos headers antes de pasarla al backend:
traceparent: 00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01
tracestate: traefik=1
baggage: user-id=123,session-id=abc
Tus servicios backend deben leer estos headers y continuar la traza. Cada lenguaje tiene su SDK de OpenTelemetry:
- Python:
opentelemetry-api+opentelemetry-sdk - Node.js:
@opentelemetry/api+@opentelemetry/node - Go:
go.opentelemetry.io/otel - Java:
io.opentelemetry:opentelemetry-api - PHP:
open-telemetry/opentelemetry
Te pongo un ejemplo rápido en Python con FastAPI:
from opentelemetry import trace
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
# Configurar el tracer
provider = TracerProvider()
processor = BatchSpanProcessor(OTLPSpanExporter(endpoint="otel-collector:4317", insecure=True))
provider.add_span_processor(processor)
trace.set_tracer_provider(provider)
# Instrumentar FastAPI automáticamente
app = FastAPI()
FastAPIInstrumentor.instrument_app(app)
Así de sencillo. Tu servicio FastAPI ya genera trazas que continúan las que empezó Traefik.
Logs de Traefik
Además de los logs de acceso, Traefik tiene sus propios logs de sistema. Por defecto son stdout (la salida estándar del contenedor), pero puedes configurarlos:
log:
level: INFO
filePath: /var/log/traefik/traefik.log
format: json
Niveles disponibles: DEBUG, INFO, WARN, ERROR. En producción usa INFO. DEBUG es muy verboso y solo para depurar. Te recomiendo que no actives DEBUG en producción a menos que estés investigando un problema específico — llenará tus logs en cuestión de minutos.
Diferencias entre logs de acceso y logs de sistema
Esto es importante y mucha gente lo confunde:
- Logs de acceso (
accessLog): registran cada petición HTTP que pasa por Traefik. Quién pide qué, cuándo, y cómo responde Traefik. - Logs de sistema (
log): registran eventos internos de Traefik. Recargas de configuración, errores internos, warnings, conexiones establecidas con proveedores.
Los logs de sistema son los que miras cuando Traefik no arranca o se comporta de forma extraña. Los logs de acceso son los que miras para auditar tráfico o analizar rendimiento.
Integración con Loki
Loki es un sistema de logs de Grafana, diseñado específicamente para logs que ya están estructurados. A diferencia de Elasticsearch, Loki no indexa el contenido del log, solo los metadatos (labels). Esto lo hace mucho más ligero y barato de operar.
services:
loki:
image: grafana/loki:latest
container_name: loki
ports:
- "3100:3100"
volumes:
- ./loki.yml:/etc/loki/loki.yml:ro
- ./loki-data:/loki
networks:
- traefik-net
Configuración de Loki:
# loki.yml
auth_enabled: false
server:
http_listen_port: 3100
ingester:
lifecycler:
ring:
kvstore:
store: inmemory
replication_factor: 1
chunk_idle_period: 5m
chunk_retain_period: 30s
schema_config:
configs:
- from: 2020-10-24
store: boltdb-shipper
object_store: filesystem
schema: v11
index:
prefix: index_
period: 24h
storage_config:
boltdb_shipper:
active_index_directory: /loki/index
cache_location: /loki/cache
shared_store: filesystem
filesystem:
directory: /loki/chunks
limits_config:
reject_old_samples: true
reject_old_samples_max_age: 168h
Y necesitas Promtail para leer los logs de Traefik y enviarlos a Loki:
services:
promtail:
image: grafana/promtail:latest
container_name: promtail
volumes:
- ./promtail.yml:/etc/promtail/promtail.yml:ro
- /var/log/traefik:/var/log/traefik:ro
- /var/run/docker.sock:/var/run/docker.sock
networks:
- traefik-net
Configuración de Promtail:
# promtail.yml
server:
http_listen_port: 9080
grpc_listen_port: 0
positions:
filename: /tmp/positions.yaml
clients:
- url: http://loki:3100/loki/api/v1/push
scrape_configs:
- job_name: traefik
static_configs:
- targets: [localhost]
labels:
job: traefik
__path__: /var/log/traefik/access.log
pipeline_stages:
- json:
expressions:
ClientHost: ClientHost
RequestHost: RequestHost
RequestPath: RequestPath
RequestMethod: RequestMethod
ResponseStatus: ResponseStatus
Duration: Duration
ResponseSize: ResponseSize
- labels:
ResponseStatus:
RequestMethod:
- timestamp:
source: Time
format: RFC3339Nano
Con Promtail parseas el JSON de los logs de acceso, extraes campos como labels de Loki, y puedes filtrar y buscar en Grafana como si fuera una base de datos.
Ejemplo completo
Te dejo un docker-compose.yml completo con Traefik, Prometheus, Grafana, Loki, Promtail, OpenTelemetry Collector y Tempo para monitorización completa. Esto es lo que te recomiendo para producción:
services:
traefik:
image: traefik:v3.7
container_name: traefik
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./traefik.yml:/etc/traefik/traefik.yml:ro
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./letsencrypt:/letsencrypt
- ./logs:/var/log/traefik
networks:
- monitoring-net
prometheus:
image: prom/prometheus:latest
container_name: prometheus
restart: unless-stopped
volumes:
- ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro
- ./prometheus/alertas.yml:/etc/prometheus/alertas.yml:ro
- prometheus-data:/prometheus
networks:
- monitoring-net
grafana:
image: grafana/grafana:latest
container_name: grafana
restart: unless-stopped
ports:
- "3000:3000"
volumes:
- ./grafana/grafana-datasources.yml:/etc/grafana/provisioning/datasources/datasources.yml:ro
- ./grafana/grafana-dashboards.yml:/etc/grafana/provisioning/dashboards/dashboards.yml:ro
- ./grafana/dashboards:/var/lib/grafana/dashboards
- grafana-data:/var/lib/grafana
networks:
- monitoring-net
alertmanager:
image: prom/alertmanager:latest
container_name: alertmanager
restart: unless-stopped
volumes:
- ./alertmanager.yml:/etc/alertmanager/alertmanager.yml:ro
networks:
- monitoring-net
otel-collector:
image: otel/opentelemetry-collector-contrib:latest
container_name: otel-collector
restart: unless-stopped
command: ["--config=/etc/otelcol-contrib/config.yaml"]
volumes:
- ./otel/otel-collector.yml:/etc/otelcol-contrib/config.yaml:ro
networks:
- monitoring-net
tempo:
image: grafana/tempo:latest
container_name: tempo
restart: unless-stopped
command: ["-config.file=/etc/tempo.yaml"]
volumes:
- ./tempo.yml:/etc/tempo.yaml:ro
- tempo-data:/tmp/tempo
networks:
- monitoring-net
loki:
image: grafana/loki:latest
container_name: loki
restart: unless-stopped
volumes:
- ./loki.yml:/etc/loki/loki.yml:ro
- loki-data:/loki
networks:
- monitoring-net
promtail:
image: grafana/promtail:latest
container_name: promtail
restart: unless-stopped
volumes:
- ./promtail.yml:/etc/promtail/promtail.yml:ro
- ./logs:/var/log/traefik:ro
- /var/run/docker.sock:/var/run/docker.sock
networks:
- monitoring-net
node-exporter:
image: prom/node-exporter:latest
container_name: node-exporter
restart: unless-stopped
volumes:
- /proc:/host/proc:ro
- /sys:/host/sys:ro
- /:/rootfs:ro
command:
- '--path.procfs=/host/proc'
- '--path.sysfs=/host/sys'
- '--path.rootfs=/rootfs'
networks:
- monitoring-net
pid: host
networks:
monitoring-net:
name: monitoring-net
driver: bridge
volumes:
prometheus-data:
grafana-data:
tempo-data:
loki-data:
Fíjate en que he añadido node-exporter para métricas del sistema. No solo monitorices Traefik, monitoriza también el servidor donde corre.
Troubleshooting
No todo va a funcionar a la primera. Aquí te dejo los problemas más comunes y cómo solucionarlos.
Prometheus no scrapea métricas
Síntoma: Prometheus marca el target como DOWN en la página de targets (http://prometheus:9090/targets).
Posibles causas:
- El entrypoint de métricas no es accesible desde Prometheus. Verifica que el entrypoint
metricsesté escuchando en0.0.0.0:8082o en la IP de la red de Docker. Si pusiste127.0.0.1:8082, solo es accesible desde el propio contenedor de Traefik.
# Verifica desde dentro del contenedor de Prometheus
docker exec prometheus wget -qO- http://traefik:8082/metrics | head -20
- Redes de Docker separadas. Prometheus y Traefik deben estar en la misma red Docker.
- Firewall bloqueando el puerto. Si Traefik y Prometheus están en hosts diferentes, asegúrate de que el puerto 8082 esté abierto.
Grafana no ve datos
Síntoma: Los paneles de Grafana muestran «No data» o «N/A».
Posibles causas:
- La fuente de datos apunta a la URL incorrecta. En Grafana, la URL debe ser
http://prometheus:9090(nombre del servicio Docker, no localhost). - El intervalo de tiempo es demasiado corto. Las métricas de Prometheus tardan en acumularse. Pon un rango de al menos 5-15 minutos.
- Las queries de PromQL son incorrectas para tu versión. Algunos nombres de métricas cambiaron entre versiones de Traefik. Verifica los nombres exactos en
http://traefik:8082/metrics.
OpenTelemetry no envía trazas
Síntoma: No aparecen trazas en Jaeger/Tempo.
Posibles causas:
- El collector no está accesible. Verifica que el contenedor
otel-collectoresté ejecutándose y escuchando en los puertos correctos:
docker logs otel-collector
docker exec otel-collector netstat -tlnp
- La configuración de Traefik no tiene
experimentalhabilitado. Sin esa sección, OpenTelemetry no funciona. - El puerto gRPC o HTTP no coincide. Asegúrate de que el puerto en la configuración de Traefik coincida con el del collector.
- Timeout de conexión. Si el collector tarda en responder, Traefik puede descartar la traza. Ajusta el timeout:
experimental:
openTelemetry:
grpc:
address: "otel-collector:4317"
insecure: true
timeout: 10s
Logs de acceso vacíos
Síntoma: El archivo de log existe pero está vacío, o no se crea.
Posibles causas:
- El directorio de logs no existe. Traefik no crea directorios automáticamente. Asegúrate de que la ruta exista:
mkdir -p ./logs
chmod 755 ./logs
- Permisos del volumen. Si usas Docker, el usuario de Traefik (normalmente uid 1000 o 1001) debe poder escribir en el directorio montado.
- Formato incorrecto. Si usas
format: jsony el archivo no se genera, prueba primero conformat: commonpara descartar problemas de serialización.
Certificados Let’s Encrypt no se renuevan
Síntoma: Los certificados caducan y Traefik no los renueva automáticamente.
Métricas para detectarlo:
traefik_tls_certificates_not_after - time() < 604800
Si esta alerta se dispara, revisa los logs de sistema de Traefik para ver si hay errores de ACME:
log:
level: DEBUG
filePath: /var/log/traefik/traefik.log
format: json
Las causas más comunes son límites de rate de Let’s Encrypt (5 renovaciones por dominio a la semana) o problemas de conectividad con el servidor ACME.
Alto consumo de memoria
Síntoma: Traefik consume cada vez más RAM con el tiempo (memory leak aparente).
Posibles causas:
- Buffering de logs demasiado grande. Reduce
bufferingSizeen el accessLog. - Demasiadas métricas con alta cardinalidad. Si tienes muchos servicios, cada uno con múltiples labels, Prometheus puede generar muchas series temporales. Limita las labels:
metrics:
prometheus:
addEntryPointsLabels: true
addServicesLabels: false # Si tienes muchos servicios, desactiva esto
manualRouting: true
- OpenTelemetry sin límite de memoria en el collector. Añade el processor
memory_limiteren el collector:
processors:
memory_limiter:
check_interval: 1s
limit_mib: 512
spike_limit_mib: 128
Buenas prácticas
- Activa los logs de acceso en formato JSON. Son más fáciles de procesar que el formato texto y cualquier sistema de logs los entiende.
- Usa Prometheus + Grafana para métricas. Es el stack más maduro y con mejores dashboards. Lleva años en producción en empresas de todos los tamaños.
- No expongas el endpoint de métricas a internet. Ponlo en localhost o en tu red interna. Si alguien accede a tus métricas, sabe exactamente qué servicios tienes y cómo se comportan.
- Configura alertas cuanto antes. No sirve de nada tener métricas si no te avisan cuando algo va mal. Empieza con las alertas críticas (errores 5xx, certificados próximos a expirar, latencia alta) y luego añade más.
- OpenTelemetry es para sistemas complejos. Si tienes 2-3 servicios, con Prometheus y logs tienes suficiente. Pero si tienes microservicios que se llaman entre sí, las trazas son la única forma de entender qué está pasando.
- Retención de datos sensata. No guardes logs y métricas para siempre. Define una política de retención: 30 días para métricas, 90 días para logs, 7 días para trazas. Ajusta según tus necesidades y espacio disponible.
- Monitoriza también el monitoring. Asegúrate de que Prometheus, Grafana y Loki tienen recursos suficientes y están funcionando. No hay nada más triste que un sistema de monitorización caído.
- Prueba las alertas. Simula un fallo: para un servicio, cambia la configuración a propósito, deja caducar un certificado de prueba. Verifica que las alertas se disparan y te llegan por el canal correcto (email, Slack, Telegram…).
Conclusión
La monitorización es lo que separa un sistema amateur de uno profesional. Con Prometheus, Grafana, logs de acceso, Loki y OpenTelemetry, tienes visibilidad completa de lo que pasa en tu infraestructura Traefik. Cuando algo vaya mal, lo sabrás antes de que los usuarios te lo digan — y eso, créeme, no tiene precio.
Te recomiendo que montes todo el stack que te he mostrado, aunque sea en un entorno de pruebas primero. Configura las alertas, juega con los dashboards, aprende a leer las trazas. Cuando llegue el momento de pasar a producción, ya sabrás exactamente qué mirar y dónde.
Y recuerda: el monitoring no es un proyecto de un día. Es algo que vas ajustando con el tiempo. Añade métricas nuevas, refina las alertas, mejora los dashboards. Dale caña y no dejes de iterar.
En el próximo y último capítulo veremos HTTP/3 y rendimiento, cómo optimizar Traefik para sacarle el máximo partido.
Más información,
- Traefik Documentation — Observability / Access Logs — Logs de acceso: formato, filtros, campos
- Traefik Documentation — Observability / Metrics / Prometheus — Métricas de Prometheus: configuración, buckets, labels
- Traefik Documentation — Observability / Metrics / OpenTelemetry — Trazas y métricas con OpenTelemetry
- Traefik Documentation — Operations / Ping — Endpoint de health check para monitorización
- Traefik Documentation — Operations / API — API REST para consultar estado de routers, servicios y middlewares
- Grafana — Dashboard oficial de Traefik — Dashboard 17378: monitorización completa de Traefik
- Tutorial original en atareao.es — Fuente de este capítulo