← 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

14 lecturas

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

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.

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