Let's Encrypt has made free, automated SSL certificate renewal accessible to every web server on the internet. When configured correctly, Certbot (or other ACME clients) renews certificates automatically before expiry, eliminating certificate-related outages entirely. But "when configured correctly" is doing a lot of work in that sentence — Let's Encrypt auto-renewal fails silently more often than teams realize.
How Let's Encrypt Auto-Renewal Works
Let's Encrypt certificates expire every 90 days. Certbot, the official ACME client, is designed to:
- Run automatically via cron job or systemd timer (typically twice daily)
- Check all managed certificates for upcoming expiry
- If a certificate will expire in < 30 days, attempt renewal
- Complete ACME challenge validation (HTTP-01 or DNS-01)
- Obtain new certificate from Let's Encrypt
- Replace old certificate files
- Reload the web server to serve new certificate
When this works, you never think about certificate renewal. When it fails silently, you discover it when the certificate expires and users start seeing security warnings.
Common Let's Encrypt Renewal Failures
Port 80 Firewall Blocking (HTTP-01 Challenge)
HTTP-01 validation requires Let's Encrypt to make an HTTP request to http://yourdomain.com/.well-known/acme-challenge/[token]. If port 80 is blocked by a firewall, the challenge fails.
Symptoms: Certificate renewal works in staging but fails in production. Renewal was working, then stopped after a firewall configuration change.
Fix:
# Test if port 80 is accessible from outside
curl -I http://yoursite.com/.well-known/acme-challenge/test
# Check firewall rules (iptables)
iptables -L INPUT -n | grep -E "80|http"
Rate Limit Violations
Let's Encrypt imposes rate limits:
- 50 certificates per registered domain per week
- 5 failed validations per hostname per hour
Exceeding these limits causes renewal failures that won't auto-resolve until the rate limit window expires.
# Check current rate limit status
certbot certificates | grep "VALID"
# If you've hit rate limits, Certbot shows:
# Error: too many certificates already issued for exact set of domains
DNS-01 Challenge Configuration Issues
Wildcard certificates require DNS-01 challenges. If your DNS API credentials expire, change, or your DNS provider isn't supported, DNS-01 challenges fail.
Certbot Timer Not Running
If the systemd timer or cron job is not running, no renewal attempts occur:
# Check systemd timer status
systemctl status certbot.timer
systemctl list-timers certbot*
# Check cron job
crontab -l | grep certbot
cat /etc/cron.d/certbot
# Manually trigger a renewal attempt
certbot renew --dry-run
Web Server Configuration Change
After a web server configuration change, Certbot might not be able to place challenge files in the webroot, or the challenge location might not be served correctly.
Setting Up Reliable Certbot Automation
Basic Setup (HTTP-01 Challenge)
# Install Certbot
apt-get install certbot python3-certbot-nginx # Ubuntu/Debian with Nginx
# Obtain initial certificate
certbot --nginx -d yoursite.com -d www.yoursite.com
# Certbot automatically installs the certificate and sets up systemd timer
# Verify the timer is active:
systemctl status certbot.timer
Nginx with Auto-Renewal
server {
listen 80;
server_name yoursite.com www.yoursite.com;
# Allow ACME challenge path
location /.well-known/acme-challenge/ {
root /var/www/html;
}
# Redirect all other HTTP to HTTPS
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 443 ssl;
server_name yoursite.com www.yoursite.com;
ssl_certificate /etc/letsencrypt/live/yoursite.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/yoursite.com/privkey.pem;
# ... rest of SSL config
}
DNS-01 Challenge for Wildcards
# Install Certbot with DNS plugin (e.g., for Cloudflare)
pip install certbot-dns-cloudflare
# Configure credentials
cat /etc/letsencrypt/cloudflare.ini
# dns_cloudflare_email = you@example.com
# dns_cloudflare_api_key = your_api_key
# Obtain wildcard certificate
certbot certonly \
--dns-cloudflare \
--dns-cloudflare-credentials /etc/letsencrypt/cloudflare.ini \
-d yoursite.com \
-d "*.yoursite.com"
Renewal Hook for Web Server Reload
Certbot needs to reload your web server after renewal so it serves the new certificate:
# /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh
#!/bin/bash
systemctl reload nginx
# Make executable
chmod +x /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh
Monitoring for Renewal Failures
The most critical piece often missing: monitoring that auto-renewal actually works.
Monitor Certificate Validity Window
If auto-renewal is working correctly, your certificate should never have less than 30 days remaining (Let's Encrypt renews at 60 days). Monitor for certificates with fewer than 35 days remaining as a signal that auto-renewal may have failed:
# AzMonitor: Alert if certificate drops below 35 days
# (should never happen with working auto-renewal)
ssl_monitor:
domain: yoursite.com
alert_thresholds:
- days_remaining: 35
severity: critical
message: "Certificate has < 35 days remaining — auto-renewal likely failed"
- days_remaining: 14
severity: emergency
Certbot Dry-Run Monitoring
Run certbot renew --dry-run periodically and alert on failure:
#!/bin/bash
# /etc/cron.daily/check-certbot-renewal
if ! certbot renew --dry-run --quiet 2>/dev/null; then
# Send alert
curl -X POST https://api.azmonitor.com/v1/alerts \
-H "Authorization: Bearer $AZMONITOR_API_KEY" \
-d '{"message": "Certbot renewal dry-run failed on $(hostname)", "severity": "critical"}'
fi
Slack Notification on Renewal
Add a renewal hook that notifies your team when a certificate is successfully renewed:
# /etc/letsencrypt/renewal-hooks/post/notify-slack.sh
#!/bin/bash
curl -X POST $SLACK_WEBHOOK_URL \
-H 'Content-type: application/json' \
-d "{\"text\": \"SSL certificate renewed successfully for $RENEWED_DOMAINS\"}"
Renewal in Container and Kubernetes Environments
Containerized environments require special consideration for certificate renewal:
Docker: Mount Let's Encrypt certificates from the host into containers. Renewal on the host automatically updates what containers see if volumes are configured correctly.
Kubernetes: Use cert-manager, which handles certificate issuance and renewal natively:
# cert-manager Certificate resource
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: yoursite-tls
spec:
secretName: yoursite-tls-secret
issuerRef:
name: letsencrypt-prod
kind: ClusterIssuer
dnsNames:
- yoursite.com
- www.yoursite.com
- api.yoursite.com
cert-manager automatically renews certificates at 2/3 of their lifetime and stores the renewed certificate in a Kubernetes Secret that your Ingress uses automatically.
Monitor your SSL certificates with AzMonitor — even with perfect auto-renewal, monitoring ensures you know immediately if anything goes wrong. See also SSL expiry alerts for configuring alert windows appropriate for automated renewal setups.
3 monitors free forever · No credit card needed · Set up in 2 minutes
Start monitoring free →