Kokil Thapa - Professional Web Developer in Nepal
Freelancer Web Developer in Nepal with 15+ Years of Experience

Kokil Thapa is an experienced full-stack web developer focused on building fast, secure, and scalable web applications. He helps businesses and individuals create SEO-friendly, user-focused digital platforms designed for long-term growth.

Automate Certificate Rotation

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.

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.

Automate Certificate Rotation LifecycleSchedulercron / systemdCertbotcertbot renewACME CALet's EncryptDeploy Hookreload nginxFailure Points You Must MonitorDNS challenge fails · Port 80 blocked · Hook reload skippedDisk full in /etc/letsencrypt · Rate limit hit · Cron path staleAlert if cert expires in < 30 days
Automate certificate rotation: scheduler triggers Certbot, ACME issues the cert, deploy hooks reload the web server

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.

ToolBest forRotation triggerOperational cost
Certbot + cron/systemdSingle VPS, Laravel/WordPress on UbuntuScheduled certbot renewLow — free, well documented
cert-managerKubernetes clustersCertificate CRD near expiryMedium — cluster addon to maintain
ACME client + DNS APIWildcard certs, behind CDN, no port 80DNS-01 challenge via API tokenMedium — API keys need rotation too
Cloud LB managed certsAWS ALB, GCP LB, CloudflareProvider-managedLow ops, vendor lock-in
HashiCorp Vault PKIInternal mTLS, microservicesShort TTL + auto-renew agentsHigh — 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.

Certificate Rotation Tool SelectionCertbot1–5 VPS hostsLaravel / WordPressRecommended startcert-managerKubernetes ingressMany microservicesCloud LBManaged certsMulti-region trafficDecision RuleTerminate TLS on the box you control → CertbotTerminate at ingress controller → cert-manager
Choose Certbot for VPS-hosted apps, cert-manager for Kubernetes, cloud LB for fully managed termination

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.

  1. Log Certbot output. Redirect renew stdout to a dated log. Scan for Congratulations or error strings weekly.
  2. External TLS probe. Run openssl s_client or a Nagios-style plugin from outside your network. Internal checks lie when the local cert file is fresh but the edge is not.
  3. 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.

Certificate Expiry Monitoring StackCertbot Logsrenew success/failExternal Probeopenssl / HTTPS checkCI Pre-deployblock bad releasesAlert: cert < 30 daysEmail · Slack · PagerDutyHealthy HTTPSUsers and crawlers see valid TLS
Layer Certbot logs, external HTTPS probes, and CI gates to catch failed automate certificate rotation before expiry

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_client after 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 --staging while 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.

Automate Certificate Rotation FailuresNo Reload HookCert renewed, server stalePort 80 BlockedHTTP-01 challenge failsStale Cron PathTimer never firesPrevention Checklistdeploy-hook reload · certbot renew --dry-run monthlyabsolute cron paths · external expiry probeTest after every deploy pipeline change
Prevent automate certificate rotation failures with deploy hooks, dry-run tests, and external expiry monitoring

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-run after 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

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.

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.

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.

Let's Encrypt certs expire every 90 days, so each hostname needs renewal roughly four times per year. Across subdomains, staging hosts, and client sites, manual renewal does not scale. One missed renewal triggers browser warnings, user bounce, and lost trust — painful on legal-tech portals handling document uploads. Automated rotation removes human memory from the loop: the ACME protocol issues certs, your scheduler handles timing, and deploy hooks reload the web server. You only intervene when automation fails, and with proper alerts you learn before users do.

On Ubuntu 22.04 or 24.04, install Certbot with the plugin matching your web server — certbot certonly --nginx or --apache for initial issuance. Test first with --dry-run --staging to avoid burning rate limits. Certbot writes renewal config to /etc/letsencrypt/renewal/. Verify the systemd timer is active with systemctl status certbot.timer; it runs certbot renew twice daily and only renews certs expiring within 30 days. On minimal VPS images without systemd timers, add cron with the full binary path /usr/bin/certbot renew. Always chain a deploy hook that reloads Nginx or Apache after renewal.

Tool choice depends on where certificates terminate and how many hosts you manage. Certbot with cron or systemd suits single VPS deployments running Laravel or WordPress on Ubuntu — free and well documented. cert-manager fits Kubernetes clusters where Certificate CRDs trigger renewal without shell access to nodes. ACME clients with DNS-01 via API token handle wildcard certs and origins hidden behind CDNs. Cloud load balancer managed certs on AWS ALB, GCP LB, or Cloudflare offer low ops but vendor lock-in. HashiCorp Vault PKI serves internal mTLS and microservices but carries high operational overhead. For most SMB sites, Certbot on Ubuntu with Apache or Nginx is sufficient.

HTTP-01 requires port 80 reachable from the public internet, which breaks when origin servers sit behind a CDN, staging hosts are internal-only, or you need wildcard certificates. Switch to DNS-01 validation: Certbot creates a TXT record proving domain control, and wildcard certs become possible. Install python3-certbot-dns-cloudflare, store API credentials in a root-owned file with chmod 600, and issue with --dns-cloudflare. Renewal reuses the same DNS plugin automatically and the systemd timer path stays identical. Rotate the API token on the same schedule you rotate SSH keys, and never store credentials in git or Deployer recipes.

Automation without monitoring is optimism — Certbot can succeed while your deploy hook fails, DNS changes, or disk fills in /etc/letsencrypt. Build three layers. First, log Certbot renew output to a dated file and scan weekly for success or error strings. Second, run external TLS probes using openssl s_client from outside your network, because internal checks lie when the local cert file is fresh but the edge is not. Third, alert when any production cert drops below 30 days remaining. Wire a check script into cron plus email, Uptime Kuma, or a CI scheduled job. Before major releases, add a GitLab CI pre-deploy gate that fails if staging TLS expires within seven days.

The most common failures repeat across client stacks. A missing or wrong reload hook leaves the new cert on disk while Nginx or Apache serves the old one from memory — verify with openssl s_client after forced renewal. Port 80 blocked after UFW hardening breaks HTTP-01 unless you move to DNS-01. Stale cron paths after Deployer symlink swaps point at old release directories — pin /usr/bin/certbot with absolute paths. Repeated failed attempts hit Let's Encrypt rate limits of 50 certs per registered domain per week — use --staging while debugging. Running snap Certbot and apt Certbot on the same host causes conflicting timers. CDN orange-cloud masking means the origin cert expires unnoticed while the edge looks healthy.

certbot certonly obtains a new certificate during initial setup or when adding hostnames interactively. It uses your web-server plugin or DNS plugin, writes the cert to /etc/letsencrypt, and creates a renewal config file under /etc/letsencrypt/renewal/ that every future rotation reuses. certbot renew is the scheduled command your systemd timer or cron job runs twice daily. It checks all stored renewal configs and only re-issues certificates expiring within 30 days. You do not run certonly again unless adding domains or migrating servers. After infrastructure changes, test the renewal path with certbot renew --dry-run rather than re-running certonly.

Renewal without reload is a silent failure. Create a script at /usr/local/bin/reload-webserver.sh that checks whether Nginx or Apache is active and runs systemctl reload on the correct service. Register it globally by adding deploy-hook = /usr/local/bin/reload-webserver.sh to /etc/letsencrypt/cli.ini, or set renew_hook per domain in /etc/letsencrypt/renewal/example.com.conf. Per-domain hooks help when one certificate fronts multiple services. After any hook change, test with certbot renew --dry-run. The same deploy discipline applies after symlink swaps — reload any service that caches state, not just the web server.

Certbot on Ubuntu is free with low operational overhead. cert-manager and DNS-01 plugins add medium maintenance cost for cluster addons and API key rotation.

Certbot works well for single-server and small-fleet Linux deployments until you hit ten-plus domains or multi-cloud load balancers. cert-manager is the better fit for Kubernetes clusters: it watches Certificate custom resources and renews before expiry without requiring shell access to nodes. Choose Certbot for VPS-hosted Laravel, WordPress, or Apache/Nginx apps where you SSH into each host. Move to cert-manager when your workloads run in Kubernetes and you need centralized, declarative TLS management across many services and namespaces.

Always run certbot renew --dry-run after every infrastructure change — firewall rules, DNS moves, Deployer path updates, or deploy hook edits. For initial issuance, use certbot certonly with --dry-run --staging against Let's Encrypt staging environment so you do not burn production rate limits while debugging validation. After a forced renewal test, verify the running server actually serves the new cert with openssl s_client -servername example.com -connect example.com:443. Treat dry-run failures with the same urgency as a cert expiring tomorrow, because the same DNS, port, or hook problem will break scheduled renewal within weeks.

When migrating a site to a new server, export the entire /etc/letsencrypt directory or re-issue cleanly on the new host before DNS cutover. Do not delete renewal config files during migrations — Certbot reuses /etc/letsencrypt/renewal/example.com.conf for every future rotation. Running two active hosts against the same domain burns Let's Encrypt rate limits fast. Document which server holds the active ACME account key; losing that key is recoverable but painful because you must re-validate every hostname from scratch. For enterprise clients needing compliance evidence, store Certbot logs in a central bucket with timestamps and review quarterly.

Share this article

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.

Quick Contact Options
Choose how you want to connect me: