Let's Encrypt: Free TLS Certificate Automation
Let's Encrypt fundamentally changed how we approach TLS certificates. Before 2016, obtaining a certificate meant paying a certificate authority, dealing with manual verification processes, and...
Key Insights
- Let’s Encrypt provides free, automated TLS certificates through the ACME protocol, eliminating the cost and manual overhead of traditional certificate authorities while issuing over 3 million certificates daily.
- The HTTP-01 challenge is simplest for single domains, while DNS-01 enables wildcard certificates and works behind firewalls, making it essential for complex infrastructure deployments.
- Production environments require automated renewal strategies using systemd timers or cert-manager in Kubernetes, with monitoring to prevent unexpected certificate expirations that cause outages.
Understanding Let’s Encrypt and ACME
Let’s Encrypt fundamentally changed how we approach TLS certificates. Before 2016, obtaining a certificate meant paying a certificate authority, dealing with manual verification processes, and remembering to renew before expiration. Let’s Encrypt solved this by providing free certificates through complete automation.
The secret sauce is the ACME (Automated Certificate Management Environment) protocol. ACME defines a standardized way for certificate authorities to verify domain ownership and issue certificates programmatically. The protocol uses challenge-response mechanisms where you prove control of a domain by completing tasks the CA can verify externally.
Let’s Encrypt certificates expire after 90 days, not because they’re free, but by design. Short-lived certificates reduce the impact of compromised keys and force automation—manual renewal every 90 days would be painful enough that you’ll build proper automation.
Domain Validation Methods
Let’s Encrypt offers three validation methods, each suited for different scenarios.
HTTP-01 Challenge is the most common. The ACME client places a specific file at http://yourdomain.com/.well-known/acme-challenge/TOKEN. Let’s Encrypt’s servers fetch this file to verify you control the domain. This requires port 80 to be accessible and doesn’t work for wildcard certificates.
DNS-01 Challenge requires adding a TXT record to your DNS zone. Let’s Encrypt queries _acme-challenge.yourdomain.com to verify the record. This method works for wildcard certificates and doesn’t require any open ports, making it perfect for internal services or servers behind firewalls.
TLS-ALPN-01 Challenge uses a special TLS handshake on port 443. It’s less common but useful when port 80 isn’t available but 443 is.
Here’s how the ACME flow works:
Client Let's Encrypt CA
| |
|---(1) Certificate Request--------->|
|<---(2) Challenge Options-----------|
| |
|---(3) Challenge Response---------->|
| |
| (CA validates challenge) |
| |
|<---(4) Certificate Issued----------|
Be aware of rate limits: 50 certificates per registered domain per week, 5 duplicate certificates per week, and 300 new orders per account per 3 hours. These limits rarely affect normal usage but matter for large deployments.
Getting Started with Certbot
Certbot is the official ACME client from the Electronic Frontier Foundation. It’s battle-tested and handles the heavy lifting of certificate management.
Installation on Ubuntu/Debian:
sudo apt update
sudo apt install certbot python3-certbot-nginx
For RHEL/CentOS:
sudo yum install epel-release
sudo yum install certbot python3-certbot-nginx
To obtain your first certificate using the standalone method (requires stopping your web server temporarily):
sudo certbot certonly --standalone -d example.com -d www.example.com
The webroot method works while your server runs:
sudo certbot certonly --webroot -w /var/www/html -d example.com -d www.example.com
Certbot stores certificates in /etc/letsencrypt/live/example.com/. The key files are:
fullchain.pem- Your certificate plus intermediate certificatesprivkey.pem- Your private keycert.pem- Your certificate onlychain.pem- Intermediate certificates only
Configure Nginx to use your new certificate:
server {
listen 443 ssl http2;
server_name example.com www.example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
# Your application configuration
location / {
proxy_pass http://localhost:3000;
}
}
# Redirect HTTP to HTTPS
server {
listen 80;
server_name example.com www.example.com;
return 301 https://$server_name$request_uri;
}
For Apache:
<VirtualHost *:443>
ServerName example.com
ServerAlias www.example.com
SSLEngine on
SSLCertificateFile /etc/letsencrypt/live/example.com/cert.pem
SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pem
SSLCertificateChainFile /etc/letsencrypt/live/example.com/chain.pem
# Your application configuration
</VirtualHost>
Automated Renewal
Manual renewal defeats the purpose of Let’s Encrypt. Certbot includes renewal logic—you just need to schedule it.
Test renewal first:
sudo certbot renew --dry-run
Create a cron job for automatic renewal:
sudo crontab -e
Add this line to check twice daily (certificates renew when they have 30 days or less remaining):
0 0,12 * * * certbot renew --quiet --deploy-hook "systemctl reload nginx"
For systemd-based systems, use a timer instead. Create /etc/systemd/system/certbot-renewal.service:
[Unit]
Description=Let's Encrypt renewal
After=network-online.target
[Service]
Type=oneshot
ExecStart=/usr/bin/certbot renew --quiet --deploy-hook "systemctl reload nginx"
Create /etc/systemd/system/certbot-renewal.timer:
[Unit]
Description=Let's Encrypt renewal timer
[Timer]
OnCalendar=0/12:00:00
RandomizedDelaySec=1h
Persistent=true
[Install]
WantedBy=timers.target
Enable and start the timer:
sudo systemctl enable certbot-renewal.timer
sudo systemctl start certbot-renewal.timer
Pre and post-renewal hooks let you perform actions during renewal:
sudo certbot renew --pre-hook "systemctl stop nginx" \
--post-hook "systemctl start nginx" \
--deploy-hook "systemctl reload nginx"
Wildcard Certificates and DNS Automation
Wildcard certificates (*.example.com) require DNS-01 validation. You’ll need a DNS provider with API support.
Install the appropriate DNS plugin. For Cloudflare:
sudo apt install python3-certbot-dns-cloudflare
Create credentials file at /etc/letsencrypt/cloudflare.ini:
dns_cloudflare_api_token = your_api_token_here
Secure it:
sudo chmod 600 /etc/letsencrypt/cloudflare.ini
Request a wildcard certificate:
sudo certbot certonly \
--dns-cloudflare \
--dns-cloudflare-credentials /etc/letsencrypt/cloudflare.ini \
-d example.com \
-d '*.example.com'
For AWS Route53:
sudo apt install python3-certbot-dns-route53
Configure AWS credentials with appropriate IAM permissions, then:
sudo certbot certonly \
--dns-route53 \
-d example.com \
-d '*.example.com'
The DNS plugins handle record creation and cleanup automatically. Renewal works the same as HTTP-01 certificates.
Production Deployment Patterns
For Docker environments, use a sidecar container pattern. Here’s a Docker Compose setup:
version: '3.8'
services:
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
- certbot-etc:/etc/letsencrypt
- certbot-var:/var/lib/letsencrypt
- web-root:/var/www/html
depends_on:
- certbot
certbot:
image: certbot/certbot
volumes:
- certbot-etc:/etc/letsencrypt
- certbot-var:/var/lib/letsencrypt
- web-root:/var/www/html
entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h & wait $${!}; done;'"
volumes:
certbot-etc:
certbot-var:
web-root:
In Kubernetes, cert-manager is the standard solution. Install it:
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.13.0/cert-manager.yaml
Create a ClusterIssuer for Let’s Encrypt:
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: admin@example.com
privateKeySecretRef:
name: letsencrypt-prod
solvers:
- http01:
ingress:
class: nginx
Request a certificate via Ingress annotation:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: example-ingress
annotations:
cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
tls:
- hosts:
- example.com
secretName: example-tls
rules:
- host: example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: example-service
port:
number: 80
cert-manager automatically obtains and renews certificates, storing them in Kubernetes secrets.
Monitoring and Troubleshooting
Monitor certificate expiration to avoid outages. Create a check script:
#!/bin/bash
DOMAIN="example.com"
EXPIRY_DATE=$(echo | openssl s_client -servername $DOMAIN -connect $DOMAIN:443 2>/dev/null | openssl x509 -noout -enddate | cut -d= -f2)
EXPIRY_EPOCH=$(date -d "$EXPIRY_DATE" +%s)
NOW_EPOCH=$(date +%s)
DAYS_LEFT=$(( ($EXPIRY_EPOCH - $NOW_EPOCH) / 86400 ))
if [ $DAYS_LEFT -lt 14 ]; then
echo "WARNING: Certificate expires in $DAYS_LEFT days"
exit 1
fi
echo "Certificate valid for $DAYS_LEFT days"
For Prometheus monitoring, use blackbox_exporter:
- job_name: 'ssl-expiry'
metrics_path: /probe
params:
module: [http_2xx]
static_configs:
- targets:
- https://example.com
relabel_configs:
- source_labels: [__address__]
target_label: __param_target
- source_labels: [__param_target]
target_label: instance
- target_label: __address__
replacement: blackbox-exporter:9115
Common troubleshooting commands:
# Check certificate details
sudo certbot certificates
# Verbose renewal test
sudo certbot renew --dry-run --verbose
# Check if port 80 is accessible
sudo netstat -tlnp | grep :80
# Verify DNS propagation
dig _acme-challenge.example.com TXT
# Check Let's Encrypt logs
sudo tail -f /var/log/letsencrypt/letsencrypt.log
The most common issues are firewall rules blocking port 80, DNS records not propagating, or web server misconfigurations preventing access to .well-known/acme-challenge/. Always test with --dry-run before making changes to production certificates to avoid hitting rate limits.
Let’s Encrypt transformed TLS from a paid, manual process into free, automated infrastructure. With proper automation and monitoring, certificate management becomes invisible—exactly how it should be.