SSL Certificate Renewal Guide 2026
Complete guide to SSL certificate renewal with Let's Encrypt, certbot auto-renewal, cron jobs, and monitoring alerts. Prevent expired cert outages.
SSL Certificate Renewal
Your SSL certificate expires in 12 days. You know this because your monitoring just fired an alert — or worse, because a customer reported that your site shows a security warning. Either way, the clock is ticking. Certificate renewal should be automated, tested, and monitored so this situation never occurs. This guide covers the complete renewal lifecycle: manual renewal for the immediate fix, certbot automation for the long-term solution, cron scheduling for reliability, and PingCheck alerts as the safety net. Every command here is tested against real infrastructure running in 2026.
Understanding SSL Certificate Lifespans
Certificate validity periods have shortened steadily:
The 90-day cycle means auto-renewal is not a convenience — it is a requirement. Manually renewing every 90 days across multiple domains is unsustainable.
Emergency Manual Renewal with Certbot
If your certificate is about to expire or already has, renew it manually first. Fix the automation later.
# Check which certificates certbot manages
sudo certbot certificatesOutput:
Certificate Name: luxkern.com
Serial Number: 04a3b2c1d4e5f6...
Key Type: ECDSA
Domains: luxkern.com www.luxkern.com
Expiry Date: 2026-04-19 23:59:59+00:00 (INVALID: EXPIRED)
Certificate Path: /etc/letsencrypt/live/luxkern.com/fullchain.pem
Private Key Path: /etc/letsencrypt/live/luxkern.com/privkey.pemRenew a specific certificate
sudo certbot renew --cert-name luxkern.com --force-renewal--force-renewal ignores the "not due for renewal" check. Use this only for emergencies — excessive force-renewals can hit Let's Encrypt rate limits.Renew all certificates
sudo certbot renewCertbot checks each managed certificate and renews any that are within 30 days of expiry.
Restart the web server after renewal
Certbot renews the certificate files on disk, but your web server is still serving the old certificate from memory. Restart it.
# Nginx
sudo systemctl reload nginx
Apache
sudo systemctl reload apache2
Node.js (if using native TLS)
You must restart the process — Node.js caches certificates at startup
pm2 restart your-appSetting Up Certbot from Scratch
If you are starting fresh or migrating to Let's Encrypt, here is the complete setup.
Install certbot
# Ubuntu/Debian
sudo apt update
sudo apt install certbot
With Nginx plugin
sudo apt install python3-certbot-nginx
With Apache plugin
sudo apt install python3-certbot-apache
Using snap (recommended by certbot team)
sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbotObtain your first certificate
# For Nginx (auto-configures the server block)
sudo certbot --nginx -d luxkern.com -d www.luxkern.com
For Apache (auto-configures the virtual host)
sudo certbot --apache -d luxkern.com -d www.luxkern.com
Standalone mode (when no web server is running)
sudo certbot certonly --standalone -d luxkern.com -d www.luxkern.com
DNS challenge (for wildcard certificates)
sudo certbot certonly --manual --preferred-challenges dns \
-d luxkern.com -d "*.luxkern.com"The Nginx and Apache plugins modify your server configuration to reference the new certificate files. The standalone mode starts a temporary web server on port 80 for the HTTP-01 challenge. The DNS challenge requires you to create a TXT record — necessary for wildcard certs.
Verify the installation
echo | openssl s_client -servername luxkern.com -connect luxkern.com:443 2>/dev/null \
| openssl x509 -noout -dates -subject -issuerOutput:
notBefore=Apr 7 00:00:00 2026 GMT
notAfter=Jul 6 23:59:59 2026 GMT
subject=CN = luxkern.com
issuer=C = US, O = Let's Encrypt, CN = R11Automating Renewal with Cron
Certbot installs a systemd timer or cron job automatically on most systems. Verify it exists:
# Check for systemd timer
systemctl list-timers | grep certbot
Or check crontab
sudo crontab -l | grep certbotIf neither exists, create one manually:
# Edit root's crontab
sudo crontab -eAdd this line:
0 3 * * * certbot renew --quiet --deploy-hook "systemctl reload nginx" >> /var/log/certbot-renew.log 2>&1Breaking this down:
0 3 * * * — runs daily at 3:00 AM. Certbot internally skips certificates that are not due.--quiet — suppresses output unless there is an error.--deploy-hook — runs only when a certificate is actually renewed. This reloads Nginx to pick up the new certificate.>> /var/log/certbot-renew.log — logs output for debugging.Testing the renewal process
Never wait until expiry to find out your renewal is broken. Dry-run it:
sudo certbot renew --dry-runOutput:
Simulating renewal of an existing certificate for luxkern.com and www.luxkern.com
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Congratulations, all simulated renewals succeeded:
/etc/letsencrypt/live/luxkern.com/fullchain.pem (success)If the dry run fails, you have a configuration issue to fix before the real renewal attempt. Common failures:
Renewal with Docker and Reverse Proxies
Docker Compose with Nginx and Certbot
# docker-compose.yml
version: '3.8'
services:
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
- certbot-webroot:/var/www/certbot:ro
- certbot-certs:/etc/letsencrypt:ro
depends_on:
- app
certbot:
image: certbot/certbot
volumes:
- certbot-webroot:/var/www/certbot
- certbot-certs:/etc/letsencrypt
entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew --webroot -w /var/www/certbot; sleep 12h & wait $${!}; done'"
app:
build: .
expose:
- "3000"
volumes:
certbot-webroot:
certbot-certs:The certbot container runs in a loop, checking for renewal every 12 hours. The shared volumes ensure Nginx always has access to the latest certificates.
Initial certificate in Docker
# First time: obtain the certificate
docker compose run --rm certbot certonly \
--webroot -w /var/www/certbot \
-d luxkern.com -d www.luxkern.com \
--email ops@luxkern.com --agree-tos --no-eff-email
Reload Nginx to pick up the new certificate
docker compose exec nginx nginx -s reloadRenewal for Wildcard Certificates
Wildcard certificates (
*.luxkern.com) require DNS-01 challenges. Automate this with a DNS plugin.Cloudflare DNS plugin
# Install the plugin
sudo apt install python3-certbot-dns-cloudflare
Create credentials file
sudo mkdir -p /etc/letsencrypt
cat <<EOF | sudo tee /etc/letsencrypt/cloudflare.ini
dns_cloudflare_api_token = YOUR_CLOUDFLARE_API_TOKEN
EOF
sudo chmod 600 /etc/letsencrypt/cloudflare.ini
Obtain wildcard certificate
sudo certbot certonly \
--dns-cloudflare \
--dns-cloudflare-credentials /etc/letsencrypt/cloudflare.ini \
-d luxkern.com \
-d "*.luxkern.com"The DNS plugin creates and cleans up the TXT record automatically. Renewal works with the same cron job — certbot remembers which plugin was used.
Route 53 DNS plugin (AWS)
sudo apt install python3-certbot-dns-route53
Uses AWS credentials from environment or ~/.aws/credentials
sudo certbot certonly \
--dns-route53 \
-d luxkern.com \
-d "*.luxkern.com"Post-Renewal Hooks
Certbot supports three types of hooks:
sudo certbot renew \
--pre-hook "echo 'Starting renewal'" \
--deploy-hook "systemctl reload nginx && curl -s -X POST https://hooks.slack.com/services/T.../B.../xxx -d '{\"text\": \"SSL renewed for luxkern.com\"}'" \
--post-hook "echo 'Renewal attempt complete'"--pre-hook runs before any renewal attempt.--deploy-hook runs only when a certificate is actually renewed.--post-hook runs after all renewal attempts, regardless of outcome.The deploy hook above reloads Nginx and sends a Slack notification. You can also use it to restart Docker containers, purge CDN caches, or update load balancer listeners.
Monitoring Renewal with PingCheck
Automation fails silently. Your cron job might stop running because the server ran out of disk space. The DNS plugin might lose access because someone rotated the API token. The web server might not reload properly.
Luxkern PingCheck monitors your SSL certificates independently of your renewal process:
PingCheck acts as an independent safety net. Even if certbot, your cron job, and your deploy hook all fail, PingCheck catches the approaching expiry and alerts you with enough time to intervene manually.
Try PingCheck free — no credit card required.
Monitoring Cron Job Execution
The renewal cron itself needs monitoring. If it stops running, you will not know until the certificate expires.
# Add a heartbeat ping after successful renewal check
0 3 * * * certbot renew --quiet --deploy-hook "systemctl reload nginx" && curl -s https://hc-ping.luxkern.com/your-check-id > /dev/nullThe heartbeat ping tells your monitoring system that the cron job ran. If the ping stops arriving, you get an alert. This is the pattern described in how to monitor cron jobs.
Handling Rate Limits
Let's Encrypt enforces rate limits to prevent abuse:
If you hit a rate limit:
# Check your current certificates to avoid unnecessary renewals
sudo certbot certificates
Use the staging environment for testing
sudo certbot certonly --staging --nginx -d test.luxkern.comThe staging environment has much higher rate limits and issues certificates that browsers do not trust — perfect for testing your automation.
Migrating from Paid Certificates to Let's Encrypt
If you are currently paying for DV certificates, switching to Let's Encrypt saves money with no security tradeoff. DV certificates from paid CAs and Let's Encrypt provide identical encryption and browser trust.
Migration steps:
certbot certonly to obtain the new certificate (does not affect the running server).Keep the old certificate active as a fallback until you have confirmed auto-renewal works through at least one cycle (90 days).
Troubleshooting Renewal Failures
"Challenge failed for domain"
The HTTP-01 challenge requires Let's Encrypt to reach
http://yourdomain.com/.well-known/acme-challenge/TOKEN. Check:"Too many certificates already issued"
You have hit the rate limit. Wait one week or use a different registered domain. Use
--staging for testing."Could not bind to IPv4 or IPv6"
Another process is using port 80. Stop it, or use the webroot or DNS challenge method instead of standalone.
Renewal succeeds but the site still shows the old certificate
The web server was not reloaded. Check that your
--deploy-hook is correctly configured and that the web server user has permission to read the new certificate files.Certificate Renewal Checklist
certbot renew --dry-run passes.For the complementary skill of checking certificate expiry on demand, see how to check SSL certificate expiry. For monitoring your API endpoints' uptime and performance alongside SSL, read how to monitor cron jobs.
Try PingCheck free — no credit card required.