instalar certbot nginx ubuntu 24.04
Guía para instalar certbot con apt o snap en Ubuntu 24.04. Aprenda la diferencia entre versiones y cómo evitar conflictos de renovación en el puerto 80.
Instalar Certbot: apt o snap
En Ubuntu 24.04, sudo apt install certbot python3-certbot-nginx proporciona un Certbot funcional que emite certificados de Let's Encrypt reales y de confianza pública. La documentación oficial de Certbot recomienda usar snap; la diferencia es mínima: el paquete snap sigue las versiones oficiales, mientras que el paquete de los repositorios sigue la versión incluida en la LTS y recibe parches de seguridad.
Elija uno. Tener dos copias de Certbot significa tener dos temporizadores de renovación apuntando al mismo árbol /etc/letsencrypt, y el que olvide será el que cause problemas.
La opción apt:
sudo apt update
sudo apt install certbot python3-certbot-nginxEsto instala /usr/bin/certbot, el plugin de nginx, un par certbot.service + certbot.timer y una entrada /etc/cron.d/certbot que no realiza ninguna acción bajo systemd.
La opción snap:
sudo apt remove certbot python3-certbot-nginx
sudo snap install core && sudo snap refresh core
sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbotEl snap incluye su propio temporizador, snap.certbot.renew.timer. Elimine el paquete apt antes de instalar el snap.
Ambas instalaciones se comportan igual después de la instalación. Certbot 2.x usa ECDSA (P-256) por defecto; use --key-type rsa solo si el cliente no admite ECDSA. Todo el estado se almacena en /etc/letsencrypt: archive/ contiene los archivos reales de la clave y el certificado, live/ contiene los enlaces simbólicos a los actuales, renewal/ contiene un archivo de configuración por certificado y accounts/ contiene su clave de cuenta ACME.
Qué hace realmente HTTP-01 y por qué el puerto 80 no es opcional
El desafío HTTP-01 es una llamada de retorno (callback). Usted solicita a Let's Encrypt un certificado para example.com; el servidor resuelve el nombre en el DNS público, abre una conexión al puerto 80 en la dirección encontrada y solicita http://example.com/.well-known/acme-challenge/<token>. Su servidor responde con el contenido exacto del token que Certbot acaba de escribir en el disco. Ese es todo el mecanismo. De esto se derivan tres consecuencias que causan la mayoría de los fallos en la emisión.
- El puerto 80 debe ser alcanzable desde internet, no solo desde su laptop. Una regla de
ufw, un grupo de seguridad de un proveedor de la nube o un firewall de consola de VPS que solo abra el puerto 443 impedirá la emisión y todas las renovaciones futuras. - El DNS debe apuntar ya a este equipo. El servidor de validación realiza su propia consulta desde el exterior; sus entradas
/etc/hostsy la caché del navegador no tienen efecto para él. - Si publica un registro AAAA, se intentará IPv6 primero. Let's Encrypt reintenta mediante IPv4 si la conexión IPv6 falla por completo; pero un registro AAAA desactualizado que apunte a un host que acepte la conexión y entregue algo distinto provocará un error crítico.
Las redirecciones están permitidas: la validación sigue una redirección HTTP a HTTPS y no le importa que el certificado en el otro extremo falte, esté expirado o sea autofirmado. Lo que no hará es iniciar la conexión en un puerto distinto al 80. Certbot no tiene una implementación de TLS-ALPN-01, por lo que "usar solo el 443" no es una solución.
Elección de un autenticador: --nginx, --webroot, --standalone
--nginx es la opción predeterminada correcta si nginx ya está en ejecución y ya sirve el dominio. Certbot analiza su configuración, inserta una ubicación temporal para el challenge, recarga nginx, valida y escribe las directivas TLS en su server block. Sin tiempo de inactividad.
sudo certbot --nginx -d example.com -d www.example.comPara uso mediante scripts en un servidor nuevo:
sudo certbot --nginx \
-d example.com -d www.example.com \
--agree-tos -m ops@example.com --no-eff-email \
--redirect --non-interactive--webroot es la opción correcta cuando no desea que Certbot modifique su configuración de nginx; por ejemplo, si usa una configuración generada desde una plantilla, guardada en git o desplegada con Ansible. Certbot solo escribe el archivo de challenge en un directorio que usted ya sirve.
sudo certbot certonly --webroot -w /var/www/example.com \
-d example.com -d www.example.com \
--deploy-hook "systemctl reload nginx"--standalone es la opción correcta cuando nada escucha en el puerto 80: un servidor de correo, una API que solo usa el puerto 443 o un script de primer arranque que se ejecuta antes de que exista nginx. Certbot ocupa el puerto 80 por unos segundos. Si nginx está en ejecución, esto fallará; detenga el servicio antes de ejecutarlo:
sudo certbot certonly --standalone -d mail.example.com \
--pre-hook "systemctl stop nginx" \
--post-hook "systemctl start nginx"Esos hooks se registran en la configuración de renovación del certificado, por lo que el proceso de stop/start se realiza de forma automática durante la renovación.
Un bloque de servidor que funciona antes y después de que el certificado exista
El problema de la paradoja del huevo y la gallina: nginx no inicia si ssl_certificate apunta a un archivo inexistente, y Certbot no puede validar si nginx está detenido. Primero, levante el sitio en el puerto 80.
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
root /var/www/example.com;
index index.html;
location ^~ /.well-known/acme-challenge/ {
root /var/www/example.com;
default_type "text/plain";
try_files $uri =404;
}
location / {
try_files $uri $uri/ =404;
}
}Ejecute sudo nginx -t && sudo systemctl reload nginx, confirme las respuestas de curl -I http://example.com/ desde fuera del servidor y luego emita el certificado. Después:
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
location ^~ /.well-known/acme-challenge/ {
root /var/www/example.com;
default_type "text/plain";
}
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name example.com www.example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
root /var/www/example.com;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}El prefijo ^~ en la ubicación ACME es fundamental: evita que el bloque return 301 absorba la solicitud de validación. Mantener esa ubicación en el puerto 80 permite que las renovaciones sigan funcionando después de que el resto del sitio pase a usar solo HTTPS.
La sintaxis de HTTP/2 depende de su versión de nginx; mezclar ambas formas genera un error de inicio. Ubuntu 24.04 incluye nginx 1.24, que requiere la sintaxis inline — listen 443 ssl http2;. Debian 13 incluye una versión más reciente de nginx, que requiere la directiva http2 on; independiente. Verifique nginx -v primero.
Configure nginx hacia live/, nunca hacia archive/. Los symlinks de live/ se actualizan en cada renovación; usar una ruta absoluta hacia archive/ le vincula a un certificado que expirará.
Los comodines implican DNS-01, y DNS-01 implica un plugin
Un certificado wildcard (*.example.com) no puede validarse mediante HTTP-01; no existe un único hostname para descargar el archivo. DNS-01 es la única vía: debe demostrar el control publicando un registro TXT _acme-challenge.example.com. Certbot requiere las credenciales de la API de su proveedor de DNS para realizar este proceso de forma automática; para eso existen los plugins del proveedor. Guía completa del certificado wildcard explica la mecánica del registro TXT y el error de renovación en modo manual; a continuación se presenta la versión corta para Cloudflare.
sudo snap set certbot trust-plugin-with-root=ok
sudo snap install certbot-dns-cloudflareEn la ruta de apt, el archivo es sudo apt install python3-certbot-dns-cloudflare. Las credenciales se guardan en un archivo accesible solo por root:
# /root/.secrets/cloudflare.ini
# then: sudo chmod 600 /root/.secrets/cloudflare.ini
dns_cloudflare_api_token = your_scoped_token_hereLimite el token a permisos de edición de DNS en esa zona específica. Es una clave para su DNS; trátela como tal.
sudo certbot certonly \
--dns-cloudflare \
--dns-cloudflare-credentials /root/.secrets/cloudflare.ini \
-d example.com -d '*.example.com'Use comillas en el wildcard para que el shell no realice un globbing. DNS-01 también resuelve lo que HTTP-01 no puede: certificados para hosts sin puerto 80 público, como un servicio interno, un equipo accesible solo mediante una VPN WireGuard auto-hospedada en un VPS o un panel de administración en una interfaz privada.
Renovación: los 90 días, el temporizador y el deploy hook
Los certificados de Let's Encrypt tienen una validez de 90 días. Certbot realiza la renovación cuando quedan menos de 30 días. Esto proporciona un margen de 30 días para corregir errores antes de que ocurra una interrupción del servicio. Let's Encrypt ya no envía correos electrónicos de advertencia de expiración; la responsabilidad del monitoreo recae exclusivamente en el administrador.
Verifique el temporizador de su instalación:
systemctl list-timers 'certbot*' 'snap.certbot*'
sudo certbot certificatescertbot renew analiza cada configuración en /etc/letsencrypt/renewal/, omite lo que esté fuera del margen de 30 días y renueva el resto utilizando exactamente los mismos flags de la ejecución original. Por esto la primera ejecución es crítica: es la que se registra.
Renovar el archivo en disco no aplica los cambios por sí solo; nginx mantiene el certificado antiguo en memoria hasta que se le solicita un reload. Configure un deploy hook:
sudo tee /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh >/dev/null <<'EOF'
#!/bin/sh
set -e
nginx -t && systemctl reload nginx
EOF
sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.shCualquier ejecutable en renewal-hooks/deploy/ se ejecuta tras una renovación exitosa. El flag --deploy-hook realiza la misma función para un único certificado, almacenando renew_hook = ... en su configuración de renovación. certbot --nginx realiza el reload automáticamente; las configuraciones --webroot y --standalone no lo hacen. La ausencia de un hook es la causa de que un sitio sirva un certificado expirado mientras certbot certificates reporta uno nuevo correctamente. Cualquier otro proceso que lea el certificado al iniciar requiere el mismo hook; una aplicación en contenedores, como una instalación de Nextcloud VPS con Docker, TLS y backups, requiere su propio paso de reinicio o reload configurado aquí.
Prueba de renovación real
sudo certbot renew --dry-runEsto ejecuta el desafío completo contra el entorno de staging de Let's Encrypt: mismo flujo de código, mismo firewall, mismo DNS, sin coste de rate-limit y sin escribir nada en el disco. Si pasa hoy, la renovación automática en 60 días también pasará, siempre que no cambie nada en la configuración del servidor.
Un dry run no demuestra que el reload hook se ejecute; el comportamiento varía según la versión de Certbot. Pruebe esa parte manualmente: ejecute el script del hook directamente, confirme que systemctl reload nginx sea exitoso y verifique sudo grep renew_hook /etc/letsencrypt/renewal/example.com.conf.
Los errores que encontrará realmente
Could not bind to IPv4 or IPv6. — --standalone porque nginx ya está usando el puerto 80. Use --nginx o --webroot, o detenga nginx antes de la ejecución. Confirme qué proceso ocupa el puerto con sudo ss -lntp | grep ':80'.
Timeout during connect (likely firewall problem) — Let's Encrypt no pudo alcanzar el puerto 80. Revise la conectividad: sudo ufw status (ábralo con sudo ufw allow 'Nginx Full'), luego el firewall del proveedor de VPS y finalmente el DNS. Pruebe desde un equipo externo al servidor: curl -sSv http://example.com/.well-known/acme-challenge/test. Un registro AAAA obsoleto produce este mismo mensaje.
unauthorized :: Invalid response from http://example.com/.well-known/acme-challenge/xyz: 404 — el puerto 80 es accesible, pero no se entrega el token. La solicitud llegó a un server block distinto (verifique cuál tiene la propiedad de default_server), o el directorio pasado a -w no es el que nginx sirve. Coloque un archivo en /var/www/example.com/.well-known/acme-challenge/test y descárguelo externamente; si devuelve un error 404, el problema no es el certificado.
**DNS problem: NXDOMAIN looking up A for example.com — el nombre no se resuelve públicamente. Puede deberse a registros nuevos sin propagar o a un registro en una zona que su registrador no está sirviendo.
**too many certificates already issued for: example.com — un límite de tasa (rate limit), común durante procesos de depuración en bucle. Let's Encrypt limita los certificados duplicados —el mismo conjunto exacto de nombres— a cinco por semana, y permite 50 certificados nuevos por dominio registrado por semana; solo el tiempo resuelve esto. Depure usando el entorno de staging con --dry-run.
**nginx: [emerg] cannot load certificate "/etc/letsencrypt/live/example.com/fullchain.pem": No such file or directory — nginx está configurado para un certificado que nunca se emitió, o uno eliminado con certbot delete. Comente el server block de TLS, inicie nginx, emita el certificado y restaure el bloque.
**open() "/etc/letsencrypt/options-ssl-nginx.conf" failed — ese archivo se incluye con el paquete del plugin de nginx. En un sistema certonly sin python3-certbot-nginx, instale el plugin o reemplace la línea include con sus propios ajustes de ssl_protocols y ssl_ciphers.
Gestión a gran escala
Un certificado puede contener hasta 100 nombres. Usar un único certbot --nginx -d a.example.com -d b.example.com ... es tentador, pero si un registro DNS obsoleto falla la validación, todos los demás nombres del certificado dejarán de funcionar. Los certificados separados por sitio fallan de forma independiente; este es el comportamiento deseado en un servidor que aloja múltiples servicios. Al gestionar muchos sitios, un proxy inverso compatible con ACME es la mejor opción: un Traefik reverse proxy ejecutando múltiples apps bajo Docker Compose solicita y renueva los certificados automáticamente, eliminando la necesidad de usar Certbot.
Realice una copia de seguridad completa de /etc/letsencrypt — sudo tar -czf letsencrypt-$(date +%F).tar.gz -C /etc letsencrypt — manteniendo los symlinks. Ese directorio contiene accounts/, su clave de cuenta ACME, la cual no puede regenerarse de forma idéntica. Migrar a un nuevo VPS se reduce a: usar rsync para copiar el directorio con -a, instalar Certbot, actualizar el DNS y ejecutar certbot renew --dry-run antes de realizar el cambio.
El temporizador de renovación no se transfiere automáticamente al reconstruir el servidor o cambiar a una nueva versión LTS. Tras cualquier migración, restauración de snapshot o actualización de distribución, ejecute systemctl list-timers 'certbot*' y un --dry-run. Omitir este paso provoca la caída del sitio 89 días después, a las 3am, cuando el certificado que se suponía se renovaba solo expire.
Todo esto requiere un servidor bajo su control, con una IP pública y el puerto 80 abierto al mundo; es decir, un VPS. Los pasos técnicos son idénticos en cualquier VPS.
Los mismos pasos para los certificados se aplican en Apache en lugar de nginx. Cuando un certificado público no sea viable, un certificado autofirmado en Ubuntu es suficiente para servicios internos.
FAQ
¿Es necesario tener abierto el puerto 80 si mi sitio solo usa HTTPS?
Sí, para el desafío HTTP-01. Let's Encrypt siempre inicia su solicitud de validación en el puerto 80. Certbot no tiene una implementación de TLS-ALPN-01, por lo que un firewall que solo abra el puerto 443 bloqueará tanto la emisión inicial como cualquier renovación automática posterior. Una redirección del puerto 80 a HTTPS es válida, ya que la validación la sigue. La única forma de omitir el puerto 80 por completo es mediante DNS-01 con un plugin de proveedor.
apt o snap — ¿qué Certbot debo instalar para nginx en Ubuntu 24.04?
Use apt. sudo apt install certbot python3-certbot-nginx le proporciona Certbot 2.9.0 en Ubuntu 24.04, que es suficiente para todo lo contenido en esta guía, recibe parches de seguridad a través de unattended-upgrades y no requiere snapd. Elija snap solo si necesita la versión más reciente de inmediato o un plugin de DNS distribuido exclusivamente como snap. En cualquier caso, elija solo uno: dos instalaciones implican dos temporizadores de renovación apuntando al mismo árbol /etc/letsencrypt, y el que se olvide será el que cause problemas.
¿Puede Certbot emitir un certificado wildcard para nginx?
Solo mediante DNS-01. Un wildcard como *.example.com no tiene un único hostname para descargar el archivo de desafío, por lo que --nginx, --webroot y --standalone no son opciones. Instale el plugin para su proveedor de DNS, coloque un token de API con permisos limitados en un archivo de credenciales de solo root y ejecute certbot certonly --dns-cloudflare -d example.com -d '*.example.com', usando comillas en el wildcard para evitar el globbing de la shell.
¿Por qué nginx sigue sirviendo el certificado antiguo tras una renovación exitosa?
nginx mantiene el certificado en memoria y no detecta el nuevo archivo en disco hasta que se recarga. certbot --nginx realiza la recarga por usted, pero las ejecuciones de --webroot y --standalone no lo hacen, por lo que una renovación puede ser exitosa mientras el navegador sigue mostrando un certificado próximo a expirar. Coloque un script ejecutable en /etc/letsencrypt/renewal-hooks/deploy/ que ejecute nginx -t && systemctl reload nginx para que se active tras cada renovación exitosa.
¿Demuestra certbot renew --dry-run que la renovación funcionará?
En su mayor parte. Ejecuta el desafío real contra el entorno de staging —mismo firewall, mismo DNS, misma ruta de código— sin coste de rate-limit y sin escribir nada en disco, por lo que un resultado positivo indica que la parte de red es correcta. Sin embargo, no demuestra de forma fiable que su hook de despliegue se ejecute. Pruebe eso por separado: ejecute el script del hook manualmente y verifique sudo grep renew_hook /etc/letsencrypt/renewal/example.com.conf.