Hasta ahora todo el tutorial se ha centrado en servicios HTTP. Y tiene sentido, porque es el caso de uso más común de Traefik. Pero Traefik también puede enrutar tráfico TCP y UDP, lo que te permite gestionar servicios que no son HTTP: bases de datos, servidores de juego, de voz, DNS, y un largo etcétera.
De hecho, cuando empiezas a tener más de dos o tres servicios en tu servidor, te das cuenta de que abrir puertos directos en el firewall para cada uno es un caos. Terminas con una lista de puertos que ni tú mismo recuerdas para qué sirven. Ahí es donde Traefik TCP/UDP marca la diferencia.
¿Por qué usar Traefik para TCP/UDP?
Puede que te preguntes: «si ya tengo un proxy inverso para HTTP, ¿para qué quiero enrutar TCP con él?». La respuesta es simple: centralización. En lugar de tener que abrir puertos directos en tu firewall para cada servicio, todo pasa por Traefik. Así mantienes el control de quién accede a qué, incluso a nivel de puerto.
Además, con los entrypoints TCP/UDP puedes:
- Usar el mismo puerto 443 para varios servicios (con SNI).
- Aplicar middlewares de autenticación a servicios TCP.
- Tener logs centralizados de todo el tráfico.
- Añadir TLS a servicios que no lo soportan nativamente.
- Balancear carga entre múltiples instancias de un mismo servicio.
- Hacer health checks para detectar instancias caídas.
Y todo esto sin tocar una sola regla de iptables. Te recomienda que te tomes un momento para pensar en los servicios de tu servidor que no son HTTP. ¿Tienes una base de datos? ¿Un servidor de Minecraft? ¿DNS? ¿Un servicio de voz como Mumble o TeamSpeak? Todos ellos pueden beneficiarse de pasar por Traefik.
Conceptos clave del enrutamiento TCP/UDP
Antes de meternos en harina, conviene entender cómo piensa Traefik cuando no hablamos de HTTP.
Con HTTP, Traefik examina la petición entera: mira el Host, el Path, las cabeceras… puede decidir a qué servicio enviar cada petición basándose en un montón de criterios.
Con TCP, la cosa cambia. Traefik no puede inspeccionar el contenido de la conexión porque es un flujo de bytes sin estructura. Lo único que puede mirar es:
- El puerto de destino (por el entrypoint que configures).
- El nombre SNI si la conexión lleva TLS.
Con UDP, todavía más simple: no hay SNI, no hay negociación, no hay nada. Todo el tráfico que llega a un entrypoint UDP va directamente al servicio de destino. Por eso es tan importante diseñar bien los entrypoints desde el principio.
TCP en Traefik
El enrutamiento TCP funciona de forma similar al HTTP, pero con algunas diferencias importantes que voy a detallarte.
EntryPoints TCP
Los entrypoints TCP se definen igual que los HTTP, pero sin configuración http:.
entryPoints:
postgres:
address: ":5432"
mysql:
address: ":3306"
redis:
address: ":6379"
mumble:
address: ":64738"
minecraft:
address: ":25565"
Puedes tener tantos entrypoints TCP como quieras, pero recuerda: cada uno ocupa un puerto en el sistema. Si tienes un firewall como UFW o iptables, solo necesitas abrir los puertos de Traefik, no los de los servicios internos.
Routers TCP
Los routers TCP no usan reglas como Host() o Path(). En su lugar, usan HostSNI() para enrutar según el nombre del servidor en la negociación TLS. O simplemente aceptan todo el tráfico que llegue a ese entrypoint.
labels:
- "traefik.tcp.routers.postgres.rule=HostSNI(`*`)"
- "traefik.tcp.routers.postgres.entrypoints=postgres"
- "traefik.tcp.routers.postgres.service=postgres-servicio"
- "traefik.tcp.services.postgres-servicio.loadbalancer.server.port=5432"
HostSNI(*): acepta cualquier conexión que llegue a este entrypoint. Para conexiones no TLS, usaHostSNI(*).entrypoints: el entrypoint TCP donde escucha.service: el servicio de destino.server.port: el puerto del contenedor de destino.
Puedes usar patrones más específicos como HostSNI(*.db.midominio.com) o incluso HostSNI(db1.midominio.com). Esto te permite tener varios servicios en el mismo entrypoint.
TCP con TLS
Puedes añadir TLS a conexiones TCP. Esto es muy útil para servicios que no soportan TLS nativamente, como algunas bases de datos antiguas o herramientas que no esperan cifrado.
labels:
- "traefik.tcp.routers.postgres.rule=HostSNI(`*`)"
- "traefik.tcp.routers.postgres.entrypoints=postgres-tls"
- "traefik.tcp.routers.postgres.service=postgres-servicio"
- "traefik.tcp.routers.postgres.tls=true"
- "traefik.tcp.services.postgres-servicio.loadbalancer.server.port=5432"
Cuando activas tls=true en un router TCP, Traefik hace de terminador TLS: descifra la conexión, la pasa en plano al servicio de destino, y cifra la respuesta de vuelta. El servicio ni siquiera sabe que existe TLS. Esto te permite proteger conexiones sin tener que configurar certificados en cada servicio.
Puedes incluso especificar qué certificado usar si tienes varios:
labels:
- "traefik.tcp.routers.postgres.tls=true"
- "traefik.tcp.routers.postgres.tls.certresolver=letsencrypt"
- "traefik.tcp.routers.postgres.tls.domains[0].main=db.midominio.com"
TLS Passthrough
Una alternativa a la terminación TLS es el TLS passthrough. En este modo, Traefik no descifra la conexión, solo la reenvía tal cual al servicio de destino. La negociación TLS ocurre directamente entre el cliente y el servicio.
labels:
- "traefik.tcp.routers.postgres.rule=HostSNI(`db.midominio.com`)"
- "traefik.tcp.routers.postgres.entrypoints=postgres"
- "traefik.tcp.routers.postgres.service=postgres-servicio"
- "traefik.tcp.routers.postgres.tls.passthrough=true"
Esto tiene ventajas e inconvenientes:
Ventajas: el servicio gestiona su propio TLS, puedes usar mutual TLS (mTLS) donde cliente y servidor se autentican mutuamente, y no hay descifrado intermedio que pueda ralentizar.
Inconvenientes: Traefik no puede inspeccionar el tráfico, no puede aplicar middlewares, y el servicio tiene que soportar TLS. Además, si usas passthrough, la regla HostSNI es obligatoria porque Traefik necesita al menos el nombre SNI para enrutar.
Mi recomendación: salvo que necesites mTLS o que el servicio exija gestionar su propio certificado, usa terminación TLS. Es más sencillo, más flexible, y centralizas la gestión de certificados en Traefik.
Enrutamiento TCP por SNI
Puedes tener múltiples servicios TCP en el mismo entrypoint, usando el nombre SNI para distinguirlos.
labels:
# Servicio 1
- "traefik.tcp.routers.db1.rule=HostSNI(`db1.dominio.com`)"
- "traefik.tcp.routers.db1.entrypoints=db-tls"
- "traefik.tcp.routers.db1.service=db1-servicio"
- "traefik.tcp.routers.db1.tls=true"
- "traefik.tcp.services.db1-servicio.loadbalancer.server.port=5432"
# Servicio 2
- "traefik.tcp.routers.db2.rule=HostSNI(`db2.dominio.com`)"
- "traefik.tcp.routers.db2.entrypoints=db-tls"
- "traefik.tcp.routers.db2.service=db2-servicio"
- "traefik.tcp.routers.db2.tls=true"
- "traefik.tcp.services.db2-servicio.loadbalancer.server.port=3306"
En este ejemplo, db1.dominio.com apunta a PostgreSQL y db2.dominio.com apunta a MySQL, ambos por el mismo entrypoint db-tls. El cliente se conecta a la misma IP y puerto, y Traefik distingue el destino por el SNI. Esto te puede ahorrar más de un disgusto cuando tienes que abrir puertos en firewalls corporativos.
Balanceo de carga TCP
Una de las funcionalidades más potentes de Traefik para TCP es el balanceo de carga. Puedes definir varios servidores de backend para un mismo servicio y Traefik distribuirá las conexiones entre ellos.
labels:
- "traefik.tcp.services.mi-servicio.loadbalancer.server.port=8080"
- "traefik.tcp.services.mi-servicio.loadbalancer.server.port=8081@internal"
O desde File provider:
tcp:
services:
mi-servicio:
loadBalancer:
servers:
- address: "192.168.1.10:5432"
- address: "192.168.1.11:5432"
- address: "192.168.1.12:5432"
Traefik usa round-robin por defecto, pero puedes configurar pesos para que unos servidores reciban más conexiones que otros:
tcp:
services:
mi-servicio:
weighted:
services:
- name: servidor-potente
weight: 3
- name: servidor-normal
weight: 1
Esto es ideal para bases de datos en replicación: el primario recibe más peso que los réplicas, o para servidores de juego donde quieres que un servidor potente gestione más jugadores.
Health checks en TCP
Si vas a balancear carga entre varias instancias, necesitas que Traefik sepa cuándo una instancia está caída. Para eso existen los health checks TCP.
tcp:
services:
postgres-cluster:
loadBalancer:
servers:
- address: "192.168.1.10:5432"
- address: "192.168.1.11:5432"
healthCheck:
interval: "10s"
timeout: "3s"
En TCP, el health check funciona de forma más simple que en HTTP: Traefik intenta abrir una conexión TCP con el servidor de backend. Si la conexión se establece correctamente, el servidor se considera sano. Si falla, se marca como caído y Traefik deja de enviarle tráfico.
Puedes configurar:
interval: cada cuánto se hace el health check (por defecto 30s).timeout: tiempo máximo de espera para la conexión (por defecto 5s).
tcp:
services:
redis-cluster:
loadBalancer:
servers:
- address: "10.0.0.1:6379"
- address: "10.0.0.2:6379"
healthCheck:
interval: "5s"
timeout: "2s"
Esto te puede ahorrar más de un disgusto cuando uno de tus servidores de base de datos se queda colgado y no te enteras hasta que los usuarios se quejan.
UDP en Traefik
El enrutamiento UDP es similar al TCP, pero con algunas limitaciones importantes que debes conocer antes de lanzarte.
- No soporta TLS (lógico, UDP no tiene negociación TLS).
- No tiene reglas de enrutamiento como
HostSNI. Todo el tráfico que llega al entrypoint se enruta al servicio configurado. - No tiene middlewares.
- No tiene health checks. Si el servicio de destino cae, Traefik seguirá enviándole tráfico.
- No soporta balanceo de carga con múltiples servidores de forma nativa.
¿Y entonces para qué sirve? Pues para servicios como DNS, servidores de voz, gaming, o cualquier protocolo que use UDP. La clave está en que, aunque UDP es más limitado, sigue siendo útil tenerlo centralizado en Traefik.
EntryPoints UDP
entryPoints:
dns:
address: ":53/udp"
mumble-udp:
address: ":64738/udp"
game-udp:
address: ":7777/udp"
Fíjate en la barra /udp al final. Sin eso, Traefik asume TCP y no funcionará. Es un error muy típico que te volverá loco durante un rato hasta que caes en que falta el /udp.
Routers UDP
labels:
- "traefik.udp.routers.dns.entrypoints=dns"
- "traefik.udp.routers.dns.service=dns-servicio"
- "traefik.udp.services.dns-servicio.loadbalancer.server.port=53"
No hay rule en UDP. Todo el tráfico que entra por el entrypoint va al servicio. Simple, directo, y sin complicaciones.
Limitaciones avanzadas de UDP
UDP trae una limitación adicional que poca gente menciona: el balanceo de carga en UDP no es realmente balanceo. Traefik usa un enfoque llamado «sticky session» basado en la IP de origen: una vez que un cliente se conecta a un servidor de backend UDP, todas sus conexiones futuras van al mismo servidor. Esto es porque UDP no tiene concepto de «conexión», así que Traefik no puede hacer round-robin de forma fiable.
Además, si el servidor de backend UDP se cae, Traefik no lo detectará y seguirá enviando tráfico. Para servicios críticos como DNS, te recomiendo que tengas un plan B fuera de Traefik, como tener un servidor DNS secundario independiente.
Middlewares en TCP
Aunque los middlewares TCP no son tan variados como los HTTP, tienes algunos disponibles que pueden sacarte de más de un apuro.
IPWhiteList para TCP
labels:
- "traefik.tcp.routers.postgres.rule=HostSNI(`*`)"
- "traefik.tcp.routers.postgres.entrypoints=postgres"
- "traefik.tcp.routers.postgres.service=postgres-servicio"
- "traefik.tcp.routers.postgres.middlewares=solo-local"
- "traefik.tcp.middlewares.solo-local.ipwhitelist.sourcerange=192.168.1.0/24,10.0.0.0/8"
Esto es muy útil para bases de datos: solo accesibles desde tu red local. Sin este middleware, cualquier persona que sepa la IP de tu servidor podría intentar conectarse a tu PostgreSQL.
InFlightConn para TCP
El middleware InFlightConn limita el número de conexiones simultáneas a un servicio TCP. Esto te protege de conexiones masivas que podrían saturar el servicio.
labels:
- "traefik.tcp.routers.postgres.middlewares=limitar-conexiones"
- "traefik.tcp.middlewares.limitar-conexiones.inflightconn.amount=50"
Si tienes una base de datos que no debería recibir más de 50 conexiones simultáneas, esto te puede ahorrar más de un disgusto cuando un proceso se vuelve loco y empieza a abrir conexiones sin control.
Ejemplos prácticos
Vamos a ver ejemplos reales de servicios que puedes enrutar con Traefik TCP/UDP. Dale caña.
PostgreSQL con Traefik
services:
postgres:
image: postgres:16
environment:
POSTGRES_PASSWORD: micontraseña
labels:
- "traefik.enable=true"
- "traefik.tcp.routers.postgres.rule=HostSNI(`*`)"
- "traefik.tcp.routers.postgres.entrypoints=postgres"
- "traefik.tcp.routers.postgres.service=postgres-servicio"
- "traefik.tcp.routers.postgres.middlewares=solo-local"
- "traefik.tcp.middlewares.solo-local.ipwhitelist.sourcerange=192.168.1.0/24"
- "traefik.tcp.services.postgres-servicio.loadbalancer.server.port=5432"
networks:
- traefik-net
Y en el traefik.yml:
entryPoints:
postgres:
address: ":5432"
Si además quieres cifrar la conexión con TLS (aunque PostgreSQL soporta TLS nativamente, puedes dejar que Traefik lo gestione):
labels:
- "traefik.tcp.routers.postgres.rule=HostSNI(`bd.midominio.com`)"
- "traefik.tcp.routers.postgres.entrypoints=postgres-tls"
- "traefik.tcp.routers.postgres.service=postgres-servicio"
- "traefik.tcp.routers.postgres.tls=true"
- "traefik.tcp.routers.postgres.tls.certresolver=letsencrypt"
- "traefik.tcp.services.postgres-servicio.loadbalancer.server.port=5432"
MySQL con Traefik
MySQL funciona prácticamente igual que PostgreSQL, solo cambia el puerto:
services:
mysql:
image: mysql:8
environment:
MYSQL_ROOT_PASSWORD: micontraseña
labels:
- "traefik.enable=true"
- "traefik.tcp.routers.mysql.rule=HostSNI(`*`)"
- "traefik.tcp.routers.mysql.entrypoints=mysql"
- "traefik.tcp.routers.mysql.service=mysql-servicio"
- "traefik.tcp.routers.mysql.middlewares=solo-local"
- "traefik.tcp.middlewares.solo-local.ipwhitelist.sourcerange=192.168.1.0/24"
- "traefik.tcp.services.mysql-servicio.loadbalancer.server.port=3306"
networks:
- traefik-net
Una curiosidad: MySQL tiene un protocolo de handshake propio que incluye información de capacidad. Si usas TLS con Traefik como terminador, asegúrate de que tu cliente MySQL soporte conexiones TLS. La mayoría de clientes modernos lo hacen sin problema.
Redis con Traefik
Redis es un caso interesante porque a menudo se usa como caché o cola de mensajes, y es muy habitual tenerlo corriendo en el mismo servidor que otras aplicaciones.
services:
redis:
image: redis:7-alpine
labels:
- "traefik.enable=true"
- "traefik.tcp.routers.redis.rule=HostSNI(`*`)"
- "traefik.tcp.routers.redis.entrypoints=redis"
- "traefik.tcp.routers.redis.service=redis-servicio"
- "traefik.tcp.routers.redis.middlewares=solo-local"
- "traefik.tcp.middlewares.solo-local.ipwhitelist.sourcerange=10.0.0.0/8"
- "traefik.tcp.services.redis-servicio.loadbalancer.server.port=6379"
networks:
- traefik-net
Redis no tiene TLS en su versión básica (hay Redis Stack que lo soporta), así que usar Traefik como terminador TLS es una excelente idea si necesitas acceder a Redis desde fuera de tu red local.
MongoDB con Traefik
MongoDB también puede enrutarse por TCP. Eso sí, ten en cuenta que MongoDB usa un protocolo binario propio y que si activas TLS en Traefik, el cliente debe estar configurado para confiar en el certificado de Traefik.
services:
mongodb:
image: mongo:7
environment:
MONGO_INITDB_ROOT_PASSWORD: micontraseña
labels:
- "traefik.enable=true"
- "traefik.tcp.routers.mongo.rule=HostSNI(`*`)"
- "traefik.tcp.routers.mongo.entrypoints=mongo"
- "traefik.tcp.routers.mongo.service=mongo-servicio"
- "traefik.tcp.routers.mongo.middlewares=solo-local"
- "traefik.tcp.middlewares.solo-local.ipwhitelist.sourcerange=192.168.1.0/24"
- "traefik.tcp.services.mongo-servicio.loadbalancer.server.port=27017"
networks:
- traefik-net
Servidor Minecraft (TCP)
Minecraft es uno de esos servicios que la gente suele tener en su servidor y que se beneficia enormemente de pasar por Traefik. El protocolo de Minecraft es TCP puro, y aunque no usa TLS, puedes usar SNI si configuras un proxy como BungeeCord o Velocity detrás de Traefik.
services:
minecraft:
image: itzg/minecraft-server:latest
environment:
EULA: "TRUE"
TYPE: PAPER
labels:
- "traefik.enable=true"
- "traefik.tcp.routers.minecraft.rule=HostSNI(`*`)"
- "traefik.tcp.routers.minecraft.entrypoints=minecraft"
- "traefik.tcp.routers.minecraft.service=minecraft-servicio"
- "traefik.tcp.services.minecraft-servicio.loadbalancer.server.port=25565"
networks:
- traefik-net
Y en el traefik.yml:
entryPoints:
minecraft:
address: ":25565"
Si tienes varios servidores Minecraft (por ejemplo, uno de supervivencia y otro creativo), puedes usar un proxy BungeeCord detrás de Traefik y enrutar por SNI. El BungeeCord escucha en un puerto interno y redirige a los servidores según el nombre que el cliente envíe.
Una advertencia importante: Minecraft tiene un sistema de ping que envía información del servidor (número de jugadores, versión, etc.). Con Traefik de por medio, este ping puede verse afectado. Te recomiendo probar que los jugadores pueden ver el servidor en la lista multijugador antes de abrirlo al público.
También puedes enrutar el protocolo Query de Minecraft (UDP en el puerto 25565) si tu servidor lo soporta. Algunos plugins de monitorización usan este protocolo.
entryPoints:
minecraft-query:
address: ":25565/udp"
Servidor Mumble (TCP + UDP)
Mumble necesita tanto TCP como UDP. El TCP se usa para la señalización y el control, mientras que el UDP lleva el audio en tiempo real.
services:
mumble:
image: mumble-server/mumble-server:latest
labels:
- "traefik.enable=true"
- "traefik.tcp.routers.mumble.rule=HostSNI(`*`)"
- "traefik.tcp.routers.mumble.entrypoints=mumble"
- "traefik.tcp.routers.mumble.service=mumble-servicio"
- "traefik.tcp.services.mumble-servicio.loadbalancer.server.port=64738"
- "traefik.udp.routers.mumble-udp.entrypoints=mumble-udp"
- "traefik.udp.routers.mumble-udp.service=mumble-udp-servicio"
- "traefik.udp.services.mumble-udp-servicio.loadbalancer.server.port=64738"
networks:
- traefik-net
Y en el traefik.yml:
entryPoints:
mumble:
address: ":64738"
mumble-udp:
address: ":64738/udp"
Mumble negocia automáticamente si usa TCP o UDP. Si el UDP no funciona (por ejemplo, si el firewall bloquea el puerto UDP), Mumble cae a TCP. Con Traefik, al tener ambos entrypoints, te aseguras de que la calidad de audio sea la mejor posible.
Servidor DNS con CoreDNS
El DNS es el ejemplo clásico de servicio UDP. Pero ojo, el DNS también usa TCP para transferencias de zona y respuestas grandes (más de 512 bytes).
services:
dns:
image: coredns/coredns:latest
labels:
- "traefik.enable=true"
- "traefik.udp.routers.dns.entrypoints=dns"
- "traefik.udp.routers.dns.service=dns-servicio"
- "traefik.udp.services.dns-servicio.loadbalancer.server.port=53"
- "traefik.tcp.routers.dns-tcp.rule=HostSNI(`*`)"
- "traefik.tcp.routers.dns-tcp.entrypoints=dns-tcp"
- "traefik.tcp.routers.dns-tcp.service=dns-tcp-servicio"
- "traefik.tcp.services.dns-tcp-servicio.loadbalancer.server.port=53"
networks:
- traefik-net
Y en el traefik.yml:
entryPoints:
dns:
address: ":53/udp"
dns-tcp:
address: ":53"
Fíjate que aquí tienes el puerto 53 tanto en TCP como en UDP. Es obligatorio si quieres un servidor DNS completamente funcional.
Servidor DNS con Unbound
Unbound es un resolver DNS muy popular que puedes usar como DNS local o como forwarder. Es más ligero que CoreDNS y consume menos recursos.
services:
unbound:
image: mvance/unbound:latest
labels:
- "traefik.enable=true"
- "traefik.udp.routers.unbound.entrypoints=dns"
- "traefik.udp.routers.unbound.service=unbound-dns"
- "traefik.udp.services.unbound-dns.loadbalancer.server.port=53"
- "traefik.tcp.routers.unbound-tcp.rule=HostSNI(`*`)"
- "traefik.tcp.routers.unbound-tcp.entrypoints=dns-tcp"
- "traefik.tcp.routers.unbound-tcp.service=unbound-dns-tcp"
- "traefik.tcp.services.unbound-dns-tcp.loadbalancer.server.port=53"
networks:
- traefik-net
Si montas tu propio DNS, acuérdate de que los puertos por debajo de 1024 requieren privilegios de root. Docker los gestiona automáticamente si mapeas el puerto, pero si ejecutas Traefik directamente, necesitarás permisos especiales o usar CAP_NET_BIND_SERVICE.
Configuración avanzada desde File provider
Cuando tus servicios TCP/UDP empiezan a ser varios, los labels de Docker se vuelven difíciles de mantener. Aquí es donde el File provider brilla con luz propia.
# traefik-dynamic-tcp.yml
tcp:
routers:
postgres:
rule: HostSNI(`*`)
entryPoints:
- postgres
service: postgres-servicio
middlewares:
- solo-local@file
mysql:
rule: HostSNI(`*`)
entryPoints:
- mysql
service: mysql-servicio
middlewares:
- solo-local@file
redis:
rule: HostSNI(`*`)
entryPoints:
- redis
service: redis-servicio
middlewares:
- solo-local@file
mongo:
rule: HostSNI(`*`)
entryPoints:
- mongo
service: mongo-servicio
middlewares:
- solo-local@file
minecraft:
rule: HostSNI(`*`)
entryPoints:
- minecraft
service: minecraft-servicio
dns-tcp:
rule: HostSNI(`*`)
entryPoints:
- dns-tcp
service: dns-tcp-servicio
services:
postgres-servicio:
loadBalancer:
servers:
- address: "192.168.1.50:5432"
mysql-servicio:
loadBalancer:
servers:
- address: "192.168.1.51:3306"
redis-servicio:
loadBalancer:
servers:
- address: "192.168.1.52:6379"
mongo-servicio:
loadBalancer:
servers:
- address: "192.168.1.53:27017"
minecraft-servicio:
loadBalancer:
servers:
- address: "192.168.1.60:25565"
dns-tcp-servicio:
loadBalancer:
servers:
- address: "192.168.1.54:53"
middlewares:
solo-local:
ipWhiteList:
sourceRange:
- "192.168.1.0/24"
- "10.0.0.0/8"
- "172.16.0.0/12"
limitar-conexiones:
inFlightConn:
amount: 50
udp:
routers:
dns:
entryPoints:
- dns
service: dns-servicio
mumble-udp:
entryPoints:
- mumble-udp
service: mumble-udp-servicio
services:
dns-servicio:
loadBalancer:
servers:
- address: "192.168.1.54:53"
mumble-udp-servicio:
loadBalancer:
servers:
- address: "192.168.1.55:64738"
Y en el traefik.yml principal, asegúrate de cargar este archivo:
providers:
file:
filename: /etc/traefik/dynamic/traefik-dynamic-tcp.yml
watch: true
Con watch: true, Traefik recarga la configuración automáticamente cuando modificas el archivo. No necesitas reiniciar el servicio.
Seguridad y advertencias
El enrutamiento TCP/UDP con Traefik es potente, pero también abre la puerta a ciertos riesgos si no lo configuras con cuidado.
Firewall y puertos expuestos
Cada entrypoint TCP/UDP que defines en Traefik es un puerto abierto al mundo (a menos que tengas un firewall delante). Si expones un servicio como PostgreSQL directamente a internet, aunque sea a través de Traefik, estás aumentando la superficie de ataque.
Mi recomendación: usa IPWhiteList en todos los servicios TCP que no necesiten ser públicos. Bases de datos, Redis, MongoDB… todo eso debería estar restringido a tu red local o a IPs específicas.
Además, configura un firewall a nivel de sistema operativo:
# Solo permitir tráfico a los puertos necesarios
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow 5432/tcp # Solo si necesitas PostgreSQL público
sudo ufw enable
TLS Passthrough vs terminación TLS
Elegir entre passthrough y terminación TLS no es una decisión trivial.
Con terminación TLS, Traefik descifra el tráfico y lo reenvía en plano al servicio. Esto significa que:
- El tráfico entre Traefik y el servicio va en claro. Si alguien accede a tu red interna, puede espiarlo.
- Traefik puede aplicar middlewares (IPWhiteList, InFlightConn).
- Gestionas los certificados en un solo sitio.
Con TLS passthrough:
- El tráfico viaja cifrado de principio a fin.
- No puedes aplicar middlewares.
- El servicio necesita gestionar su propio certificado.
Si el servicio y Traefik están en la misma máquina o en la misma red Docker, la terminación TLS es segura. Si el servicio está en otra máquina o red, valora usar passthrough.
Protección contra abusos
Los servicios TCP expuestos a internet son vulnerables a ataques de fuerza bruta. Una base de datos PostgreSQL con el puerto 5432 abierto va a recibir intentos de conexión constantes de bots.
Algunas medidas que te recomiendo:
- IPWhiteList: restringe el acceso por IP.
- InFlightConn: limita conexiones simultáneas.
- Fail2ban: combínalo con Traefik para banear IPs tras varios intentos fallidos.
- Cambia el puerto externo: aunque no es seguridad real, usar un puerto no estándar reduce el ruido de los scanners automáticos.
Para el punto 4, puedes hacer que Traefik escuche en un puerto diferente al estándar:
entryPoints:
postgres:
address: ":15432" # Puerto no estándar
Y luego desde tu cliente te conectas a tu-servidor:15432. No es infalible, pero los scanners automáticos suelen probar solo los puertos por defecto.
Redes internas y aislamiento
Cuando uses Traefik para servicios TCP/UDP, asegúrate de que los contenedores de base de datos no estén expuestos en la red de Traefik si no es necesario. Puedes tener una red separada solo para bases de datos:
networks:
traefik-net:
external: true
db-net:
driver: bridge
services:
postgres:
networks:
- db-net # No necesita traefik-net, Traefik se conecta por db-net
Y luego configuras el servicio de Traefik para que se conecte a la red db-net también. Así, los contenedores de base de datos solo son accesibles desde Traefik y no desde otros contenedores.
Troubleshooting
Cuando algo no funciona con TCP/UDP en Traefik, los síntomas suelen ser confusos. Te dejo una guía rápida de los problemas más comunes y cómo solucionarlos.
Conexión rechazada (Connection refused)
Si ves «Connection refused» al intentar conectarte a un servicio TCP detrás de Traefik:
- Verifica que el entrypoint está definido correctamente en
traefik.yml. - Comprueba que el servicio de destino está corriendo y escuchando en el puerto correcto.
- Revisa los logs de Traefik:
docker logs traefikojournalctl -u traefik. - Asegúrate de que las redes Docker están bien: el contenedor de Traefik y el del servicio deben estar en la misma red.
# Verificar que el puerto está abierto en Traefik
docker exec traefik netstat -tlnp | grep 5432
# Verificar logs de Traefik
docker logs traefik --tail 50
SNI no coincide
Si usas enrutamiento por SNI y las conexiones no llegan al servicio correcto:
- Revisa que el cliente envía SNI: no todos los clientes lo hacen. Por ejemplo,
psqlno envía SNI a menos que usessslmode=require. - Usa
HostSNI(*)como fallback para capturar conexiones sin SNI. - Comprueba la ortografía del nombre SNI: un punto mal puesto y la regla no casa.
# Verificar qué SNI está enviando el cliente (desde otro terminal)
openssl s_client -connect tu-servidor:5432 -servername bd.midominio.com
Si ves CONNECTED y luego el certificado correcto, el SNI funciona.
UDP no responde
Los problemas con UDP son particularmente difíciles de depurar porque no hay confirmación de conexión. Si tu servicio UDP no responde:
- Asegúrate de que el entrypoint tiene
/udp: sin eso, Traefik asume TCP y el paquete UDP se pierde. - Verifica que el firewall no bloquea UDP: el protocolo UDP puede estar bloqueado aunque TCP esté abierto.
- Comprueba los logs de Traefik: los errores UDP suelen aparecer como «accept udp: too many open files» o similares.
# Probar si el puerto UDP está accesible
nc -z -u tu-servidor 53
# Ver logs específicos de UDP en Traefik
docker logs traefik 2>&1 | grep -i udp
Conflictos de puertos
Si Traefik no arranca porque el puerto está ocupado:
# Ver qué proceso está usando el puerto
sudo lsof -i :5432
sudo netstat -tlnp | grep 5432
Posibles causas:
- El servicio de destino está exponiendo el puerto directamente en el host además de a través de Traefik.
- Otro servicio (como PostgreSQL nativo) está usando el mismo puerto.
- La configuración de Docker publica el puerto del contenedor (
ports:) además de los labels de Traefik.
Solución: no publiques los puertos de los contenedores de servicios si van a través de Traefik. Usa solo las redes Docker:
services:
postgres:
# Sin ports: - Traefik se conecta por la red interna
expose:
- "5432"
networks:
- traefik-net
Timeout de conexión
Si la conexión se queda colgada y finalmente da timeout:
- Revisa el firewall intermedio: puede que esté bloqueando el puerto pero devolviendo un drop silencioso en lugar de un reject.
- Aumenta el timeout en el router TCP si es necesario:
tcp:
routers:
postgres:
rule: HostSNI(`*`)
entryPoints:
- postgres
service: postgres-servicio
# No hay timeout configurables en routers TCP,
# pero puedes ajustar el loadbalancer
services:
postgres-servicio:
loadBalancer:
servers:
- address: "192.168.1.50:5432"
# Los timeouts se heredan de la configuración global de Traefik
También puedes ajustar los timeouts globales en traefik.yml:
providers:
# ...
serversTransport:
insecureSkipVerify: false
# No hay transport para TCP, pero hay config global
Diagnóstico con logs
Traefik tiene logs muy detallados para TCP/UDP si activas el nivel adecuado:
# Activar logs de depuración
docker exec traefik traefik --log.level=DEBUG
O en traefik.yml:
log:
level: DEBUG
filePath: /var/log/traefik.log
Con DEBUG verás cada conexión TCP que entra, a qué router se asigna, y a qué servicio se envía. Es la mejor herramienta para depurar problemas de enrutamiento TCP.
Buenas prácticas
Después de todo lo visto, te dejo un resumen de buenas prácticas para trabajar con TCP y UDP en Traefik.
- Usa
HostSNI(*)para TCP a no ser que necesites enrutar por nombre. Es la regla más simple y funciona para todo. - Protege los servicios TCP con IPWhiteList. No tiene sentido tener una base de datos accesible desde todo internet. Si necesitas acceso remoto, usa un VPN como WireGuard en lugar de exponer la base de datos directamente.
- Añade TLS a servicios TCP que no lo soporten nativamente. Traefik puede hacer de terminador TLS para ellos. Redis, MongoDB o MySQL se benefician enormemente de esto.
- Para UDP, ten en cuenta que no hay middlewares ni reglas. Todo el tráfico que llega al entrypoint va al servicio. Asegúrate de que quieres que cualquier persona pueda alcanzar ese servicio.
- No olvides el
/udpen la definición del entrypoint UDP. Sin eso, Traefik lo trata como TCP y no funcionará. Es el error más tonto y a la vez más común. - Usa redes Docker separadas para aislar servicios. No pongas todos los contenedores en la misma red si no es necesario.
- Documenta qué puertos has abierto y para qué. Cuando tengas 20 entrypoints, te alegrarás de tener una lista.
- Monitorea los logs de Traefik. Un aumento repentino de conexiones a un puerto TCP puede ser un ataque o un error de configuración.
- Combina Traefik con un firewall externo. Traefik gestiona el enrutamiento, pero el firewall debe ser la primera línea de defensa.
- Haz pruebas de conectividad después de cada cambio. Un cambio en los entrypoints puede romper servicios que llevaban meses funcionando.
Conclusión
El soporte TCP y UDP de Traefik te permite centralizar todo el tráfico de tu servidor, sea del tipo que sea. Bases de datos, servidores de voz, DNS, gaming, servidores de Minecraft… todo puede pasar por Traefik, dándote control, logs y seguridad unificados.
Hemos visto desde configuraciones básicas hasta escenarios avanzados como balanceo de carga TCP, health checks, TLS passthrough, y múltiples servicios compitiendo por el mismo entrypoint. También te he dejado una guía de troubleshooting para que los problemas más comunes no te pillen desprevenido.
En el próximo capítulo veremos Seguridad y hardening, cómo proteger tu infraestructura Traefik de amenazas externas. Veremos configuración de cabeceras de seguridad, rate limiting, autenticación, y cómo endurecer Traefik para entornos de producción. No te lo pierdas.
Más información
- Traefik Documentation — TCP Routing — Routers, servicios y balanceo TCP
- Traefik Documentation — UDP Routing — Routers y servicios UDP
- Traefik Documentation — EntryPoints — Entrypoints TCP/UDP: puertos, protocolos, configuración
- Traefik Documentation — TLS — Terminación TLS y TLS Passthrough en TCP
- Traefik Documentation — Middlewares / IPWhiteList — Restricción por IP en servicios TCP
- Traefik Documentation — Middlewares / InFlightConn — Límite de conexiones TCP simultáneas
- Tutorial original en atareao.es — Fuente de este capítulo