What Certbot Is and How ACME Works
If you've ever been jolted awake at 2 AM by a monitoring alert because a certificate expired on a production load balancer, you already know why automation matters. Certbot exists to make sure that never happens again.
Certbot is an open-source ACME client — built and maintained by the Electronic Frontier Foundation — that handles requesting, installing, and renewing TLS certificates from any ACME-compatible certificate authority. Most people associate it with Let's Encrypt, but it's not locked to a single CA. Point the --server flag at a different ACME directory URL, and Certbot will happily talk to that CA instead.
A couple of things worth knowing up front:
- ACME (Automatic Certificate Management Environment) is an IETF standard defined in RFC 8555. It isn't proprietary — any CA can implement it.
- Certbot's "vendor-neutral" design means your automation scripts don't break if you switch CAs down the road.
ACME Objects and the Issuance Flow
Here's what actually happens under the hood when Certbot requests a certificate. The ACME protocol coordinates around a handful of core objects — account, order, authorization, challenge, and certificate — and the whole dance looks like this:
- Account registration — Certbot creates (or authenticates) an account with the CA using JWS. Think of this as your identity with the CA.
- New order — Certbot says "I'd like a certificate for these domains."
- Authorizations & challenges — The CA responds with challenge options (HTTP-01, DNS-01, or TLS-ALPN-01) for each domain.
- Challenge response — Certbot publishes the proof — a file on port 80, a DNS TXT record, or a TLS handshake token.
- Validation — The CA reaches out and verifies you actually control those domains.
- Finalize — Certbot submits a CSR; the CA signs and returns your certificate chain.
- Installation — Certbot drops the cert into place and reloads your web server (if you've configured it to do so).
Pro tip: Your ACME account key is your identity with the CA — not the certificate itself. If you lose the account key, you can still get new certs, but you won't be able to manage existing orders. Back it up.
Renewal Is the Same Protocol, Repeated Safely
There's no special "renewal" endpoint in ACME. Renewal is just a fresh order placed before the current cert expires. Let's Encrypt issues 90-day certificates and recommends renewing at the 60-day mark, giving you a solid 30-day buffer.
Certbot handles this automatically through certbot renew, which checks all your certificates and only renews the ones that are due. Always test with certbot renew --dry-run after any config change — it'll catch problems before they bite you in production. Prefer not to run an ACME client yourself? You can get a free auto-renewing SSL certificate through a managed dashboard instead.
ACME Validation Methods
Before you can get a certificate, you need to prove you control the domain. ACME gives you three ways to do this, and picking the right one will save you real headaches later.
For most teams, HTTP-01 is the right starting point — it's simple, it works, and it doesn't require DNS API access. DNS-01 is where things get interesting, and it's where most production setups end up, especially once wildcard certs or CDN-fronted architectures enter the picture.
Comparison Table
| Challenge | What You Prove | Network Requirements | Wildcards | Best For | Watch Out For |
|---|---|---|---|---|---|
| HTTP-01 | You serve content on the domain | CA must reach port 80 and fetch a token file under /.well-known/acme-challenge/ | No | Simple single-host web servers | Port 80 blocked; redirect loops; multi-server token sync |
| DNS-01 | You control the domain's DNS zone | CA queries DNS for a TXT record at _acme-challenge.<domain> | Yes | Wildcards, multi-region setups, CDN/LB-fronted services | DNS API credential exposure; propagation delays can bite you |
| TLS-ALPN-01 | You control TLS on the domain | CA connects to port 443 using ALPN "acme-tls/1" and validates a special cert | No | Environments where port 80 simply isn't an option | Requires specialized ACME client support (not Certbot — yet) |
HTTP-01 Flow and Security Notes
How it works: The CA says "put this token at this URL on your domain." Certbot writes the file; the CA fetches it over plain HTTP.
Things to watch for:
- HTTP-01 won't work for wildcard certificates — full stop.
- If you're running multiple web servers behind a load balancer, every backend needs to be able to serve the challenge token. We've seen teams spend hours debugging this because their LB was routing the CA's request to the wrong backend.
- Some web server configs block or redirect
/.well-knownpaths. Make sure/.well-known/acme-challenge/is served as-is, with no redirects.
DNS-01 Flow and Security Notes
How it works: Certbot constructs a proof using the challenge token and your account key, then publishes it as a DNS TXT record at _acme-challenge.<domain>. The CA queries DNS and verifies the value.
Here's the thing about DNS-01 — it's powerful, but the security implications are real. DNS API credentials that can create arbitrary TXT records can often do much more than that. Let's Encrypt explicitly warns about this: if those credentials leak, an attacker could potentially take over your entire domain.
Our recommendation: Use narrowly scoped API tokens (Cloudflare's "Edit zone DNS" permission, for example) and consider running DNS validation from a separate, hardened machine that copies certificates to production afterward.
Also, watch out for propagation delays. We've seen DNS-01 fail because a provider advertised a 30-second TTL but actually took 3+ minutes to propagate. If you're hitting intermittent failures, bump the propagation wait time.
TLS-ALPN-01 and the Certbot Reality
TLS-ALPN-01 (RFC 8737) lets you prove domain control through a special TLS handshake on port 443, which is handy when port 80 is completely off the table.
But here's the catch: Certbot doesn't support TLS-ALPN-01 yet. If you need it, you'll have to look at alternative ACME clients like Caddy or lego. For most setups, though, you can work around this with DNS-01.
Using Certbot for Automated Issuance and Renewals
Let's get practical. This section covers installation, plugin choices, and the most common deployment patterns.
Installation Overview and Directory Layout
The Certbot project recommends installing via snap on most Linux systems. This keeps you on the latest version and avoids the stale-package problem that plagues distro repositories.
Certbot organizes everything under /etc/letsencrypt/:
live/<cert-name>/— symlinks to your current cert, key, and chain filesrenewal/<cert-name>.conf— renewal configuration for each certificatearchive/<cert-name>/— historical versions of your certs
Ubuntu with systemd (snap + timer)
Install Certbot and set up the binary:
sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/local/bin/certbotNow grab a certificate. You've got two main options:
Option A: Nginx plugin (auto-configures your Nginx server blocks)
sudo certbot --nginx -d example.com -d www.example.comOption B: Webroot (zero downtime — your web server keeps running)
sudo certbot certonly --webroot -w /var/www/html -d example.com -d www.example.comAlways test that renewal works:
sudo certbot renew --dry-runAnd verify the timer is active:
systemctl list-timers | grep -i certbot
journalctl -u snap.certbot.renew.service --no-pager -n 200Debian (snap recommended)
Same snap flow as Ubuntu — Certbot's docs recommend it across both distributions. If you must use Debian packages, just make sure you're getting a recent enough version and confirm that a cron job or systemd timer was created for auto-renewal.
CentOS/RHEL-Family Notes
Here's where things can get tricky. CentOS/RHEL packaging and lifecycle differences mean the distro-provided Certbot package might be significantly outdated. We've seen snap vs. apt/yum conflicts cause silent failures — our advice is to pick one installation method and stick with it.
If snap isn't available on your platform, OS package installs work, but verify the version and renewal mechanism manually. Certbot's own docs warn that third-party packages can fall out of date quickly.
Apache Integration
The Apache plugin works the same way as the Nginx one — it obtains and installs certificates automatically:
sudo certbot --apache -d example.com -d www.example.comReverse Proxies, CDNs, and Load Balancers
The right approach depends on where TLS terminates:
- TLS terminates on an Nginx/Apache reverse proxy you control: The
--nginxor--apacheplugins are usually the simplest path. - TLS terminates on a managed load balancer or CDN: You probably can't install certs directly on the edge device. The common pattern is to obtain certificates on a management host using DNS-01, then push them to the LB/CDN via that provider's API.
- CDN in front of your origin: HTTP-01 can still work if the CDN forwards
/.well-known/acme-challenge/to your origin without caching or rewriting. But DNS-01 sidesteps all CDN complications and is the safer baseline.
Multi-Domain (SAN) and Wildcard Certificates
Multi-Domain (SAN)
Need one certificate covering multiple hostnames? Just stack -d flags:
sudo certbot certonly --nginx \
-d example.com -d www.example.com -d api.example.comLet's Encrypt fully supports SAN certificates — you can include up to 100 names in a single cert.
Wildcard Certificates
Wildcards require DNS-01 validation — there's no way around this:
sudo certbot certonly \
--dns-cloudflare \
-d "example.com" -d "*.example.com"Important: If you used --manual for the DNS challenge, Certbot won't be able to auto-renew that certificate. You'll need to either use a DNS plugin or write custom auth hooks for unattended renewals.
DNS-01 Automation with Cloudflare and Route 53 Plugins
Manual DNS-01 is fine for a one-off test, but in production you want a plugin that creates and cleans up TXT records automatically. Here are the two most common setups.
Snap Install of DNS Plugin (Cloudflare Example)
sudo snap install certbot-dns-cloudflare
sudo snap set certbot trust-plugin-with-root=okCreate a credentials file (and lock it down):
# /root/.secrets/certbot/cloudflare.ini
dns_cloudflare_api_token = YOUR_CLOUDFLARE_API_TOKENsudo chmod 600 /root/.secrets/certbot/cloudflare.iniNow issue your cert:
sudo certbot certonly \
--dns-cloudflare \
--dns-cloudflare-credentials /root/.secrets/certbot/cloudflare.ini \
-d "example.com" -d "*.example.com"Pro tip: Use a Cloudflare API token scoped to "Zone: DNS: Edit" for just the zone you need — not a global API key. The blast radius matters.
Route 53 Plugin
The Route 53 plugin handles TXT record creation via the AWS API. Your AWS credentials come from the usual places — environment variables, instance profile, IAM role, or shared credentials file.
sudo certbot certonly \
--dns-route53 \
-d "example.com" -d "*.example.com"Generic DNS-01 Automation (Pseudo-Code)
If your DNS provider doesn't have an official plugin, you can write custom hooks. The logic looks like this:
function Present(domain, token, keyAuth):
txtValue = Base64Url(SHA256(keyAuth))
CreateTXTRecord("_acme-challenge." + domain, txtValue)
WaitForDNSPropagation()
function Cleanup(domain):
DeleteTXTRecord("_acme-challenge." + domain)Docker Workflows
Running Certbot in Docker? It works, but there are a few gotchas that catch people constantly.
The #1 mistake we see: forgetting to persist /etc/letsencrypt as a Docker volume. If that directory lives inside the container and the container gets recreated, you lose your account keys, your renewal configs, and your certificate history. Worse, you'll likely hit Let's Encrypt rate limits trying to re-issue everything.
HTTP-01 Standalone in Docker
This approach binds port 80 directly. You'll need to stop your webserver temporarily:
docker run --rm -it \
-p 80:80 \
-v /etc/letsencrypt:/etc/letsencrypt \
-v /var/lib/letsencrypt:/var/lib/letsencrypt \
-v /var/log/letsencrypt:/var/log/letsencrypt \
certbot/certbot certonly --standalone \
-d example.com \
--email admin@example.com --agree-tos --non-interactiveDNS-01 Using Route 53 Plugin Image
docker run --rm -it \
-v /etc/letsencrypt:/etc/letsencrypt \
-v /var/lib/letsencrypt:/var/lib/letsencrypt \
-v /var/log/letsencrypt:/var/log/letsencrypt \
-e AWS_ACCESS_KEY_ID="YOUR_AWS_ACCESS_KEY" \
-e AWS_SECRET_ACCESS_KEY="YOUR_AWS_SECRET_KEY" \
certbot/dns-route53 certonly --dns-route53 \
-d "example.com" -d "*.example.com" \
--email admin@example.com --agree-tos --non-interactiveKubernetes: cert-manager Integration for ACME Automation
If you're running Kubernetes, cert-manager is the way to go — and honestly, it's the only sane approach at scale. Rather than running Certbot as a sidecar or cron job in individual pods, cert-manager handles ACME issuance and renewal as a native Kubernetes resource.
Minimal ClusterIssuer for ACME
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: acme-issuer
spec:
acme:
email: admin@example.com
server: https://acme-v02.api.letsencrypt.org/directory
privateKeySecretRef:
name: acme-issuer-account-key
solvers:
- http01:
ingress:
class: nginxRequest a Certificate
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: example-com
spec:
secretName: example-com-tls
dnsNames:
- example.com
- www.example.com
issuerRef:
name: acme-issuer
kind: ClusterIssuerNeed wildcards in Kubernetes? Switch the solver to DNS-01 — same principle, same _acme-challenge TXT records, but cert-manager handles it all declaratively.
Automated Renewals, Hooks, and Production Best Practices
Understand Your Renewal Mechanism (Cron vs systemd)
Most Certbot installs ship with renewal already scheduled — but you'd be surprised how often this gets broken during upgrades or container rebuilds. Always verify that a timer or cron job actually exists.
| Mechanism | Pros | Cons | How to Inspect |
|---|---|---|---|
| systemd timer | Centralized logs via journald, dependency ordering | Needs systemd familiarity | systemctl list-timers, journalctl -u <service> |
| cron | Simple, portable, works everywhere | Logs can be scattered; no dependency control | Check /etc/crontab, /etc/cron.*/* for certbot renew |
A Safe Default Renewal Schedule
Let's Encrypt recommends renewing 90-day certificates around the 60-day mark. Run renewal checks twice daily — Certbot's smart enough to only renew certificates that are actually due, so frequent checks don't cause problems.
Here's the cron entry from Certbot's own docs (with randomized sleep to avoid thundering herds):
SLEEPTIME=$(awk 'BEGIN{srand(); print int(rand()*(3600+1))}'); \
echo "0 0,12 * * * root sleep $SLEEPTIME && certbot renew -q" | \
sudo tee -a /etc/crontab > /dev/nullReloading Services Safely with Hooks
Here's the part most people forget, and it's the part that causes outages: the deploy hook. Certbot can renew your certificate perfectly, but if your web server doesn't reload, it'll keep serving the old (potentially expired) cert.
Deploy hook to reload Nginx after renewal:
sudo mkdir -p /etc/letsencrypt/renewal-hooks/deploy
sudo tee /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh >/dev/null <<'SH'
#!/bin/sh
systemctl reload nginx
SH
sudo chmod 755 /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.shCertbot also supports pre and post hooks in /etc/letsencrypt/renewal-hooks/ — useful if you need to stop a service for standalone validation and restart it afterward.
If your pipeline also signs binaries alongside TLS automation, note that code signing certificates and the hardware-key rule work differently — the private key has to live on a FIPS-certified token or HSM, so plan for a cloud or HSM-backed signing setup rather than reusing the same automation pattern.
Staging vs Production, Dry Runs, and Rate Limits
Don't learn about rate limits the hard way. Let's Encrypt enforces strict production limits (like 50 certificates per registered domain per week), and if you burn through them during testing, you're stuck waiting.
Always start with staging:
sudo certbot certonly --nginx \
--server https://acme-staging-v02.api.letsencrypt.org/directory \
-d example.comStaging certificates won't be trusted by browsers, but they prove your automation works end to end. Once everything passes, switch to production.
Key things to remember:
- Persist your ACME account material and
/etc/letsencryptstate, especially in containers. Losing this state means re-registering and potentially re-issuing — both of which count against rate limits. - Run
certbot renew --dry-runafter every configuration change. Make it a habit.
Key Management, File Permissions, and Backups
Your private keys never leave your server — Let's Encrypt doesn't generate or hold them. That means protecting /etc/letsencrypt is entirely your responsibility.
Best practices we recommend:
- Treat
/etc/letsencryptlike a secrets vault. Private key files should be0600, and the directory structure should be readable only by root and the processes that need the certs. - Back up regularly:
- /etc/letsencrypt (keys, certificate lineages, renewal configs)
- Any DNS plugin credential files
- If you're restoring from backup, run a staging dry-run before trusting production renewal. Stale configs can surprise you.
Monitoring and Alerting Recommendations
Even with perfect automation, things go wrong — DNS providers have outages, servers get misconfigured during maintenance, containers get rebuilt without volumes. Two monitoring layers will catch these before your users notice.
Certificate Expiry at the Service Edge (What Users See)
Run a daily check against your public endpoints:
Alert thresholds we recommend:
- ⚠️ Warning at ≤21 days
- 🚨 Critical at ≤7 days
- 📟 Page on-call at ≤3 days
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null \
| openssl x509 -noout -datesRenewal Job Health (What Automation Is Doing)
Set up alerts for:
- Any non-zero exit code from the renewal job
- No successful renewal logged in the last N days (tune N based on your schedule)
Check the logs at /var/log/letsencrypt/letsencrypt.log and your systemd/cron output.
Pro tip: Use My-SSL's SSL Checker for quick external certificate validation, and set up SSL Expiry Reminders as a safety net — because the best monitoring is the kind that doesn't depend on the same infrastructure it's monitoring.
Troubleshooting and Common Failure Modes
These are the issues we see most often in support conversations. If you're stuck, start here.
HTTP-01 Failures
What you'll see: The CA can't fetch /.well-known/acme-challenge/...
Check these first:
- Is port 80 actually reachable from the internet? (Not just from your local network.)
- Is your web server serving
/.well-known/acme-challenge/without blocking or redirecting it? - If you're behind an LB or CDN, is the challenge path being forwarded to your origin correctly?
curl -I http://example.com/.well-known/acme-challenge/test
sudo certbot -v certonly --webroot -w /var/www/html -d example.comDNS-01 Failures
What you'll see: "TXT record not found" or "Incorrect TXT record"
Debugging steps:
- Query the authoritative nameserver directly to confirm the record exists:
dig +short TXT _acme-challenge.example.com- If the record looks right but validation still fails, you're likely hitting a propagation delay. Increase the propagation wait time in your plugin config.
- Double-check that your DNS API token is scoped correctly and hasn't expired.
Rate Limit Failures
What you'll see: Errors about issuance limits being exceeded.
How to recover:
- Switch to staging for all further testing
- Stop deleting state or re-issuing repeatedly
- Wait for the rate limit window to pass (usually 7 days), and read the Let's Encrypt rate limits documentation
Renewal Succeeds but Service Still Serves Old Certificate
This almost always means your web server didn't reload after renewal. The fix: add a deploy hook that runs systemctl reload nginx (or the equivalent for your server).
Also confirm your web server config points to /etc/letsencrypt/live/<name>/fullchain.pem and privkey.pem — not copied files. Certbot updates the symlinks in live/ on each renewal, so if you copied the files elsewhere, those copies are now stale.
"Manual" Wildcard Certs Don't Auto-Renew
If you used --manual for DNS-01 without an automation script, Certbot can't renew automatically. You'll need to switch to a DNS plugin or implement custom auth/cleanup hooks. This is the number one cause of unexpected wildcard certificate expirations.
Migration from Manual Certificates to Certbot Automation
If you're migrating from manually purchased or manually renewed certs, here's a battle-tested approach that minimizes risk:
- Inventory everything — document current certificates, their SANs, and where they're configured
- Stage first — obtain a new certificate with Certbot against staging, matching all FQDNs and SANs
- Update server config — point your web server to the Certbot-managed paths under
/etc/letsencrypt/live/<cert-name>/ - Validate thoroughly — check with a browser, with
openssl s_client, and with an external SSL checker - Confirm renewal works —
certbot renew --dry-runshould pass cleanly - Only then decommission the old manual renewal process
If you need to change renewal options for an existing Certbot-managed certificate, look into certbot reconfigure — and resist the urge to hand-edit renewal conf files without testing afterward.
Production Rollout Checklist
- ✅ Choose your validation method: HTTP-01 if you can reliably serve port 80; DNS-01 for wildcards, multi-region, or CDN/LB edge cases
- ✅ Test in staging first — always, before touching production
- ✅ Persist your state:
/etc/letsencryptand related directories must survive container rebuilds - ✅ Verify automated renewal: Check cron/systemd timers; run
certbot renew --dry-run - ✅ Add deploy hooks: Services must reload on successful renewal — this is non-negotiable
- ✅ Set up external monitoring: Certificate-expiry alarms (≤21d warn, ≤7d critical, ≤3d page)
- ✅ Alert on renewal failures: Watch renewal job logs — silence isn't always golden
- ✅ Have a backup plan: Back up private keys, renewal configs, and document your restore steps
Frequently Asked Questions
Need Purchased SSL Instead of Let's Encrypt?
Get OV/EV validation, warranty protection, and professional support with a purchased SSL certificate.
DV SSL Certificate
Starting at $3.99/year
- Domain Validation
- 5-Min Issuance
- Unlimited Server Licenses
- Up to $1.75M Warranty
Frequently Asked Questions
Check Your SSL Certificate Now
Use our free SSL Checker to verify your certificate chain, expiry, and configuration — works with Let's Encrypt and purchased certs.
SSL Checker Tool
Starting at Free/year
- Certificate Chain Validation
- Expiry Monitoring
- Configuration Check
- Set Up Email Reminders
Complement Certbot with External Monitoring
Even with automated renewals, set up free email reminders as a safety net. Get notified 30, 14, 7, and 1 day before any certificate expires.
Set Up Free Reminders