Let's Encrypt: Practical Experience Over Time
Let's Encrypt changed the operational economics of TLS. Before it, deploying HTTPS on a small server meant navigating commercial CA purchase flows, validating organisation details, and managing certificate files with varying expiry dates. After it, HTTPS became a cron job. The 90-day certificate lifetime forces automation, which eliminates the manual renewal failures that caused a disproportionate share of HTTPS problems. This page documents accumulated experience running Let's Encrypt across multiple production configurations — renewal automation approaches, the rate limits that catch unprepared deployments, CAA record configuration, OCSP stapling behaviour, and the gotchas that only show up after months of production operation. The context is direct server administration, not managed hosting where TLS is abstracted away. This page is part of the security section. For TLS server configuration context, the Apache mod_brotli and HTTPS setup notes cover complementary ground, and the privacy and security topic hub provides broader context.
The 90-day certificate lifetime
Let's Encrypt issues certificates with a 90-day validity period by design. The intended effect is to make manual certificate management operationally unsustainable, pushing operators toward automated renewal. This worked. Organisations running Let's Encrypt with manual processes eventually experience a certificate expiry event, add automation, and then rarely have expiry problems again. The 90-day limit also bounds the exposure window for compromised private keys, since even without revocation a certificate becomes invalid within 90 days.
The standard recommendation is to configure renewal at 60 days — with 30 days of renewal window before expiry. This provides time to notice and fix automation failures before the certificate actually expires. Certbot's default renewal timer runs twice daily and renews certificates within 30 days of expiry, which is conservative. More aggressive schedules (renew at 45 days) increase the renewal failure window but reduce the frequency of ACME round-trips.
Automation approaches
The two dominant automation approaches are Certbot with systemd timers (on modern Linux systems) and direct ACME client integration in web servers or reverse proxies.
# Check renewal timer status:
systemctl status certbot.timer
# Test renewal without actually renewing:
certbot renew --dry-run
# Check what certificates are managed and their expiry:
certbot certificates
For servers running Caddy or Traefik, TLS management is built into the server itself — no separate ACME client needed. Caddy requests, renews, and reloads certificates automatically without external tooling. For Apache and Nginx, Certbot with deploy hooks is the most common setup.
# /etc/letsencrypt/renewal-hooks/deploy/reload-apache.sh
#!/bin/bash
systemctl reload apache2
Deploy hooks run after successful renewal. Without a reload hook, Apache continues serving the old certificate until the next process restart, even though Certbot has written the new files.
Rate limits
Let's Encrypt rate limits are the most common operational surprise for administrators deploying multiple hostnames or running staging environments.
The limit that causes the most pain in practice is the Certificates per Registered Domain limit: 50 new certificates per registered domain per week. This sounds generous until you're deploying a staging environment alongside production, testing deployment automation, or managing a fleet of subdomains. Each test issuance that touches the production domain counts against this limit. Staging environment ACME endpoint (--staging flag in Certbot) uses a separate, much higher rate limit and should be used for all automation testing.
The rate limits to know:
- 50 certificates per registered domain per week
- 5 duplicate certificates per week (same hostname set, regardless of key)
- 5 failed validation attempts per hostname per hour
- 300 new orders per account per 3 hours
Hitting the "5 failed validation attempts per hostname per hour" limit during firewall or DNS misconfiguration testing causes a one-hour lockout on that hostname. The lockout is silent — Certbot reports validation failure but the reason is rate limiting, not the validation error. Adding a sleep between retry attempts is not sufficient; the lockout is based on the hostname, not the account.
Challenge types in practice
HTTP-01 validation — placing a file at /.well-known/acme-challenge/ — is the simplest setup and works for standard web servers. DNS-01 validation is required for wildcard certificates and useful for servers that don't expose port 80.
# Wildcard requires DNS-01 challenge (manual or automated via DNS plugin):
certbot certonly \
--manual \
--preferred-challenges dns \
-d "example.com" \
-d "*.example.com"
# Manual: prompts for a TXT record to add at _acme-challenge.example.com
DNS-01 automation requires a Certbot DNS plugin for your DNS provider (Route53, Cloudflare, etc.) or an ACME client with native DNS provider support. The operational complexity is higher than HTTP-01 but wildcard certificates reduce the number of distinct certificate files to manage on a multi-subdomain setup.
CAA records
DNS Certification Authority Authorisation records tell compliant CAs which CAs are permitted to issue certificates for a domain. Let's Encrypt checks CAA records before issuance.
example.com. IN CAA 0 issue "letsencrypt.org"
example.com. IN CAA 0 issuewild "letsencrypt.org"
example.com. IN CAA 0 iodef "mailto:admin@example.com"
A CAA record that omits letsencrypt.org will cause Let's Encrypt issuance to fail. A missing CAA record permits any CA. The practical value of CAA is reducing the risk of misissuance from a compromised or negligent CA — it does not prevent all certificate fraud but raises the bar.
OCSP stapling
Let's Encrypt certificates use OCSP for revocation checking. OCSP stapling — where the server fetches and caches the revocation status and includes it in the TLS handshake — avoids the client needing to contact Let's Encrypt's OCSP responder during every connection.
Let's Encrypt announced plans to deprecate OCSP in favour of short-lived certificates as a revocation mechanism. The operational implication is that future Let's Encrypt certificates may have validity periods shorter than 90 days, with renewal automation becoming even more critical. For now, OCSP stapling remains the standard approach and should be configured where the web server supports it. Apache's SSLUseStapling and Nginx's ssl_stapling directives handle this.
Verify stapling is working after configuration:
openssl s_client -connect example.com:443 -status -servername example.com < /dev/null 2>/dev/null \
| grep -A 10 'OCSP response'
# Look for: OCSP Response Status: successful (0x0)
Operational lessons
After running Let's Encrypt across multiple years and configurations, the failure modes that recur are: renewal automation that works initially but silently fails after a system change (cron disabled, network change, DNS update), CAA records added during domain migration without checking LE compatibility, and staging environments that exhaust rate limits on the production domain. Monitoring certificate expiry independently of the renewal automation — via external certificate monitoring or a simple script that checks openssl s_client output — catches the automation failures that don't produce visible alerts.