
September 11, 2026
13 min read
By Kokil Thapa | Last reviewed: September 2026
TLS certificates expire. Manual renewals break at night, during Dashain traffic spikes, or when the one person who knows Certbot is on leave. Automate certificate rotation on every public-facing server you operate — web apps, APIs, load balancers, and staging mirrors. I've maintained dozens of Ubuntu stacks with Let's Encrypt and Certbot automation since 2018; the pattern below is what I still deploy on production Laravel and WordPress sites today. This guide covers Linux servers, Kubernetes with cert-manager, and the monitoring layer that catches silent failures before browsers do.
certbot renew, add deploy hooks that reload Nginx or Apache after renewal, and alert when certificates expire within 30 days.Why should you automate certificate rotation instead of renewing manually?
Public CAs like Let's Encrypt issue certificates valid for 90 days. That short lifetime is intentional. It limits the blast radius of a compromised key. It also means you renew roughly four times per year per hostname — multiplied across subdomains, staging environments, and client sites.
Manual renewal does not scale. On a real client project with eight subdomains, one missed renewal means Chrome shows a red interstitial. Users bounce. Search engines downgrade trust signals. For legal-tech portals handling document uploads, a broken padlock is worse than a slow page.
Automated rotation removes human memory from the loop. The ACME protocol handles issuance. Your scheduler handles timing. Deploy hooks push the new cert into the running web server. You only intervene when automation fails — and with proper alerts, you learn before users do.
If you are new to the underlying cryptography, read the SSL and TLS certificates explained primer first. It covers chain of trust, SANs, and why browsers trust Let's Encrypt. For PKI concepts beyond public TLS, see PKI and certificate management basics.
How do you automate certificate rotation with Certbot on Ubuntu?
Certbot remains the most practical tool for single-server and small-fleet Linux deployments. It speaks ACME, stores certs under /etc/letsencrypt, and integrates with Nginx and Apache through plugins. On Ubuntu 22.04 or 24.04 with PHP 8.3+ and Laravel 12 or 13, this is the stack I reach for first.
Initial issuance
Issue the first certificate with the plugin matching your web server. Use --dry-run against Let's Encrypt staging before touching production.
# Nginx example — replace example.com
sudo certbot certonly --nginx -d example.com -d www.example.com
# Apache example
sudo certbot certonly --apache -d example.com -d www.example.com
# Staging test (no rate limits burned)
sudo certbot certonly --nginx -d example.com --dry-run --staging Certbot writes renewal config to /etc/letsencrypt/renewal/example.com.conf. Every future rotation reuses that file. Do not delete it during server migrations — copy the entire /etc/letsencrypt directory or re-issue cleanly.
Enable automatic renewal
Modern Certbot packages install a systemd timer. Verify it is active before relying on cron.
sudo systemctl status certbot.timer
sudo systemctl list-timers | grep certbot The timer runs certbot renew twice daily. Certbot only renews certs expiring within 30 days. That window gives you weeks to fix a broken DNS record or firewall rule.
On servers without systemd timers — some minimal VPS images — add cron explicitly:
sudo crontab -e
# Run twice daily at random minutes to spread ACME load
17 3,15 * * * certbot renew --quiet --deploy-hook "/usr/local/bin/reload-webserver.sh" I have seen production outages from stale cron paths after Deployer symlink swaps. Pin the full path to the Certbot binary: /usr/bin/certbot renew. The same discipline applies to automated database backups on Linux — absolute paths survive deploy directory changes.
Deploy hooks that actually reload the server
Renewal without reload is a silent failure. The new cert sits on disk. Nginx or Apache still serves the old one from memory until restart. Always chain a deploy hook.
#!/bin/bash
# /usr/local/bin/reload-webserver.sh
set -euo pipefail
if systemctl is-active --quiet nginx; then
systemctl reload nginx
elif systemctl is-active --quiet apache2; then
systemctl reload apache2
else
echo "No web server found to reload" >&2
exit 1
fi sudo chmod +x /usr/local/bin/reload-webserver.sh
# Register hook globally for all renewals
echo 'deploy-hook = /usr/local/bin/reload-webserver.sh' | \
sudo tee -a /etc/letsencrypt/cli.ini For per-domain hooks — useful when one cert fronts multiple services — edit the renewal config directly:
# /etc/letsencrypt/renewal/example.com.conf
renew_hook = systemctl reload nginx After any hook change, test with a forced renewal dry run:
sudo certbot renew --dry-run Detailed server-level SSL steps live in install SSL certificates on Ubuntu. Pair that with Linux system administration if you want hands-off server maintenance.
Which tools should you use to automate certificate rotation in production?
Tool choice depends on where certificates terminate and how many hosts you manage. One Certbot install per VM works until you hit ten-plus domains or multi-cloud load balancers.
| Tool | Best for | Rotation trigger | Operational cost |
|---|---|---|---|
| Certbot + cron/systemd | Single VPS, Laravel/WordPress on Ubuntu | Scheduled certbot renew | Low — free, well documented |
| cert-manager | Kubernetes clusters | Certificate CRD near expiry | Medium — cluster addon to maintain |
| ACME client + DNS API | Wildcard certs, behind CDN, no port 80 | DNS-01 challenge via API token | Medium — API keys need rotation too |
| Cloud LB managed certs | AWS ALB, GCP LB, Cloudflare | Provider-managed | Low ops, vendor lock-in |
| HashiCorp Vault PKI | Internal mTLS, microservices | Short TTL + auto-renew agents | High — own PKI operations |
For most Nepali SMB sites I maintain — law firm portals, booking systems, WooCommerce shops — Certbot on Ubuntu with Apache or Nginx is sufficient. Sites like Notary Nepal and sister legal-tech properties share a Deployer 7 pipeline; certificate rotation runs independently on each EC2 host via systemd timer plus deploy hook.
When you outgrow single-server TLS, consider cert-manager for Kubernetes TLS automation. It watches Certificate resources and renews before expiry without shell access to nodes.
How do you automate certificate rotation when HTTP-01 validation fails?
HTTP-01 requires port 80 reachable from the public internet. That breaks when you hide origin servers behind a CDN, run internal-only staging hosts, or need wildcard certificates for *.example.com.
Switch to DNS-01 validation. Certbot creates a TXT record proving domain control. Wildcard certs become possible. The trade-off: you store DNS provider API credentials on the server.
Certbot with Cloudflare DNS plugin
sudo apt install python3-certbot-dns-cloudflare
# Credentials file — chmod 600, owned by root
sudo mkdir -p /root/.secrets
cat <<'EOF' | sudo tee /root/.secrets/cloudflare.ini
dns_cloudflare_api_token = YOUR_TOKEN_WITH_DNS_EDIT
EOF
sudo chmod 600 /root/.secrets/cloudflare.ini
sudo certbot certonly \
--dns-cloudflare \
--dns-cloudflare-credentials /root/.secrets/cloudflare.ini \
-d example.com \
-d '*.example.com' Renewal uses the same DNS plugin automatically. The systemd timer path stays identical. Rotate the API token on the same schedule you rotate SSH keys — store secrets outside git, never in your Deployer recipe.
For internal service mesh mTLS or client certificates, public ACME is the wrong tool. Issue short-lived certs from an internal CA or use Vault. That path is separate from public HTTPS rotation but follows the same principle: automate issuance, automate distribution, alert on expiry.
How do you monitor certificate expiry after you automate certificate rotation?
Automation without monitoring is optimism. Certbot can succeed while your deploy hook fails. DNS can change. Disk fills up in /etc/letsencrypt. A silent stale cert is indistinguishable from healthy HTTPS until day 89.
Build three layers: local log review, external expiry probes, and application-level checks before deploy.
- Log Certbot output. Redirect renew stdout to a dated log. Scan for
Congratulationsorerrorstrings weekly. - External TLS probe. Run
openssl s_clientor a Nagios-style plugin from outside your network. Internal checks lie when the local cert file is fresh but the edge is not. - Alert threshold at 30 days. Industry standard. Gives you time to fix DNS, firewalls, or rate limits before the 7-day Let's Encrypt retry window gets tight.
#!/bin/bash
# /usr/local/bin/check-cert-expiry.sh — exit 2 if < 30 days
DOMAIN="example.com"
DAYS=$(echo | openssl s_client -servername "$DOMAIN" -connect "$DOMAIN:443" 2>/dev/null \
| openssl x509 -noout -enddate | cut -d= -f2)
EXPIRY=$(date -d "$DAYS" +%s)
NOW=$(date +%s)
LEFT=$(( (EXPIRY - NOW) / 86400 ))
if [ "$LEFT" -lt 30 ]; then
echo "CRITICAL: $DOMAIN cert expires in $LEFT days"
exit 2
fi
echo "OK: $LEFT days remaining"
exit 0 Wire that script into your existing monitoring — cron plus email, Uptime Kuma, or a CI scheduled job. Treat cert alerts with the same urgency as disk-space warnings. Both are covered in log rotation and disk space management on Linux.
Before major releases, add a pre-deploy gate in GitLab CI. Fail the pipeline if staging TLS expires within seven days. That catches environment drift early. Similar gating patterns appear in automate releases with semantic-release workflows I use on sister sites sharing one GitLab runner.
What production mistakes break automated certificate rotation?
I've debugged certificate outages on live client stacks more often than I want to admit. Most root causes repeat.
- Reload hook missing or wrong. New cert on disk, old cert in memory. Always verify with
openssl s_clientafter forced renewal. - Port 80 blocked after hardening. UFW rules added without allowing HTTP for ACME. Keep 80 open or move to DNS-01.
- Stale cron after Deployer symlink swap. Cron still points at an old release path. Use absolute paths and test after every deploy pipeline change.
- Rate limits from repeated failed attempts. Use
--stagingwhile debugging. Let's Encrypt allows 50 certs per registered domain per week. - Multiple Certbot installs fighting. Snap Certbot plus apt Certbot on the same host causes conflicting timers. Pick one packaging method.
- CDN orange-cloud masking origin. Cloudflare terminates TLS at edge while origin cert expires unnoticed. Monitor both layers.
On shared EC2 infrastructure hosting legal-tech sister sites, I standardise one Certbot packaging method per host and document the deploy hook in the same Ansible playbook used for automated server setup. Consistency beats cleverness when you maintain fifteen domains on three servers.
PHP-FPM opcache and Laravel config cache are unrelated to TLS — but the same deploy discipline applies. After symlink swap, reload services that cache state. Certificate rotation is one item in a broader support and maintenance checklist I run monthly for retainer clients.
Multi-domain and migration notes
When migrating a site to a new server, export /etc/letsencrypt or re-issue on the new host before DNS cutover. Running two active hosts against the same domain burns rate limits fast.
For domain registration and hosting clients, I document which server holds the active ACME account key. Losing that key is recoverable but painful — you re-validate every hostname from scratch.
Enterprise clients with compliance requirements sometimes need evidence that rotation ran. Log retention plus SOC 2 compliance evidence in CI patterns apply: store Certbot logs in a central bucket, timestamp them, and review quarterly.
Key Takeaways
- Install Certbot with your web-server plugin, verify the systemd timer or cron job, and never skip the deploy hook that reloads Nginx or Apache.
- Run
certbot renew --dry-runafter every infrastructure change — firewall rules, DNS moves, Deployer path updates. - Use DNS-01 with a provider plugin when you need wildcard certs or cannot expose port 80.
- Alert externally when any production cert drops below 30 days remaining — internal file checks are not enough.
- Pick one Certbot packaging method per server and standardise hooks across your fleet with Ansible or cloud-init.
- Treat certificate rotation as ongoing ops, not a one-time setup task — pair it with monthly maintenance on every production host.
People Also Ask
How often does Let's Encrypt require certificate rotation?
Let's Encrypt certificates are valid for 90 days. Certbot attempts renewal when 30 days or fewer remain. With the default systemd timer running twice daily, rotation typically completes automatically between day 60 and day 75 without manual action.
Does Certbot automatically reload Nginx after renewal?
Not by default on all installations. You must configure a deploy-hook or renew_hook that runs systemctl reload nginx. Without it, the renewed certificate file updates on disk but the running process continues serving the previous cert from memory.
Can you automate certificate rotation behind Cloudflare?
Yes. Either terminate TLS at Cloudflare with their managed certs, or use DNS-01 validation against the Cloudflare API for origin certificates. Monitor both the edge cert and the origin cert — they expire independently.
What is the difference between certbot renew and certbot certonly?
certbot certonly obtains a new certificate interactively or via initial setup. certbot renew reads existing renewal configs and re-issues certs nearing expiry. Scheduled automation always uses renew, not repeated certonly calls.
Put certificate rotation on autopilot today
Manual TLS renewal is operational debt. It fails quietly, costs trust, and pulls you out of feature work at the worst moment. Automate certificate rotation with Certbot, a reload hook, and a 30-day expiry alert — then verify with certbot renew --dry-run once a month. That three-part stack has kept HTTPS alive across every production Laravel, WordPress, and WooCommerce site I maintain from Kathmandu.
If you want this configured on your stack — plus DNS, hosting, deploy pipelines, and monitoring in one engagement — review the portfolio of shipped projects and reach out via contact us. For hands-on server work, see Linux system administration services. Useful ops utilities live on the free developer tools page, including the regex tester for parsing Certbot log output.
Official references: the Certbot automated renewal documentation and the Let's Encrypt FAQ on certificate lifetime remain the authoritative sources for ACME behaviour and rate limits.
Frequently Asked Questions
0 Comments
Leave a comment
Your email is not published. Comments appear once they have been read. Sign in to have your details filled in.

