← SCRAM AI Lab

Tutoriales

Traefik v2.10 con auto-renewal certs para 94 containers

Wildcard *.scram2k.com cubre la mayoría, certs individuales para el resto. acme.json shared, DNS-01 para wildcards, HTTP-01 para subdomains. Anti-patrón: cert por container.

May 21, 2026

437 lecturas

Traefik v2.10 con auto-renewal certs para 94 containers

Pedir un cert por container es como pedirle a Let's Encrypt que te tire

Las Let's Encrypt rate limits son 50 certs por dominio registrado por semana y 5 duplicados por cert por semana (documentadas oficialmente en la guía de límites de Let's Encrypt). Si tienes 94 containers y cada uno pide su propio cert, vas a pegar el throttle el segundo día y vas a tener 30+ servicios sin HTTPS hasta que la ventana resetee. El patrón correcto en SCRAM: un wildcard *.scram2k.com cubre 80+ subdominios, y los dominios fuera del wildcard tienen cert individual con HTTP-01.

¿Por qué conviene usar certificados wildcard frente a certificados individuales en Docker?

Para evitar bloqueos por límites de peticiones al operar decenas de microservicios, la estrategia consiste en consolidar los subdominios bajo un único certificado wildcard resuelto mediante DNS-01. Esta arquitectura reduce decenas de emisiones semanales a una sola renovación cada 90 días, blindando la disponibilidad y reservando HTTP-01 solo para dominios ajenos al árbol principal.

Configuración base de Traefik

# traefik.yml
api:
  dashboard: true

entryPoints:
  web:
    address: ":80"
    http:
      redirections:
        entryPoint:
          to: websecure
          scheme: https
  websecure:
    address: ":443"

certificatesResolvers:
  letsencrypt-wildcard:
    acme:
      email: [email protected]
      storage: /letsencrypt/acme-wildcard.json
      dnsChallenge:
        provider: cloudflare
        resolvers:
          - "1.1.1.1:53"
          - "8.8.8.8:53"

  letsencrypt-http:
    acme:
      email: [email protected]
      storage: /letsencrypt/acme-http.json
      httpChallenge:
        entryPoint: web

providers:
  docker:
    exposedByDefault: false
    network: traefik-public

El truco del wildcard

El wildcard *.scram2k.com se pide una sola vez cada 90 días. Cubre api.scram2k.com, app.scram2k.com, soporte.scram2k.com, y cualquier subdominio nuevo que crees sin necesidad de pedir cert. Para que funcione necesitas DNS-01 challenge (HTTP-01 no soporta wildcards) y eso requiere API access a tu DNS provider — en SCRAM usamos Cloudflare.

Labels en containers de servicios

# servicio que usa el wildcard
services:
  scram-api:
    image: scram-api:latest
    networks: [traefik-public]
    labels:
      - traefik.enable=true
      - traefik.http.routers.api.rule=Host(`api.scram2k.com`)
      - traefik.http.routers.api.tls.certresolver=letsencrypt-wildcard
      - traefik.http.services.api.loadbalancer.server.port=3000

  awalab-app:
    image: awalab-app:latest
    networks: [traefik-public]
    labels:
      - traefik.enable=true
      - traefik.http.routers.awalab.rule=Host(`app.awalab.mx`)
      - traefik.http.routers.awalab.tls.certresolver=letsencrypt-http
      - traefik.http.services.awalab.loadbalancer.server.port=80

acme.json: el archivo que no debe perderse

El archivo /letsencrypt/acme-wildcard.json contiene la private key y los certs emitidos. Reglas:

  • Permisos 600 (Traefik se queja si es más abierto y no inicia)
  • Backup diario a GCS bucket — perder este archivo significa re-emitir todos los certs (rate limit hit)
  • Volume persistente, no efímero
  • No commitearlo NUNCA al repo

El anti-patrón clásico

Equipo que recogimos como cliente tenía 47 containers, cada uno pidiendo su propio cert vía HTTP-01. Al desplegar un fix que reinició Traefik en mal momento, Traefik intentó renovar 12 certs simultáneamente y pegó el rate limit de "certificados duplicados". 9 dominios sin HTTPS por 6 días hasta que la ventana abrió. Solución permanente fue moverlo a 2 wildcards y dejar HTTP-01 solo para 8 dominios externos.

Comparativa de arquitecturas de terminación TLS

Para infraestructuras medianas de 50 a 150 contenedores, evaluar la herramienta de ingreso define la carga operativa del equipo de infraestructura. A continuación se comparan los enfoques habituales en producción:

Criterio Traefik v2.10 (Wildcard DNS-01) Nginx + Certbot Standalone Kubernetes + Cert-Manager
Consumo inicial de memoria Bajo (~45 MB en reposo según métricas de Traefik Labs) Mínimo (~15 MB Nginx, picos de 60 MB al invocar Certbot) Alto (controladores requieren >250 MB continuos)
Descubrimiento de servicios Automático vía Docker labels en tiempo real Manual o mediante recarga de plantillas con scripts Nativo vía Ingress / Gateway API
Emisión Wildcard Nativa en un solo binario Requiere plugins específicos de Certbot por proveedor Soportada mediante Custom Resource Definitions (CRDs)
Riesgo de rate limit Mínimo (1 solicitud cada 60-90 días) Alto si los cronjobs no agrupan subdominios Bajo si el ClusterIssuer está bien aislado

Qué puede salir mal y cómo mitigarlo

El despliegue con DNS-01 no está libre de incidentes. Los fallos más comunes identificados en producción involucran tres vectores:

1. Límites y expiración de API Tokens de Cloudflare

De acuerdo con la documentación de Cloudflare, la API estándar restringe a 1,200 peticiones cada 5 minutos por usuario. Si bien Traefik genera apenas un puñado de peticiones TXT por renovación, tokens con alcances mal configurados o caducados provocarán que el challenge falle en silencio. Es obligatorio acotar el token al permiso exclusivo Zone:DNS:Edit sobre la zona específica, evitando el uso de Global API Keys.

2. Propagación DNS lenta y Timeouts de ACME

Cuando Traefik crea el registro _acme-challenge, los servidores de Let's Encrypt deben resolverlo antes de validar la propiedad. Si los resolutores recursivos locales tardan en responder, el proceso aborta. La directiva resolvers en la sección dnsChallenge (apuntando a 1.1.1.1:53 y 8.8.8.8:53) fuerza a Traefik a consultar resolutores de baja latencia en vez del resolv.conf del host local.

3. Corrupción de archivo por escritura concurrente

Si dos instancias de Traefik montan el mismo archivo acme.json sin un backend de almacenamiento distribuido (función exclusiva de Traefik Enterprise), el JSON se corrompe. En Docker Standalone o Docker Compose tradicional, solo una instancia de Traefik debe tener acceso de escritura a ese archivo.

Consideraciones para despliegues en América Latina

En despliegues situados en centros de datos regionales (como Querétaro, Bogotá o São Paulo), la latencia hacia resolutores DNS autoritativos externos puede introducir timeouts esporádicos durante la validación del challenge. Configurar resolutores DNS Anycast de alta disponibilidad reduce fallos de handshake. Asimismo, delegar la terminación TLS en Traefik dentro de la misma máquina virtual mitiga el impacto de saltos de red adicionales en enlaces internacionales con jitter variable.

Monitoring de expiración

Una alerta en Grafana sobre los certs que expiran en menos de 21 días. Traefik los renueva automáticamente a los 30, pero si la renovación falla (DNS rate limit, Cloudflare API down) quieres saberlo con tiempo.

La pregunta sin respuesta clara aún: ¿migrar a cert-manager + Kubernetes amerita la complejidad para 94 containers en una sola VM? Spoiler: no hasta que sean 300+ containers en 3+ VMs. Mientras tanto, Traefik con wildcard es el sweet spot.

traefik
ssl
infra
← Volver a SCRAM AI Lab