¿Por qué poner Traefik al frente de su VPS?
Cuando ejecuta varias aplicaciones en contenedores sobre un mismo VPS, gestionar manualmente los virtual hosts de Nginx y renovar cada certificado se vuelve rápidamente inmanejable. Traefik resuelve ese problema leyendo directamente los labels de sus contenedores Docker: usted declara una ruta en el momento del docker run o en su docker-compose.yml, y Traefik la configura en caliente, genera el certificado Let's Encrypt y la expone por HTTPS. Ningún archivo de configuración que editar, ningún reload: todo es dinámico.
Nginx sigue siendo un servidor HTTP excelente, pero su configuración es estática: cada servicio nuevo exige un vhost, un nginx -s reload y una herramienta ACME aparte (certbot y su tarea de renovación). Traefik se diseñó para una topología que cambia sin cesar, que es justo el caso de un VPS donde se añaden, actualizan y retiran contenedores cada semana. La rama v3, publicada en abril de 2024, estabilizó HTTP/3 y la exportación de trazas con OpenTelemetry, y endureció la sintaxis de las reglas de enrutamiento.
Lo que Traefik aporta en concreto en un VPS
- Descubrimiento automático de los contenedores Docker mediante el provider Docker (las rutas aparecen sin reiniciar)
- Certificados SSL Let's Encrypt generados y renovados automáticamente (challenges HTTP-01, TLS-ALPN-01 o DNS-01), renovados por defecto 30 días antes de caducar
- Enrutamiento por dominio, por ruta o por cabecera, gestionado íntegramente mediante los labels Docker
- Dashboard web integrado para visualizar routers, servicios y middlewares en tiempo real
- Middlewares listos para usar: redirección HTTPS, basic auth, rate limiting, cabeceras de seguridad, compresión
- Balanceo de carga automático cuando escala un servicio a varias réplicas
- HTTP/3 (QUIC) activable en un entrypoint con una sola opción
- Otros providers además de Docker (Swarm, Kubernetes, Nomad, archivos) si su infraestructura crece
Requisitos previos
Traefik en sí es muy ligero: 1 vCPU y 1 GB de RAM bastan de sobra para el proxy solo, ya que el sobrecoste viene de sus aplicaciones que están detrás. Prevea un VPS con Debian 12 o Ubuntu 22.04/24.04 con Docker Engine y el plugin Docker Compose v2 instalados (el comando docker compose, sin guion), un nombre de dominio cuyos registros A/AAAA apunten a la IP del VPS antes del primer arranque (si no, Let's Encrypt no puede validar el dominio), y los puertos 80 y 443 abiertos en entrada en su cortafuegos (UFW), en TCP, más el 443 en UDP si activa HTTP/3. Tenga a mano una dirección de correo válida para la cuenta ACME. Para los certificados wildcard, también necesitará un acceso a la API de su proveedor de DNS.
Desplegar Traefik con Docker Compose
Crear la red y la estructura de carpetas
Cree una red Docker compartida que usarán Traefik y sus aplicaciones, y luego una carpeta dedicada que contenga el archivo
acme.json(el almacén de los certificados y de sus claves privadas):docker network create proxy mkdir -p /opt/traefik && cd /opt/traefik touch acme.json && chmod 600 acme.jsonCree el archivo antes del primer arranque: si la ruta montada no existe, Docker crea una carpeta en su lugar y Traefik ya no puede escribir en ella. Los permisos 600 son obligatorios: Traefik rechaza un archivo legible por otros usuarios.
Configurar los entrypoints y el resolutor ACME
Coloque este archivo en
/opt/traefik/docker-compose.yml. Declara dos entrypoints (weben :80,websecureen :443), un certificateResolver de Let's Encrypt validado por el challenge HTTP, el descubrimiento de los contenedores mediante el socket de Docker montado en solo lectura, y el dashboard:services: traefik: image: traefik:v3.7 container_name: traefik restart: unless-stopped ports: - "80:80" - "443:443" - "443:443/udp" command: - "--providers.docker=true" - "--providers.docker.exposedbydefault=false" - "--providers.docker.network=proxy" - "--entrypoints.web.address=:80" - "--entrypoints.web.http.redirections.entrypoint.to=websecure" - "--entrypoints.web.http.redirections.entrypoint.scheme=https" - "--entrypoints.websecure.address=:443" - "--entrypoints.websecure.http3=true" - "[email protected]" - "--certificatesresolvers.letsencrypt.acme.storage=/acme.json" - "--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web" - "--api.dashboard=true" volumes: - "/var/run/docker.sock:/var/run/docker.sock:ro" - "./acme.json:/acme.json" networks: - proxy labels: - "traefik.enable=true" - "traefik.http.routers.dashboard.rule=Host(`traefik.example.com`)" - "traefik.http.routers.dashboard.entrypoints=websecure" - "traefik.http.routers.dashboard.tls.certresolver=letsencrypt" - "traefik.http.routers.dashboard.service=api@internal" - "traefik.http.routers.dashboard.middlewares=dashboard-auth" - "traefik.http.middlewares.dashboard-auth.basicauth.users=admin:$$2y$$05$$REMPLACER_PAR_LE_HASH" networks: proxy: external: trueLa opción
exposedbydefault=falsees esencial: sin ella, cada contenedor del VPS quedaría publicado, incluida su base de datos. Fije la rama (v3.7) en lugar delatesty siga las releases antes de cambiar de versión menor.Forzar la redirección de HTTP a HTTPS
La redirección se declara directamente en el entrypoint
web, con las dos opciones--entrypoints.web.http.redirections.entrypoint.to=websecurey...entrypoint.scheme=httpsya presentes en el archivo anterior. Es más simple y más seguro que un router «catch-all» con middleware: todo el tráfico en claro pasa a HTTPS mediante una redirección permanente, sin label que repetir en cada servicio, y el challenge HTTP de Let's Encrypt sigue pasando por el puerto 80.Asegurar el dashboard
El dashboard se expone en
traefik.example.com, detrás del middlewarebasicauthllamadodashboard-auth. Genere un hash bcrypt (paqueteapache2-utilsen Debian/Ubuntu) duplicando los$, como exige un valor de label en un archivo Compose:apt install -y apache2-utils htpasswd -nB admin | sed -e 's/\$/\$\$/g'Pegue el resultado en lugar de
admin:$$2y$$05$$REMPLACER_PAR_LE_HASH. Un hash pegado con$simples lo interpreta Compose como variables y rompe la autenticación. No deje nunca el flag--api.insecure=trueen producción: publica el dashboard sin autenticación.Lanzar Traefik
Arranque el stack y siga los logs:
cd /opt/traefik docker compose up -d docker compose logs -f traefikDebe ver arrancar el provider Docker y luego engancharse el router
dashboard, sin errores ACME. Durante sus pruebas, añada temporalmente--certificatesresolvers.letsencrypt.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory: el servidor de staging de Let's Encrypt emite certificados que los navegadores no reconocen, pero le protege del rate limit de producción si una configuración falla en bucle. Retire la opción y vacíeacme.jsonantes de pasar a producción.Conectar una aplicación
Cada aplicación vive en su propio
docker-compose.yml, conectada a la redproxyy descrita por sus labels:services: app: image: ghcr.io/example/app:1.0.0 restart: unless-stopped networks: - proxy labels: - "traefik.enable=true" - "traefik.http.routers.app.rule=Host(`app.example.com`)" - "traefik.http.routers.app.entrypoints=websecure" - "traefik.http.routers.app.tls.certresolver=letsencrypt" - "traefik.http.services.app.loadbalancer.server.port=3000" networks: proxy: external: trueAl hacer
docker compose up -d, Traefik detecta el contenedor, obtiene el certificado y activa la ruta en unos segundos, sin tocar su propia configuración. El labelloadbalancer.server.portindica el puerto interno de la aplicación; se vuelve imprescindible en cuanto la imagen expone varios. El nombre del router (appaquí) debe ser único en todo el VPS.Comprobar el certificado y la redirección
Compruebe desde una máquina externa al VPS:
curl -I http://app.example.com curl -I https://app.example.com dig +short app.example.comLa primera petición debe devolver una redirección permanente hacia
https://, la segunda un200(o el código de su aplicación) servido con un certificado Let's Encrypt válido, ydigdebe mostrar la IP del VPS. El dashboard también lista cada router con su resolutor de certificados. Un puerto 80 que rechaza la conexión casi siempre apunta al cortafuegos.
Middlewares útiles en producción
Un middleware de Traefik se declara por label, una sola vez, y después se asocia a uno o varios routers con traefik.http.routers.<router>.middlewares=<nombre1>,<nombre2>. Así que los define en el servicio afectado, sin tocar la configuración central de Traefik. Estos son los que sirven en casi todos los VPS.
Cinco middlewares que conviene conocer
- BasicAuth en una herramienta de administración (Adminer, Flower…):
traefik.http.middlewares.adminauth.basicauth.users=admin:$$2y$$05$$..., y despuéstraefik.http.routers.miadmin.middlewares=adminauth. La misma regla de$duplicados que para el dashboard. - Rate limit delante de una API o un formulario:
traefik.http.middlewares.ratelimit.ratelimit.average=100,...ratelimit.period=1sy...ratelimit.burst=200permiten 100 peticiones por segundo de media, con ráfagas de hasta 200. Sinaverage, el límite está desactivado. - Cabeceras de seguridad: el middleware
headersañade HSTS (stsSeconds=31536000,stsIncludeSubdomains=true),X-Frame-Options: DENY(frameDeny=true) yX-Content-Type-Options: nosniff(contentTypeNosniff=true), sin archivo de configuración aparte. - Redirección de www al dominio desnudo:
redirectregexconregex=^https://www\.(.+),replacement=https://$${1}ypermanent=true. Recuerde declarar los dos hosts en la regla del router para que el certificado cubrawww. - Compresión:
traefik.http.middlewares.compress.compress=trueactiva gzip, Brotli y Zstandard según lo que acepte el navegador, sin ajuste en la aplicación.
Para obtener un certificado wildcard (*.example.com) y evitar exponer cada subdominio, pase del httpChallenge al dnsChallenge: es el único challenge que emite un wildcard. Con Cloudflare, declare --certificatesresolvers.letsencrypt.acme.dnschallenge.provider=cloudflare y pase a Traefik la variable CF_DNS_API_TOKEN, un token de API limitado a los permisos Zone / DNS / Edit y Zone / Zone / Read. El antiguo par CF_API_EMAIL + CF_API_KEY también funciona, pero exige la clave de API global, que abre toda la cuenta: prefiera el token. Traefik demuestra entonces la propiedad del dominio con un registro TXT, lo que funciona incluso para servicios que no están expuestos públicamente.
Activar HTTP/3 (QUIC)
El archivo Compose anterior ya lo activa: --entrypoints.websecure.http3=true en el entrypoint TLS y la publicación del puerto 443:443/udp. Traefik escucha entonces en UDP en el mismo puerto que el entrypoint TCP; una conexión empieza siempre en TCP y luego el navegador pasa a QUIC en la siguiente vuelta. Dos condiciones: solo los routers con TLS se sirven en HTTP/3, y el cortafuegos debe dejar pasar el puerto 443 en UDP (ufw allow 443/udp). Si publica un puerto UDP distinto del del entrypoint, la opción http3.advertisedPort anuncia el correcto. Para comprobarlo, use un curl compilado con HTTP/3: curl --http3 -I https://app.example.com.
Venir de Traefik v2: lo que se rompe
La v3 mantiene compatible la configuración de enrutamiento v2, pero cambia la sintaxis por defecto de las reglas y se niega a arrancar con varias opciones retiradas. Para migrar sin reescribirlo todo de golpe, añada --core.defaultRuleSyntax=v2 en la configuración de instalación: sus reglas antiguas siguen funcionando, pasa los routers uno a uno a la sintaxis v3 y después retira la opción. Los logs de Traefik señalan cada opción obsoleta que siga en uso: léalos tras el primer arranque. Los cambios que más a menudo atrapan:
Cambios incompatibles de la v3
- Swarm: la opción
swarmModedel provider Docker ya no existe y bloquea el arranque. Docker Swarm tiene ahora su propio provider (--providers.swarm). - Reglas de enrutamiento:
HeadersyHeadersRegexppasan a serHeaderyHeaderRegexp,HostHeaderdesaparece en favor deHost, y la mayoría de los matchers solo aceptan ya un valor. - Rutas:
PathyPathPrefixya no leen expresiones regulares ni placeholders como{id}; una ruta así ya no coincide con nada. Pase aPathRegexp, que sigue la sintaxis regexp de Go, comoHostRegexp. - Middlewares renombrados o recortados:
IPWhiteListpasa a serIPAllowList(mismas opciones); el middlewareheaderspierdesslRedirect,sslTemporaryRedirect,sslHost,sslForceHostyfeaturePolicy. - Content-Type: Traefik ya no adivina el tipo de una respuesta que el backend no ha tipado; añada el middleware
ContentTypesi una aplicación depende de ello. - Providers y observabilidad retirados: Marathon, Rancher v1, Traefik Pilot y las métricas InfluxDB v1 impiden el arranque; el tracing pasa únicamente por OpenTelemetry (sin exportación directa a Jaeger, Zipkin o Datadog).
Traefik v3, Nginx o Caddy delante de sus contenedores
Desplace la tabla
| Criterio | Traefik v3 | Nginx | Caddy 2 |
|---|---|---|---|
| Configuración | Labels Docker, YAML o TOML, aplicados en caliente | Archivos vhost estáticos, `nginx -s reload` en cada cambio | Caddyfile o API JSON, recarga sin corte |
| HTTPS automático | Integrado (ACME: HTTP-01, TLS-ALPN-01, DNS-01) | Herramienta externa (certbot) y tarea de renovación | Integrado y activo por defecto |
| Descubrimiento de contenedores | Nativo, mediante el socket de Docker | Ausente: herramienta de terceros o edición manual | Mediante el módulo comunitario caddy-docker-proxy |
| HTTP/3 | Estable desde la v3.0 (abril de 2024), una opción por entrypoint | Experimental desde nginx 1.25.0 (mayo de 2023), configuración dedicada | Activo por defecto desde Caddy 2.6 (septiembre de 2022) |
| Curva de aprendizaje | Media: routers, servicios y middlewares que asimilar | Conocido por todos, pero HTTPS + Docker exigen herramientas | La más rápida para unos pocos sitios |
Resolución de problemas: los errores más frecuentes
La mayoría de los incidentes de Traefik vienen de cuatro fuentes: el DNS o el cortafuegos que impiden la validación de Let's Encrypt, el archivo acme.json, una red Docker que falta y labels mal escritos. Los síntomas y sus correcciones:
Síntomas y correcciones
- Certificado no obtenido, error ACME
unauthorizedo403en los logs: Let's Encrypt no alcanza su dominio. Compruebe desde fuera que el puerto 80 responde (curl -I http://app.example.com) y que el DNS apunta al VPS (dig +short app.example.com). Detrás de un CDN en modo proxy, prefiera eldnsChallenge. permissions 644 for /acme.json are too open, please use 600: el resolutor rechaza el archivo. Corríjalo conchmod 600 /opt/traefik/acme.jsony reinicie Traefik; este control protege las claves privadas que contiene.- Ruta visible en el dashboard pero
502 Bad Gateway: el contenedor no está en la redproxy, o el puerto interno es incorrecto. Compruébelo condocker inspect <contenedor>(secciónNetworks), añada la red o el labelloadbalancer.server.porty recree el contenedor. En un contenedor conectado a varias redes, el labeltraefik.docker.network=proxyelimina la ambigüedad. - Labels ignorados, ruta ausente del dashboard: falta
traefik.enable=true(obligatorio conexposedbydefault=false), o dos servicios declaran el mismo nombre de router. Los logs de Traefik señalan entonces el conflicto. - BasicAuth que rechaza una contraseña correcta: los
$del hash no están duplicados en eldocker-compose.yml. En un archivo Compose, cada$se convierte en$$; en un archivo de configuración dinámica YAML o TOML, el hash se pega tal cual.
Siguiente paso: endurecer el stack
Traefik resuelve el enrutamiento y el TLS, pero un stack listo para producción exige más: healthchecks, política de reinicio, rotación de logs, secretos fuera del archivo Compose y copias de seguridad de los volúmenes (incluido acme.json). Nuestro checklist de Docker Compose en producción repasa esos puntos uno por uno. Y si aún duda sobre la herramienta, la comparativa de Caddy, Traefik y Nginx Proxy Manager le ayuda a decidir según el número de servicios y la necesidad de descubrimiento automático.