Ubuntu 24.04: Certificados renovados pero Nginx sigue sirviendo el antiguo — por qué y cómo solucionarlo

¿Te fue útil?

Ejecutaste certbot renew. Indicó “éxito”. Tu monitorización sigue gritando “el certificado expira en 2 días”.
Compruebas el sistema de archivos y el certificado nuevo está ahí. Aun así, los navegadores siguen viendo el antiguo—a veces desde la misma máquina.

Esto no es un misterio. Son modos de fallo muy previsibles: ruta equivocada, bloque de servidor incorrecto, no se recargó,
un balanceador de carga que hace TLS delante, o un proceso obsoleto que nunca reabrió los ficheros. En Ubuntu 24.04 los valores por defecto
son sensatos, pero el ecosistema alrededor (Snap, systemd, varias instancias de Nginx, contenedores) facilita equivocarse con seguridad.

Qué ocurre realmente cuando “renovado” ≠ “servido”

Hay dos sistemas separados en juego:

  • Emisión/renovación de certificados: un cliente ACME (normalmente Certbot, a veces lego/acme.sh) escribe nuevos archivos en el disco.
  • Servicio TLS: Nginx lee los archivos de certificado en memoria cuando carga la configuración (inicio o recarga). No “ve” mágicamente los nuevos bytes en disco.

Así que el modo de fallo es sencillo: la renovación tuvo éxito, pero la pila de servicio no conmutó.
La parte complicada es que “la pila de servicio” puede no ser el proceso que crees. Puede ser:
Nginx en el host, Nginx en un contenedor, Nginx en un chroot, una segunda instancia de Nginx enlazada al 443,
un proxy inverso upstream, o un balanceador de carga en la nube que termina TLS antes de que el tráfico llegue a Nginx.

Una matización más: a veces Nginx se recarga correctamente, pero aún sirve el certificado “equivocado” porque la selección SNI elige un bloque de servidor distinto al esperado.
Esto es común cuando tienes un servidor por defecto, certificados comodín, o un bloque catch-all dejado por una herramienta de automatización.

Tu objetivo no es “renovar el certificado”. Eso es la parte fácil. Tu objetivo es: probar qué proceso responde TLS para un nombre de host dado, y probar qué archivos cargó.

Broma #1: Los certificados son como la leche—la frescura importa, pero nadie quiere ser la persona oliéndolos en producción a las 2 a.m.

Guion rápido de diagnóstico (primero/segundo/tercero)

Primero: confirma qué ven realmente los clientes

  1. Desde una máquina fuera de tu red (o usando una sonda pública), comprueba el certificado presentado para el nombre de host exacto.
  2. Registra emisor, número de serie y fechas notBefore/notAfter.
  3. Si es viejo, no toques Certbot todavía. El servidor está sirviendo bytes antiguos; la renovación puede ya estar bien.

Segundo: identifica quién termina TLS

  1. ¿Es el puerto 443 del host realmente Nginx?
  2. ¿Hay un balanceador de carga, CDN, WAF o controlador de ingreso que haga TLS antes de Nginx?
  3. ¿Hay más de un Nginx (host + contenedor)?

Tercero: correlaciona config Nginx → ruta de certificado → archivo en disco → proceso cargado

  1. Encuentra el bloque server que coincida con el hostname SNI.
  2. Verifica las rutas ssl_certificate y ssl_certificate_key.
  3. Comprueba si son enlaces simbólicos de Let’s Encrypt en live/ o están apuntando al archive/ fijado.
  4. Recarga Nginx y vuelve a comprobar desde el exterior.
  5. Si la recarga no cambia lo servido, estás recargando el Nginx equivocado o la configuración equivocada.

Ese es el orden de búsqueda de cuellos de botella. Empieza por lo que ven los usuarios y traza hacia dentro. No empieces por seguir los logs de Certbot.
Así es como pasas una hora demostrando lo incorrecto.

Hechos y contexto interesantes (por qué vuelve a ocurrir este problema)

  • Hecho 1: Nginx no vuelve a leer automáticamente los archivos de certificado en disco. Los carga al arrancar y al reload (recarga de configuración suave).
  • Hecho 2: Los certificados de Let’s Encrypt son de corta duración (típicamente 90 días), por diseño, para reducir el impacto de la exposición de claves.
  • Hecho 3: Certbot mantiene dos directorios: /etc/letsencrypt/archive (archivos versionados) y /etc/letsencrypt/live (enlaces simbólicos al “actual”).
  • Hecho 4: Un fallo sorprendentemente común es “renovación exitosa, pero la recarga falló.” El log de renovación dice que todo bien; Nginx siguió sirviendo el certificado antiguo durante días.
  • Hecho 5: En la era temprana de TLS, algunos servidores requerían reinicios completos para cambios de certificado; la recarga suave se volvió una característica de fiabilidad práctica, no sólo comodidad.
  • Hecho 6: SNI (Server Name Indication) permite que una sola IP sirva múltiples certificados. Si la selección SNI cae en el bloque de servidor equivocado, obtienes el certificado equivocado aunque el correcto exista.
  • Hecho 7: La adopción por parte de Ubuntu del empaquetado Snap para Certbot cambió ubicaciones de archivos para logs y a veces cómo se ejecutan los hooks de renovación, especialmente al mezclar instalaciones apt y snap.
  • Hecho 8: OCSP stapling puede confundir la depuración: los clientes pueden cachear o mostrar el estado de stapling por separado de la cadena de certificados, así que “parece incorrecto” no siempre es por el archivo del certificado.

Causas principales: de dónde viene realmente el certificado antiguo

1) Nginx nunca se recargó (o la recarga falló)

Certbot puede renovar sin reiniciar nada. Nginx sigue ejecutándose, felizmente sirviendo lo que cargó la semana pasada.
Si tienes un deploy hook que debería recargar Nginx, puede que no esté configurado, que no se ejecute o que falle.
Peor aún: nginx -s reload puede terminar con éxito pero estar recargando un binario o una instancia diferente a la que está vinculada al 443.

2) Nginx apunta a la ruta de archivo equivocada

El patrón correcto es referenciar /etc/letsencrypt/live/yourname/fullchain.pem y privkey.pem.
Si alguien codificó de forma rígida /etc/letsencrypt/archive/yourname/fullchain2.pem, nunca avanzará automáticamente.
Funcionará hasta que deje de hacerlo. Este es el clásico “funciona hasta la siguiente rotación”.

3) Se selecciona el bloque de servidor equivocado (coincidencia SNI)

Puede que tengas el certificado correcto en un bloque de servidor, y un bloque servidor por defecto con un certificado obsoleto.
Los clientes que acceden a example.com obtienen el certificado correcto; los que acceden a www.example.com o un hostname de API obtienen el por defecto.
O tu comprobador de estado usa una dirección IP y no SNI, por lo que siempre golpea el por defecto. Entonces “arreglas” lo incorrecto.

4) TLS se termina en otro lugar

Si usas un balanceador de carga en la nube, CDN, proxy inverso o ingreso de Kubernetes, Nginx puede no servir TLS en absoluto.
La renovación en el backend es irrelevante; la puerta de entrada sigue mostrando el certificado antiguo.
Los incidentes más costosos son los que hacen que todo el mundo depure la capa equivocada con gran entusiasmo.

5) Múltiples instancias de Nginx, contenedores o puertos

Ubuntu 24.04 es perfectamente capaz de ejecutar:
Nginx en el host (systemd), Nginx en Docker (compose), y un controlador de ingreso (Kubernetes).
Si dos procesos escuchan en 443 en distintas interfaces de red (o uno en la red del host, otro detrás de NAT), puedes “recargar” el equivocado todo el día.

6) Permisos o cambios de formato de clave

Certbot puede renovar pero escribir claves con permisos que Nginx no puede leer (menos común con las rutas estándar de Let’s Encrypt, más común con hooks personalizados o archivos copiados).
O rotaste de RSA a ECDSA, cambiaste nombres de archivo y olvidaste actualizar la configuración de Nginx. Entonces la recarga falla y Nginx mantiene la config antigua en memoria.

Tareas prácticas: comandos, salidas y decisiones (12+)

Estas son las comprobaciones que realmente ejecuto cuando la producción dice “certificado antiguo”. Cada tarea incluye:
el comando, qué significa la salida y la decisión que tomas a partir de ello.

Task 1: Check the served certificate from the outside (SNI on)

cr0x@server:~$ openssl s_client -connect example.com:443 -servername example.com -showcerts </dev/null 2>/dev/null | openssl x509 -noout -subject -issuer -dates -serial
subject=CN = example.com
issuer=C = US, O = Let's Encrypt, CN = R11
notBefore=Dec  1 00:12:34 2025 GMT
notAfter=Feb 29 00:12:33 2026 GMT
serial=03A1B2C3D4E5F6

Significado: Esto es lo que ven los clientes. La fecha notAfter te dice si es antiguo.

Decisión: Si sigue siendo la expiración antigua, continúa. Si es nueva, tu monitorización puede estar comprobando un hostname o endpoint diferente.

Task 2: Check served certificate without SNI (default server behavior)

cr0x@server:~$ openssl s_client -connect example.com:443 -showcerts </dev/null 2>/dev/null | openssl x509 -noout -subject -dates
subject=CN = default.invalid
notBefore=Sep  2 10:00:00 2025 GMT
notAfter=Dec  1 10:00:00 2025 GMT

Significado: Sin SNI, Nginx elige el servidor por defecto para esa IP:puerto.

Decisión: Si el certificado por defecto está obsoleto, arregla el bloque por defecto también, o configura los clientes/comprobadores para enviar SNI.

Task 3: Confirm which process is listening on 443

cr0x@server:~$ sudo ss -ltnp | grep ':443 '
LISTEN 0      511          0.0.0.0:443       0.0.0.0:*    users:(("nginx",pid=2147,fd=6),("nginx",pid=2146,fd=6))

Significado: Ves qué binario posee el socket.

Decisión: Si no es Nginx (o no es el que crees), para y persigue ese proceso. No recargues lo equivocado.

Task 4: Ensure you’re reloading the same Nginx that’s running

cr0x@server:~$ ps -fp 2147
UID          PID    PPID  C STIME TTY          TIME CMD
root        2147       1  0 Dec28 ?        00:00:02 nginx: master process /usr/sbin/nginx -g daemon on; master_process on;

Significado: Confirma la ruta (/usr/sbin/nginx) y que systemd probablemente lo controla.

Decisión: Usa systemctl reload nginx para esta instancia; no llames a otro binario nginx desde una imagen de contenedor.

Task 5: Validate Nginx configuration before reloading

cr0x@server:~$ sudo nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

Significado: La recarga probablemente tendrá éxito. Si esto falla, Nginx se negará a cargar nuevas rutas/certificados.

Decisión: Arregla errores de configuración primero. Si nginx -t falla, la “recarga” no actualizará certificados.

Task 6: Reload Nginx and confirm systemd accepted it

cr0x@server:~$ sudo systemctl reload nginx
cr0x@server:~$ systemctl status nginx --no-pager -l
● nginx.service - A high performance web server and a reverse proxy server
     Loaded: loaded (/usr/lib/systemd/system/nginx.service; enabled; preset: enabled)
     Active: active (running) since Sun 2025-12-28 09:10:12 UTC; 1 day 3h ago
       Docs: man:nginx(8)
    Process: 33910 ExecReload=/usr/sbin/nginx -g 'daemon on; master_process on;' -s reload (code=exited, status=0/SUCCESS)

Significado: La recarga se ejecutó y salió con éxito.

Decisión: Vuelve a comprobar el certificado servido externamente. Si no cambió, estás mirando el terminador TLS equivocado o el bloque de servidor equivocado.

Task 7: Find which server block matches your hostname

cr0x@server:~$ sudo nginx -T 2>/dev/null | grep -nE 'server_name|listen 443|ssl_certificate'
415:    listen 443 ssl http2;
416:    server_name example.com www.example.com;
432:    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
433:    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
601:    listen 443 ssl;
602:    server_name _;
612:    ssl_certificate /etc/ssl/certs/old-default.pem;
613:    ssl_certificate_key /etc/ssl/private/old-default.key;

Significado: Tienes al menos dos bloques TLS: uno correcto y uno por defecto con un certificado antiguo.

Decisión: Actualiza el bloque por defecto (o elimínalo) si los clientes pueden golpearlo. También asegura que cada hostname esté cubierto con server_name explícito.

Task 8: Verify the live/ symlinks actually point to the newest files

cr0x@server:~$ sudo ls -l /etc/letsencrypt/live/example.com/
total 4
-rw-r--r-- 1 root root 692 Sep  2 10:00 README
lrwxrwxrwx 1 root root  42 Dec 28 09:05 cert.pem -> ../../archive/example.com/cert12.pem
lrwxrwxrwx 1 root root  43 Dec 28 09:05 chain.pem -> ../../archive/example.com/chain12.pem
lrwxrwxrwx 1 root root  47 Dec 28 09:05 fullchain.pem -> ../../archive/example.com/fullchain12.pem
lrwxrwxrwx 1 root root  45 Dec 28 09:05 privkey.pem -> ../../archive/example.com/privkey12.pem

Significado: Nginx debería referenciar estos enlaces simbólicos. Certbot rota sus destinos.

Decisión: Si Nginx apunta directamente a archive/, cámbialo a live/ para que futuras renovaciones no requieran ediciones manuales.

Task 9: Inspect on-disk cert dates (what renewal actually produced)

cr0x@server:~$ sudo openssl x509 -in /etc/letsencrypt/live/example.com/fullchain.pem -noout -dates -issuer -subject
notBefore=Dec 28 09:05:11 2025 GMT
notAfter=Mar 28 09:05:10 2026 GMT
issuer=C = US, O = Let's Encrypt, CN = R11
subject=CN = example.com

Significado: El archivo en disco es nuevo.

Decisión: Si el disco es nuevo pero lo servido es antiguo, el problema es recarga/SNI/terminador TLS, no la renovación.

Task 10: Check Certbot renewal history and whether a deploy hook ran

cr0x@server:~$ sudo grep -R "Deploying certificate" -n /var/log/letsencrypt/letsencrypt.log | tail -n 3
2025-12-28 09:05:12,345:INFO:Deploying certificate to /etc/letsencrypt/live/example.com/fullchain.pem
2025-12-28 09:05:12,346:INFO:Deploying key to /etc/letsencrypt/live/example.com/privkey.pem
2025-12-28 09:05:12,900:INFO:Running deploy-hook command: systemctl reload nginx

Significado: Certbot renovó e intentó recargar.

Decisión: Si no hay línea de deploy-hook, añade una. Si existe, verifica que tuvo éxito en el journal del sistema.

Task 11: Verify the Certbot timer (Ubuntu 24.04 systemd scheduling)

cr0x@server:~$ systemctl list-timers --all | grep -E 'certbot|letsencrypt'
Sun 2025-12-29 06:18:00 UTC  10h left  Sat 2025-12-28 06:18:01 UTC  13h ago  snap.certbot.renew.timer  snap.certbot.renew.service

Significado: La renovación está programada (aquí vía la unidad Snap).

Decisión: Si no existe un timer, la renovación no ocurrirá automáticamente. Si existe pero usaste apt certbot, puedes tener dos configuraciones en conflicto—elige una.

Task 12: Confirm which Certbot you’re using (Snap vs apt) to avoid split-brain

cr0x@server:~$ which certbot
/snap/bin/certbot
cr0x@server:~$ snap list certbot
Name    Version   Rev  Tracking       Publisher  Notes
certbot 2.11.0    3741 latest/stable  certbot*   classic

Significado: Estás usando Certbot desde Snap.

Decisión: Asegúrate de que cualquier hook, log y expectativa coincidan con el empaquetado Snap. Evita mezclar con un Certbot instalado por apt a menos que disfrutes de arqueología forense.

Task 13: Find out if you’re behind a load balancer or proxy doing TLS

cr0x@server:~$ curl -sI https://example.com | grep -iE 'server:|via:|x-forwarded|cf-|x-amz'
server: cloud-proxy
via: 1.1 edge-gw
x-forwarded-proto: https

Significado: Los encabezados sugieren una capa proxy. No es prueba, pero huele fuerte.

Decisión: Revisa tu DNS y ruta de red. Si TLS se termina aguas arriba, actualiza el certificado allí, no en Nginx.

Task 14: Check Nginx error logs for reload/cert read failures

cr0x@server:~$ sudo tail -n 30 /var/log/nginx/error.log
2025/12/28 09:05:13 [notice] 2147#2147: signal process started
2025/12/28 09:05:13 [emerg] 2147#2147: cannot load certificate "/etc/letsencrypt/live/example.com/fullchain.pem": BIO_new_file() failed (SSL: error:80000002:system library::No such file or directory)

Significado: La recarga intentó pero falló al cargar el archivo. Nginx mantendrá la config antigua activa.

Decisión: Arregla la ruta del archivo, permisos o restricciones SELinux/AppArmor (AppArmor es más común en Ubuntu). Luego recarga de nuevo.

Task 15: Prove which certificate file the running Nginx worker has open

cr0x@server:~$ sudo lsof -p 2147 | grep -E 'fullchain|cert\.pem|privkey' | head
nginx 2147 root  mem REG  252,0   4231  131072 /etc/letsencrypt/live/example.com/fullchain.pem
nginx 2147 root  mem REG  252,0   1704  131073 /etc/letsencrypt/live/example.com/privkey.pem

Significado: Esto es lo más cercano a “qué cargó Nginx” desde la vista del SO.

Decisión: Si aún apunta a un archivo antiguo de archive o a otro dominio, arregla la config y recarga. Si está correcto pero los clientes ven lo antiguo, la terminación TLS está en otro lugar.

Task 16: Confirm your hostname resolves to the box you’re debugging

cr0x@server:~$ dig +short A example.com
203.0.113.10
cr0x@server:~$ ip -brief address show | awk '{print $1,$3}'
lo 127.0.0.1/8
eth0 203.0.113.10/24

Significado: El registro DNS A coincide con la IP del host.

Decisión: Si DNS apunta a otro sitio (o estás detrás de anycast/LB), estás depurando la máquina equivocada. Esto ocurre más a menudo de lo que la gente admite.

Tres mini-historias del mundo corporativo

Mini-historia 1: El incidente causado por una suposición equivocada

Una compañía mediana tenía una configuración “simple”: una VM, un Nginx, un dominio. O eso creía todo el mundo.
Renovaron un certificado de Let’s Encrypt en Ubuntu, recargaron Nginx, y siguieron recibiendo alertas de expiración.
El ingeniero de guardia asumió que la monitorización estaba equivocada porque certbot certificates mostraba una nueva expiración.

El navegador de su portátil mostró también el certificado antiguo. Luego se volvió más raro: un compañero en otro ISP vio el nuevo.
Esa diferencia provocó la primera pregunta correcta: “¿Estamos detrás de una red perimetral?”
Resultó que marketing había activado recientemente una función gestionada de CDN/WAF en el panel del proveedor de DNS.
TLS se terminaba en el borde con un certificado subido que nadie había rotado.

La “suposición equivocada” no fue incompetencia técnica; fue organizativa. Todos asumieron que el diagrama de arquitectura del año pasado seguía siendo válido.
En la práctica, el sistema evolucionó sin una sola solicitud de cambio a Nginx.
Renovar el certificado del backend no hizo nada porque el backend ya no era el punto de terminación TLS.

La solución fue aburrida: rotar el certificado donde se termina TLS, documentarlo y añadir una sonda que compruebe el certificado desde múltiples redes.
La prevención fue aún más aburrida: una revisión semanal de “qué cambió en nuestras configuraciones de borde”.
A nadie le encanta, pero evita aprender sobre cambios de arquitectura desde un pager.

Mini-historia 2: La optimización que salió mal

Otro equipo quería “reducir recargas” en una flota Nginx muy cargada. Compartían la creencia de que recargar Nginx demasiado a menudo provocaría picos de latencia.
Deshabilitaron hooks automáticos de recarga tras la renovación y planearon recargar en una ventana de mantenimiento semanal.
La idea sonaba razonable: menos recargas, menos piezas móviles.

Dos meses después, toparon con el borde predecible: la renovación tuvo éxito varias veces, pero los procesos en ejecución nunca se recargaron.
Algunos servidores se reiniciaron por razones no relacionadas y sirvieron certificados nuevos; otros no y sirvieron certificados obsoletos.
La flota se volvió un patchwork de fechas de expiración. La monitorización pasó de una señal limpia a ruido.

El dolor operativo vino de la ambigüedad. Cuando un hostname empezó a fallar en TLS para un subconjunto de usuarios, los ingenieros no sabían si era un problema de red,
un nodo concreto o caché del cliente. Pasaron horas recogiendo evidencia que apuntaba a una sola frase: “nuestra optimización creó deriva de estado”.

Revirtieron el cambio: restauraron los deploy hooks, mantuvieron las recargas automáticas y midieron el impacto en lugar de asumirlo.
Si se ejecutan bien, las recargas de Nginx son suaves. No cortan conexiones; arrancan nuevos workers y drenan los antiguos.
Lo que falló no fue que las recargas sean inherentemente peligrosas, sino tratar la consistencia operativa como opcional.

Mini-historia 3: La práctica aburrida pero correcta que salvó el día

Una empresa regulada ejecutaba Nginx en Ubuntu con un proceso de cambios estricto. Todos se quejaban de las listas de verificación.
Pero tenían una práctica que parecía burocracia y actuaba como seguro: un trabajo de verificación post-renovación.
Tras cada renovación, un job se ejecutaba desde fuera de la red y registraba el número de serie y la expiración del certificado servido en un log central.

Una mañana el job marcó un único hostname VIP que seguía sirviendo un certificado antiguo. Todo lo demás parecía sano.
El equipo no entró en pánico; tenían un plan. Primera comprobación: SNI. Segunda: servidor por defecto. Tercera: listeners del balanceador de carga.
Encontraron un listener legado en el balanceador que aún usaba un certificado subido en lugar del gestionado.

La corrección llevó minutos porque la evidencia fue inmediata y específica: “este listener presenta el serial X; se esperaba el serial Y.”
Sin debate, sin arqueología de capturas, sin “funciona en mi laptop”.
Una solicitud de cambio se aprobó rápido porque fue una sustitución directa, no cirugía exploratoria.

La moraleja es agresivamente poco romántica: verifica lo que ven los usuarios, automáticamente, después de cada rotación.
Es la práctica que hace que los ingenieros se sientan poco valorados—hasta que evita un incidente.

Errores comunes: síntoma → causa raíz → solución

1) “Certbot dice renovado, pero los navegadores siguen mostrando la antigua expiración”

Causa raíz: Nginx no se recargó, o la recarga falló.

Solución: Ejecuta sudo nginx -t; luego sudo systemctl reload nginx. Revisa systemctl status nginx y /var/log/nginx/error.log. Añade un deploy hook para que esto ocurra automáticamente.

2) “El certificado en disco es nuevo, pero lo servido es antiguo—solo para algunos hostnames”

Causa raíz: Coincidencia SNI/bloque de servidor. Un servidor por defecto u otro server_name captura tráfico.

Solución: Usa openssl s_client -servername para cada hostname. Inspecciona nginx -T para listen 443 y server_name. Asegura que cada hostname mapee al certificado correcto.

3) “Los health checks dicen que el certificado es antiguo, pero los usuarios normales están bien”

Causa raíz: El comprobador no envía SNI y golpea el servidor/certificado por defecto.

Solución: Configura el comprobador para usar el hostname (SNI) o arregla el certificado del servidor por defecto a uno válido para ese endpoint IP.

4) “La recarga tiene éxito pero nada cambia”

Causa raíz: Recargaste un Nginx distinto del que sirve 443, o TLS se termina aguas arriba.

Solución: Confirma la propiedad del socket con ss -ltnp. Confirma que DNS apunta a esta máquina. Si existe un LB/CDN, rota el certificado allí.

5) “Después de la renovación, Nginx no recarga; sigue sirviendo el certificado antiguo”

Causa raíz: Los archivos nuevos faltan o no son legibles por permisos, errores en la ruta, o referencias de archivo incorrectas (p. ej. referenciar chain.pem en vez de fullchain.pem en algunas configuraciones).

Solución: Revisa /var/log/nginx/error.log por cannot load certificate. Arregla rutas, permisos y referencia fullchain.pem. Reejecuta nginx -t.

6) “Solo algunos clientes ven el certificado antiguo; otros ven el nuevo”

Causa raíz: Múltiples nodos de borde / múltiples registros A / anycast / pool de balanceador de carga no homogéneo.

Solución: Resuelve DNS desde varios lugares; revisa cada nodo backend individualmente; asegura que renovación y recarga ocurran en todas partes; corrige la deriva.

7) “Actualizamos la config de Nginx a la nueva ruta de certificado, pero luego se revirtió”

Causa raíz: Gestión de configuración o el instalador Nginx de Certbot sobrescribió archivos.

Solución: Gestiona las directivas TLS en tu fuente de la verdad (Ansible, Puppet, etc.) y evita ediciones manuales en fragmentos generados sin propiedad.

Broma #2: Nada construye alineación de equipo como un certificado que caduca—de repente todos recuerdan quién administra DNS.

Listas de verificación / plan paso a paso

Paso a paso: arregla la situación “renovado pero aún antiguo” de forma segura

  1. Prueba el síntoma externamente. Usa openssl s_client -servername y registra expiración + serial.
  2. Prueba quién posee :443. Usa ss -ltnp. Si no es Nginx, para y reencamina el ticket correctamente.
  3. Prueba que DNS apunte aquí. Usa dig +short y compáralo con IPs locales.
  4. Inspecciona la selección de config de Nginx. Usa nginx -T, encuentra el server_name correcto e identifica las rutas ssl_certificate.
  5. Asegura referenciar enlaces simbólicos live/ de Let’s Encrypt. Corrige cualquier hardcode a archive/.
  6. Valida la configuración. Ejecuta nginx -t. Sin validación, no recargues.
  7. Recarga Nginx. Usa systemctl reload nginx (o tu gestor) y confirma éxito en systemctl status.
  8. Vuelve a comprobar externamente. Mismo comando, mismo hostname, confirma cambio de serial/expiración.
  9. Si no cambia, busca terminación aguas arriba. Revisa listeners de LB/CDN y dónde se gestionan los certificados.
  10. Automatiza el siguiente evento. Añade un deploy hook y una comprobación de verificación externa post-renovación.

Lista de verificación: cómo se ve “bien” en Ubuntu 24.04

  • La configuración TLS de Nginx referencia /etc/letsencrypt/live/<name>/fullchain.pem y privkey.pem.
  • Existe exactamente una canalización de autoridad de certificados (Snap Certbot o tooling basado en apt), no ambas.
  • Un timer systemd ejecuta renovaciones regularmente y los logs muestran hooks de despliegue ejecutándose.
  • La recarga de Nginx se valida (nginx -t) y se monitoriza por fallos.
  • Una sonda externa verifica el certificado servido después de la renovación y alerta sobre discrepancias.

Lista de verificación: evita estos movimientos “ingeniosos”

  • No apuntes Nginx a archivos en /etc/letsencrypt/archive a menos que disfrutes rotaciones manuales.
  • No “optimices” quitando hooks de recarga. La deriva te encontrará.
  • No asumas que la VM sirve TLS sólo porque ejecuta Nginx.
  • No depures usando solo una captura de pantalla del navegador. Usa openssl y captura números de serie.

FAQ

1) ¿Por qué Nginx no recoge automáticamente certificados renovados?

Porque Nginx lee los archivos de certificado y clave cuando carga la configuración. La renovación cambia los archivos en disco, no el estado en memoria de Nginx.
Necesitas una recarga (o reinicio) para reabrir y parsear los archivos nuevos.

2) ¿Es seguro ejecutar systemctl reload nginx en producción?

Normalmente, sí. Una recarga está diseñada para ser suave: los nuevos workers arrancan con la nueva config, los antiguos drenan.
Aun así, ejecuta nginx -t primero; una config rota convierte lo “seguro” en un incidente autoinfligido.

3) ¿Cuál es la diferencia entre fullchain.pem y cert.pem?

cert.pem es sólo el certificado hoja. fullchain.pem incluye la hoja más los intermedios.
Muchos clientes requieren los intermedios para construir la cadena de confianza de forma fiable, por eso fullchain.pem es la elección estándar en Nginx.

4) ¿Por qué veo el nuevo certificado en disco pero uno viejo en la red?

O Nginx no se recargó, Nginx apunta a archivos distintos de los que inspeccionaste, o el endpoint TLS no es Nginx (balanceador/CDN).
Trátalo como un problema de enrutamiento/propiedad hasta que se demuestre lo contrario.

5) Mi monitorización dice “certificado expira pronto”, pero openssl s_client muestra un certificado fresco. ¿Quién miente?

Posiblemente ninguno. La monitorización puede estar comprobando un hostname distinto, sin SNI, una IP antigua o una región/edge diferente.
Actualiza la comprobación para enviar SNI y validar el hostname exacto que utilizan los usuarios.

6) ¿Puedo simplemente reiniciar Nginx en lugar de recargar?

Puedes, pero recargar suele ser la mejor práctica: menos disrupción y menos efectos secundarios inesperados.
Reiniciar es una herramienta tosca; úsala cuando la recarga esté bloqueada (p. ej. workers atascados) o cuando tu gestor de procesos lo requiera.

7) ¿Y si uso un balanceador de carga que termina TLS?

Entonces renovar el certificado del backend no cambiará lo que ven los usuarios. Rota el certificado en el balanceador (o activa certificados gestionados allí).
Puedes mantener TLS en el backend por defensa en profundidad, pero no lo confundas con el certificado público.

8) ¿Cómo evito que esto vuelva a ocurrir?

Dos cosas: (1) un deploy hook que recargue Nginx tras una renovación exitosa y (2) una verificación externa que confirme el serial/expiración servido.
Automatización sin verificación es simplemente fallo rápido.

9) ¿Cuál es la señal única en la que confío al depurar?

El certificado realmente servido a un cliente real, medido con SNI usando openssl s_client (o una sonda equivalente) y registrado con número de serie + expiración.
Todo lo demás es evidencia de soporte.

Conclusión: pasos siguientes para evitar la repetición

Este problema raramente es “Let’s Encrypt falló.” Casi siempre es “la capa servidora no conmutó.” Tu depuración debe reflejar eso.
Empieza desde el exterior, prueba lo que se sirve, prueba quién lo sirve, luego alinea rutas de configuración y comportamiento de recarga.

El principio operativo se captura bien con una idea parafraseada a menudo atribuida a Ronald Coase: si torturas los datos el tiempo suficiente, confesará.
En términos de operaciones: no tortures logs y suposiciones. Mide el endpoint en vivo, luego trabaja hacia atrás.

Pasos prácticos siguientes:

  • Estandariza Nginx para referenciar /etc/letsencrypt/live/.../fullchain.pem y privkey.pem, no archive/.
  • Haz que nginx -t + systemctl reload nginx sean la acción post-renovación vía un deploy hook.
  • Añade una sonda externa que registre el número de serie y la expiración del certificado servido por hostname tras la renovación.
  • Audita tus puntos de terminación TLS: VM, ingreso en contenedor, balanceador de carga, CDN. Pon un responsable al lado de cada uno.
← Anterior
MySQL vs MariaDB: preparación para Kubernetes—probes, reinicios y seguridad de datos
Siguiente →
MySQL vs SQLite: señales de migración — cuándo es el momento de actualizar

Deja un comentario