Overview
Let's Encrypt issues free, trusted TLS certificates and automates renewal. Certbot is the official client that configures your web server and installs a renewal timer. This tutorial covers issuance, renewal, wildcard certificates, and troubleshooting.
How Let's Encrypt Works
| Concept | Description |
|---|---|
| ACME protocol | Automated Certificate Management Environment, used to request and renew |
| Certificate authority | Let's Encrypt |
| Validation | Proves you control the domain |
| Validity | 90 days, renewed automatically |
| Rate limits | 50 certificates per registered domain per week |
Validation Methods
| Method | How it works | Use case |
|---|---|---|
| HTTP-01 | Places a file at /.well-known/acme-challenge/ | Standard web servers |
| DNS-01 | Creates a TXT record | Wildcard certificates, no public HTTP |
| TLS-ALPN-01 | Responds on port 443 with a special certificate | Only HTTPS available |
Install Certbot
# Ubuntu / Debian
sudo apt update
sudo apt install certbot python3-certbot-nginx
# CentOS / RHEL (via snap is recommended)
sudo dnf install certbot python3-certbot-nginx
# macOS
brew install certbot
Full installation instructions are on the Certbot official site.
Nginx: Automated Configuration
sudo certbot --nginx -d example.com -d www.example.com
Certbot will:
- Verify domain ownership via HTTP-01.
- Obtain the certificate.
- Modify your Nginx configuration to listen on 443 and redirect HTTP to HTTPS.
- Install a systemd timer for renewal.
Verify the automatic changes:
sudo nginx -t
sudo systemctl reload nginx
Apache: Automated Configuration
sudo apt install python3-certbot-apache
sudo certbot --apache -d example.com -d www.example.com
Nginx: Manual Configuration
If you prefer not to let Certbot edit your config, use certonly:
sudo certbot certonly --nginx -d example.com -d www.example.com
Then configure Nginx yourself:
server {
listen 80;
server_name example.com www.example.com;
return 301 https://$host$request_uri;
}
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_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
root /var/www/html;
index index.html;
}
Standalone Mode
For servers without a web server running (or during initial setup):
sudo certbot certonly --standalone -d example.com
Port 80 must be reachable from the internet and not in use by another process.
Wildcard Certificates
Wildcards require DNS-01 validation, which means Certbot must modify DNS records. Use a DNS plugin for your provider.
# Example: Cloudflare
sudo apt install python3-certbot-dns-cloudflare
# /root/.secrets/cloudflare.ini
dns_cloudflare_api_token = YOUR_TOKEN
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"
Wildcards cover one level: *.example.com matches api.example.com but not a.b.example.com.
Renewal
Certbot installs a systemd timer or cron job that runs twice daily and renews certificates within 30 days of expiry.
# Check the timer
systemctl list-timers | grep certbot
# Dry-run a renewal to confirm it works
sudo certbot renew --dry-run
# Force renewal (only if needed)
sudo certbot renew --force-renewal
Never rely on manual renewal. A certificate that silently expires produces a browser security warning and takes down the site.
Reloading the Web Server After Renewal
Certbot supports hooks that run before and after renewal:
sudo certbot renew \
--deploy-hook "systemctl reload nginx"
For a persistent hook, place a script in /etc/letsencrypt/renewal-hooks/deploy/:
#!/bin/bash
# /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh
systemctl reload nginx
sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh
Certificate Files
| File | Purpose |
|---|---|
fullchain.pem | Certificate plus intermediate chain; use this for ssl_certificate |
privkey.pem | Private key |
cert.pem | Leaf certificate only |
chain.pem | Intermediate certificates |
All files live under /etc/letsencrypt/live/<domain>/ and are symlinks to the current version in archive/.
Rate Limits
Let's Encrypt enforces several limits:
- 50 certificates per registered domain per week
- 5 duplicate certificates per week for the same exact set of names
- 300 new orders per account per 3 hours
- 5 failed validations per account, hostname, and hour
Use the staging environment to test:
sudo certbot --staging --nginx -d example.com
Staging certificates are not trusted by browsers, but they do not count against production limits.
Troubleshooting
| Error | Cause | Fix |
|---|---|---|
Timeout during connect | Port 80 blocked | Open the firewall or use DNS-01 |
Invalid response from ... | Challenge file not served | Check Nginx serves /.well-known/acme-challenge/ |
too many certificates already issued | Weekly rate limit reached | Wait or use staging |
No such authorization | Stale ACME account state | Re-register with certbot register |
| Certificate expired | Renewal failed silently | Test renewal monthly and monitor expiry |
Best Practices
- Always test with
--dry-runafter initial setup. - Set up monitoring for certificate expiry as a backstop.
- Use a deploy hook to reload the web server after each renewal.
- Prefer DNS-01 for wildcards and for servers without public HTTP.
- Keep port 80 open so HTTP-01 challenges can complete; redirect to HTTPS after validation.
- Back up
/etc/letsencryptincluding account credentials.
