Tutorial

Despliegue

Desplegar sus aplicaciones con Traefik en un VPS

Traefik es un reverse proxy y balanceador de carga cloud-native que descubre sus contenedores Docker automáticamente y les asigna rutas y certificados SSL sin recarga manual. Desplegado en su VPS, se convierte en la puerta de entrada única de todas sus aplicaciones, con una configuración declarativa mediante labels Docker. Esta guía instala la rama v3 (v3.7 en el momento de escribir), con HTTPS Let's Encrypt, dashboard protegido, HTTP/3, middlewares de producción y las trampas que conviene conocer si viene de Traefik v2.

11 min de lectura7 pasos
Contenido· ¿Por qué poner Traefik al frente de su VPS?1/13
  1. 01¿Por qué poner Traefik al frente de su VPS?
  2. 02Lo que Traefik aporta en concreto en un VPS
  3. 03Requisitos previos
  4. 04Desplegar Traefik con Docker Compose
  5. 05Middlewares útiles en producción
  6. 06Cinco middlewares que conviene conocer
  7. 07Activar HTTP/3 (QUIC)
  8. 08Venir de Traefik v2: lo que se rompe
  9. 09Cambios incompatibles de la v3
  10. 10Traefik v3, Nginx o Caddy delante de sus contenedores
  11. 11Resolución de problemas: los errores más frecuentes
  12. 12Síntomas y correcciones
  13. 13Siguiente paso: endurecer el stack

¿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

  1. 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.json

    Cree 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.

  2. Configurar los entrypoints y el resolutor ACME

    Coloque este archivo en /opt/traefik/docker-compose.yml. Declara dos entrypoints (web en :80, websecure en :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: true

    La opción exposedbydefault=false es esencial: sin ella, cada contenedor del VPS quedaría publicado, incluida su base de datos. Fije la rama (v3.7) en lugar de latest y siga las releases antes de cambiar de versión menor.

  3. 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=websecure y ...entrypoint.scheme=https ya 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.

  4. Asegurar el dashboard

    El dashboard se expone en traefik.example.com, detrás del middleware basicauth llamado dashboard-auth. Genere un hash bcrypt (paquete apache2-utils en 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=true en producción: publica el dashboard sin autenticación.

  5. Lanzar Traefik

    Arranque el stack y siga los logs:

    cd /opt/traefik
    docker compose up -d
    docker compose logs -f traefik

    Debe 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íe acme.json antes de pasar a producción.

  6. Conectar una aplicación

    Cada aplicación vive en su propio docker-compose.yml, conectada a la red proxy y 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: true

    Al 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 label loadbalancer.server.port indica el puerto interno de la aplicación; se vuelve imprescindible en cuanto la imagen expone varios. El nombre del router (app aquí) debe ser único en todo el VPS.

  7. 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.com

    La primera petición debe devolver una redirección permanente hacia https://, la segunda un 200 (o el código de su aplicación) servido con un certificado Let's Encrypt válido, y dig debe 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és traefik.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=1s y ...ratelimit.burst=200 permiten 100 peticiones por segundo de media, con ráfagas de hasta 200. Sin average, el límite está desactivado.
  • Cabeceras de seguridad: el middleware headers añade HSTS (stsSeconds=31536000, stsIncludeSubdomains=true), X-Frame-Options: DENY (frameDeny=true) y X-Content-Type-Options: nosniff (contentTypeNosniff=true), sin archivo de configuración aparte.
  • Redirección de www al dominio desnudo: redirectregex con regex=^https://www\.(.+), replacement=https://$${1} y permanent=true. Recuerde declarar los dos hosts en la regla del router para que el certificado cubra www.
  • Compresión: traefik.http.middlewares.compress.compress=true activa 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 swarmMode del provider Docker ya no existe y bloquea el arranque. Docker Swarm tiene ahora su propio provider (--providers.swarm).
  • Reglas de enrutamiento: Headers y HeadersRegexp pasan a ser Header y HeaderRegexp, HostHeader desaparece en favor de Host, y la mayoría de los matchers solo aceptan ya un valor.
  • Rutas: Path y PathPrefix ya no leen expresiones regulares ni placeholders como {id}; una ruta así ya no coincide con nada. Pase a PathRegexp, que sigue la sintaxis regexp de Go, como HostRegexp.
  • Middlewares renombrados o recortados: IPWhiteList pasa a ser IPAllowList (mismas opciones); el middleware headers pierde sslRedirect, sslTemporaryRedirect, sslHost, sslForceHost y featurePolicy.
  • Content-Type: Traefik ya no adivina el tipo de una respuesta que el backend no ha tipado; añada el middleware ContentType si 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

CriterioTraefik v3NginxCaddy 2
ConfiguraciónLabels Docker, YAML o TOML, aplicados en calienteArchivos vhost estáticos, `nginx -s reload` en cada cambioCaddyfile o API JSON, recarga sin corte
HTTPS automáticoIntegrado (ACME: HTTP-01, TLS-ALPN-01, DNS-01)Herramienta externa (certbot) y tarea de renovaciónIntegrado y activo por defecto
Descubrimiento de contenedoresNativo, mediante el socket de DockerAusente: herramienta de terceros o edición manualMediante el módulo comunitario caddy-docker-proxy
HTTP/3Estable desde la v3.0 (abril de 2024), una opción por entrypointExperimental desde nginx 1.25.0 (mayo de 2023), configuración dedicadaActivo por defecto desde Caddy 2.6 (septiembre de 2022)
Curva de aprendizajeMedia: routers, servicios y middlewares que asimilarConocido por todos, pero HTTPS + Docker exigen herramientasLa 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 unauthorized o 403 en 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 el dnsChallenge.
  • permissions 644 for /acme.json are too open, please use 600: el resolutor rechaza el archivo. Corríjalo con chmod 600 /opt/traefik/acme.json y 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 red proxy, o el puerto interno es incorrecto. Compruébelo con docker inspect <contenedor> (sección Networks), añada la red o el label loadbalancer.server.port y recree el contenedor. En un contenedor conectado a varias redes, el label traefik.docker.network=proxy elimina la ambigüedad.
  • Labels ignorados, ruta ausente del dashboard: falta traefik.enable=true (obligatorio con exposedbydefault=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 el docker-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.

Lance Traefik en unos minutos en un VPS Cloud ServOrbit

Pida un VPS Cloud con Docker preinstalado y despliegue su stack Traefik a partir de la plantilla Despliegue. Sus aplicaciones se enrutan por HTTPS automáticamente.