TroubleshootingWritten by GLWS12 min readUbuntu 24.04 LTS / AlmaLinux 9
Commands run on your server unless a section says otherwise. Replace example usernames, addresses and paths with your own values. Keep a working session open when changing remote access.

How renewal works

Certbot renews certificates that are close to expiry by repeating the validation that proved you control the domain. The usual method asks the certificate authority to fetch a token over plain HTTP from port 80 of the domain. A change anywhere on that path breaks renewal: DNS, the firewall, the web server configuration or a proxy.

Certificates last about 90 days, so a broken renewal is often noticed only when the site shows a warning. Test renewal regularly, and monitor the expiry date.

Both systems
sudo certbot certificates
systemctl list-timers | grep -i certbot
AlmaLinux: enable the renewal timer installed by Certbot
sudo systemctl enable --now certbot-renew.timer
systemctl list-timers certbot-renew.timer

Reproduce the failure with a dry run

A dry run goes through the full renewal using the staging system, without changing your real certificates and without using up production rate limits. Read the error it prints, which names the domain and the cause.

Both systems
sudo certbot renew --dry-run
sudo tail -n 40 /var/log/letsencrypt/letsencrypt.log

Is the renewal job running at all?

If the timer is missing or disabled, certificates are never renewed. Check that a timer or cron job exists, that it ran recently and that its logs show no errors. If you installed Certbot with a package, a systemd timer normally exists.

Both systems
systemctl list-timers --all | grep -i certbot
sudo systemctl status certbot.timer --no-pager 2>/dev/null
sudo journalctl -u certbot --since "30 days ago" --no-pager | tail -n 20
ls /etc/cron.d/ | grep -i certbot

DNS mistakes

The domain must resolve to this server. Check the A record and, importantly, the AAAA record. If an AAAA record points to an old or wrong IPv6 address, the certificate authority tries IPv6 first and fails, even though your own tests over IPv4 work. Remove the AAAA record or fix it, as the IPv6 guide describes. Also check that the name was not moved behind a CDN or proxy.

Both systems
dig +short A example.com
dig +short AAAA example.com
curl -4 -sI http://example.com/ | head -n 1
curl -6 -sI http://example.com/ | head -n 1

Port 80 and firewalls

Validation needs port 80 reachable from the internet, even if the site only uses HTTPS. A firewall that blocks it, a provider-level firewall or a security rule added later breaks renewal. Test from outside the server, not from the server itself.

Both systems: the server side
sudo ss -lntp | grep ":80 "
sudo ufw status 2>/dev/null | grep 80
sudo firewall-cmd --list-services 2>/dev/null
From another machine
curl -sI --max-time 10 http://example.com/ | head -n 1

The challenge path and redirects

Certbot places a file under /.well-known/acme-challenge/ and the authority requests it. If your web server redirects, blocks or rewrites that path, such as a catch-all that sends everything to the app, validation fails with a 404 or 403 error. Test the path yourself by creating a file in the web root and fetching it from outside.

Both systems: replace the web root for your site
sudo mkdir -p /var/www/example.com/.well-known/acme-challenge
echo ok | sudo tee /var/www/example.com/.well-known/acme-challenge/test.txt
curl -s http://example.com/.well-known/acme-challenge/test.txt
sudo rm /var/www/example.com/.well-known/acme-challenge/test.txt
With a reverse proxy, the challenge request must reach the place Certbot serves it from. Either proxy that path to Certbot, or use the web root method with a location that serves the folder directly.

Authenticator and configuration problems

Certbot remembers the method used for each certificate in /etc/letsencrypt/renewal/. If you stopped the web server it relied on, switched from Nginx to Caddy, or changed ports, the saved method no longer works. Standalone mode needs port 80 free, so it fails when a web server is running. Review the renewal file and update it, or issue the certificate again with the right method.

Both systems
sudo ls /etc/letsencrypt/renewal/
sudo grep -E "authenticator|installer|webroot" /etc/letsencrypt/renewal/example.com.conf
sudo nginx -t

Rate limits and old certificates

Repeated failed attempts can hit Let's Encrypt rate limits, after which you must wait. Always test with the dry run, which uses the staging system. Remove certificates for domains you no longer use, because Certbot keeps trying to renew them and logs failures that hide real problems.

Both systems
sudo certbot certificates
sudo certbot delete --cert-name old.example.com

Reload the server and verify

After a successful renewal the web server must load the new certificate. Certbot normally reloads Nginx or Apache for you, but a custom setup may need a deploy hook. Check the expiry the visitor sees, not the file on disk.

Both systems
sudo certbot renew
sudo systemctl reload nginx 2>/dev/null || sudo systemctl reload apache2 2>/dev/null || sudo systemctl reload httpd 2>/dev/null
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | openssl x509 -noout -enddate

Official documentation

These upstream references provide more detail on the commands and workflows in this guide.