SSL Auto-Renewal: How to Never Let Your Certificate Expire Again
Complete guide to automatic SSL certificate renewal with Let's Encrypt and ACME. Compare Certbot, acme.sh, CertPilot strategies, cron job setup, renewal hooks, monitoring, and troubleshooting expired certificates.
SSL Auto-Renewal: How to Never Let Your Certificate Expire Again
An expired SSL certificate is one of the fastest ways to lose visitors, trust, and revenue. The browser shows a terrifying red "Not Secure" warning, users bounce, and if you run an e-commerce site, transactions stop cold.
According to Venafi's 2025 report, over 60% of certificate-related outages are caused by expired certificates — not security breaches, not misconfigurations — just expiration that someone forgot to handle.
The good news? Let's Encrypt certificates are designed for automation. With a properly configured auto-renewal setup, you can literally set it once and forget it forever.
This guide covers everything: how Let's Encrypt renewal works, the best ACME clients and their renewal strategies, cron job setup, renewal hooks, monitoring, and what to do when renewal fails.
How Let's Encrypt Certificate Renewal Works
Let's Encrypt certificates have a 90-day validity period — intentionally short. This isn't a limitation; it's a feature. Short-lived certificates force automation into the workflow, reducing the risk of key compromise and eliminating the "set-and-forget-but-forget-to-renew" problem.
The ACME Renewal Protocol
When a certificate is issued via ACME, the client stores:
- The certificate file (
.pemor.crt) - The private key (
.key) - The full certificate chain (including intermediates)
- An ACME account key (used to authenticate renewal requests)
Renewal works through ACME's renewal logic (defined in RFC 8555):
- The ACME client checks the certificate's expiration date
- If within the renewal window (typically 30 days before expiry), it initiates renewal
- The client re-authenticates using the same ACME account key
- A new certificate is issued for the same domains
- The web server reloads the new certificate — optionally via a deploy hook
This process is defined in the ACME protocol (RFC 8555), which means every compliant ACME client follows the same pattern.
When Does Renewal Trigger?
Different ACME clients use different thresholds:
| ACME Client | Renewal Window | Default Check Frequency |
|---|---|---|
| Certbot | 30 days before expiry | Twice daily (cron) |
| acme.sh | 60 days before expiry | Daily (cron) |
| CertPilot | 30 days before expiry | On-demand (dashboard) |
| Caddy | 30 days before expiry | Continuous (built-in) |
| Traefik | 30 days before expiry | Continuous (built-in) |
| lego | Customizable (default 30 days) | Per invocation |
The key insight: as long as your ACME client runs at least once during the renewal window, your certificate will be renewed automatically. Missing a single check isn't catastrophic — you have a 30-day buffer.
Setting Up Auto-Renewal with Different ACME Clients
Certbot (Standard & Reliable)
Certbot is the original Let's Encrypt client, maintained by the EFF. It's the most widely documented option.
# Install Certbot
sudo apt install certbot
# Obtain a certificate
sudo certbot certonly --standalone -d example.com -d www.example.com
# Test renewal (dry-run)
sudo certbot renew --dry-run
# Renew all expiring certificates
sudo certbot renew
Cron job — Certbot installs a systemd timer by default on modern systems, but you can also use cron:
# /etc/crontab or sudo crontab -e
0 3 * * * /usr/bin/certbot renew --quiet --post-hook "systemctl reload nginx"
The --post-hook runs a command only when renewal actually happens (not on every cron run).
acme.sh (Flexible & Lightweight)
acme.sh is a pure shell ACME client — no dependencies, works anywhere, supports 100+ DNS providers for DNS-01 validation.
# Install
curl https://get.acme.sh | sh
# Issue a certificate with DNS-01 (Cloudflare example)
export CF_Token="your_cloudflare_api_token"
acme.sh --issue --dns dns_cf -d example.com -d '*.example.com'
# Install to a specific location
acme.sh --install-cert -d example.com \
--key-file /etc/nginx/ssl/example.com.key \
--fullchain-file /etc/nginx/ssl/example.com.crt \
--reloadcmd "systemctl reload nginx"
acme.sh installs its own cron job automatically during setup. You can verify with:
crontab -l | grep acme.sh
# output: 0 0 * * * /root/.acme.sh/acme.sh --cron --home /root/.acme.sh > /dev/null
CertPilot (Dashboard-Based Management)
CertPilot provides a web dashboard for managing your Let's Encrypt certificates:
- Add your domain on the CertPilot dashboard
- Complete DNS-01 validation by adding the TXT record to your DNS provider
- Download the certificate files once issued
- Get expiry reminders — CertPilot alerts you before certificates expire, with one-click renewal
Best for: Teams managing multiple domains who want centralized visibility into certificate status and expiry dates.
Comparison: Which ACME Client Should You Use?
| Feature | Certbot | acme.sh | Caddy/Traefik | CertPilot |
|---|---|---|---|---|
| Installation | apt install |
`curl | sh` | Built-in |
| DNS-01 support | ⚠️ Plugin required | ✅ 100+ providers | ⚠️ Limited | ⚠️ Manual TXT record |
| Wildcard certs | ⚠️ Plugin required | ✅ Native | ✅ Built-in | ⚠️ Manual DNS-01 |
| Renewal cron | systemd timer / cron | Auto-installed | Built-in | Manual (expiry reminders) |
| Post-renewal hook | --post-hook |
--reloadcmd |
Auto-reload | Manual deploy |
| Monitoring | ❌ None | ❌ None | ❌ None | ✅ Dashboard + alerts |
| Learning curve | Moderate | Moderate | Low | Very low |
| Server footprint | 50 MB+ | < 5 MB | Depends | 0 MB (SaaS) |
| Best for | Single-server LAMP | Any server, pro | Containerized | Multi-domain visibility |
The Critical Piece: Renewal Hooks
A renewed certificate on disk does nothing by itself. You must reload the web server to pick up the new certificate. This is where renewal hooks come in.
Common Hook Commands
# Nginx
systemctl reload nginx # or: nginx -s reload
# Apache
systemctl reload apache2 # or: apachectl graceful
# HAProxy
systemctl reload haproxy # or: haproxy -f /etc/haproxy/haproxy.cfg -sf
# Caddy
# Caddy reloads automatically (no hook needed)
# Custom (e.g., copy to load balancer)
cp /etc/letsencrypt/live/example.com/fullchain.pem /opt/services/tls/
cp /etc/letsencrypt/live/example.com/privkey.pem /opt/services/tls/
systemctl reload my-service
Testing Your Hook
Always test with a dry-run:
certbot renew --dry-run --post-hook "echo 'Hook would run: reload nginx'"
This verifies renewal logic without actually requesting a new certificate.
Monitoring Your Renewals
Auto-renewal can fail silently. DNS API tokens expire, server clocks drift, network changes, and ACME endpoints change. Here's how to stay ahead.
Check Certificate Expiry from CLI
# Check when a cert expires
echo | openssl s_client -servername example.com -connect example.com:443 2>/dev/null \
| openssl x509 -noout -dates
# Check all certs in a directory
for cert in /etc/letsencrypt/live/*/fullchain.pem; do
echo "$(openssl x509 -in "$cert" -noout -enddate) — $cert"
done
Log-Based Monitoring
Parse your ACME client logs for renewal failures:
# Certbot logs are in /var/log/letsencrypt/
grep -i "fail\|error\|expir" /var/log/letsencrypt/letsencrypt.log
# acme.sh logs
tail -f /root/.acme.sh/acme.sh.log
External Monitoring Tools
| Tool | Features | Price |
|---|---|---|
| CertPilot | Dashboard, expiry alerts, multi-domain management | Free |
| CheckSSLTLS | External SSL checker, email alerts | Free tier |
| SSL Labs | Deep certificate analysis, grade scoring | Free |
| UptimeRobot | SSL expiry monitoring included in uptime checks | Free tier |
| Datadog | Certificate expiry metrics + alerting (requires agent setup) | Paid |
CertPilot's monitoring dashboard shows all your certificates in one place with real-time expiry status and proactive email alerts when a certificate is approaching expiry.
Common Auto-Renewal Failures (and Fixes)
1. DNS API Token Expired
DNS-01-based renewal fails if your DNS provider's API token has expired or been revoked.
Fix: Check your token validity dates. Set a calendar reminder to rotate tokens before they expire.
2. Rate Limiting
Let's Encrypt has rate limits:
- 50 certificates per registered domain per week
- 5 failed authorizations per domain per hour
- 10 certificates per domain per hour (combined issuance + renewal)
Fix: If you hit limits, the renewal will succeed the next day. This is rarely an issue for normal use.
3. Domain DNS Changed
If you moved your domain to a new DNS provider but didn't update your ACME client configuration, DNS-01 validation will fail.
Fix: Update your ACME client with the new DNS provider credentials. Check dig queries resolve correctly.
4. Expired ACME Account Key
Your ACME account key (stored locally) is used to authenticate renewal requests. If it's lost or corrupted:
Fix: Register a new ACME account with Let's Encrypt. You may need to re-issue certificates (they'll be signed by the new account).
5. Server Clock Drift
NTP failure can cause certificate validation errors — both for your server connecting to Let's Encrypt and for clients connecting to your server.
Fix: Ensure chronyd or ntpd is running:
timedatectl status
sudo systemctl enable --now chronyd
Best Practices for Bulletproof Auto-Renewal
Double Your Cron
Run your ACME client twice daily, not once. If one run fails due to a transient network issue, the next one (12 hours later) will catch it. With a 30-day renewal window, you have ~60 opportunities.
# Run at 3:00 AM and 3:00 PM
0 3,15 * * * /usr/bin/certbot renew --quiet --post-hook "systemctl reload nginx"
Separate Monitoring from Renewal
Don't rely on your ACME client to tell you it failed. Set up independent monitoring:
# Simple curl-based check
curl -I https://example.com 2>/dev/null | head -n 1
# Check cert expiry from outside (run on a separate machine)
openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null \
| openssl x509 -noout -checkend 604800 # warn if expiring in 7 days
Keep Your Renewal Infrastructure Simple
- One ACME client per machine
- One cron job (or systemd timer) per ACME client
- One post-renewal hook per web server
- One external monitoring source
Complexity is the enemy of reliability. The more moving parts, the more things break.
Automate DNS Credential Rotation
If using DNS-01 with API tokens:
# Example: rotate Cloudflare token monthly
# Use cert-manager or HashiCorp Vault for automated rotation
Use Staging for Testing
Let's Encrypt's staging environment has much higher rate limits. Test your renewal setup against staging first:
certbot certonly --staging --standalone -d example.com
certbot renew --dry-run --staging
Why 90-Day Certificates Are Better Than 1-Year Or 2-Year
This is worth emphasizing. Many administrators instinctively prefer longer validity periods, but the opposite is true for modern certificate management:
| Factor | 90-Day (Let's Encrypt) | 1-Year (Paid SSL) | 2-Year (Paid SSL) |
|---|---|---|---|
| Compromise window | 90 days max | 1 year max | 2 years max |
| Rotation habit | Automated, frequent | Manual, annual reminder | Easy to forget |
| Key rotation | Every 90 days | Once a year | Once every 2 years |
| Failure impact | Minutes to fix (auto) | Days to notice + fix | Days to notice + fix |
| Revocation cost | Low (auto-reissue) | High (manual re-buy) | High (manual re-buy) |
Short lifetimes force good security hygiene. The automation infrastructure you build for 90-day certificates creates a self-healing system that keeps your TLS posture healthy without manual intervention.
Putting It All Together: A Complete Renewal Setup
Here's what bulletproof auto-renewal looks like in practice:
For Single-Server Deployments
- Use Certbot with systemd timer (default on Ubuntu 24.04+)
- Add a post-hook to reload Nginx or Apache
- Set up CertPilot to monitor certificates from outside
- Add a cron job for additional email alerting:
# Weekly cert expiry check
0 9 * * 1 /usr/local/bin/check-certs.sh
Where check-certs.sh contains:
#!/bin/bash
DOMAINS=("example.com" "www.example.com")
for domain in "${DOMAINS[@]}"; do
if ! openssl s_client -connect "$domain:443" -servername "$domain" </dev/null 2>/dev/null \
| openssl x509 -noout -checkend 604800; then
echo "Certificate for $domain expires within 7 days!" | mail -s "SSL Expiry Warning" admin@example.com
fi
done
For Multi-Server / Multi-Domain Deployments
- Use a centralized ACME client like acme.sh with DNS-01
- Connect all DNS providers through your ACME client's DNS plugin
- Distribute certificates to each server via SCP, Ansible, or configuration management
- Use CertPilot to monitor certificate status across all domains
- Review the dashboard monthly and rotate DNS credentials quarterly
For Kubernetes / Container Deployments
- Use cert-manager (Kubernetes-native ACME client)
- Configure ClusterIssuer with Let's Encrypt production endpoint
- Use DNS-01 with a supported provider
- cert-manager handles renewal automatically — renews certificates and updates Secrets
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
privateKeySecretRef:
name: letsencrypt-account-key
solvers:
- dns01:
cloudflare:
apiTokenSecretRef:
name: cloudflare-api-token
key: api-token
The Bottom Line
SSL auto-renewal isn't optional — it's essential. Let's Encrypt's 90-day certificate lifetime is designed around automation, and every major ACME client supports hands-off renewal. The only thing standing between you and an expired certificate is a properly configured cron job and a renewal hook.
For a single server, Certbot + cron + a reload hook takes 10 minutes to set up. For multi-domain infrastructure, CertPilot provides centralized visibility into your certificate portfolio with expiry monitoring and alerts.
Whichever route you choose, set up your renewal infrastructure today. An expired certificate is one of the cheapest outages to prevent — and one of the most expensive ones to fix after the fact.
Related Articles
Free vs Paid SSL Certificates: Which One Do You Really Need in 2026?
Compare free Let's Encrypt SSL certificates vs paid SSL from DigiCert, Sectigo, and other CAs. Learn what you get with each, when paid SSL makes sense, and why 99% of sites should use free SSL in 2026.
DNS-01 Challenge Explained: The Best Way to Get Wildcard SSL Certificates
Complete guide to DNS-01 challenge for Let's Encrypt SSL certificates. Learn how DNS-01 works, why it beats HTTP-01 for wildcards, step-by-step setup with Cloudflare and Route 53, and automated renewal.
How to Get Free SSL Certificates with Let's Encrypt in 2026
Step-by-step guide to getting free SSL certificates with Let's Encrypt ACME. Learn about DNS-01 validation, auto-renewal, wildcard certificates, and automated SSL management with CertPilot.