Let's Encrypt: Nginx Config, Wildcards & Renewal
Nginx Configuration
# /etc/nginx/sites-available/example.com
server {
listen 80;
server_name example.com www.example.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
http2 on;
server_name example.com www.example.com;
# Let's Encrypt certificates
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf; # generated by certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # generated by certbot
# HSTS
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
root /var/www/example.com/html;
index index.html;
location / {
try_files $uri $uri/ =404;
}
# For reverse proxy (Node.js, Rails, etc.)
location /api/ {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
# Enable site
# sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/
# sudo nginx -t && sudo systemctl reload nginxWildcard Certificates with DNS-01
# Wildcard requires DNS-01 challenge (HTTP-01 can't validate *.domain.com)
# Use a DNS provider plugin for automation
# Cloudflare DNS plugin
pip install certbot-dns-cloudflare
# or: sudo snap install certbot-dns-cloudflare
# Create Cloudflare credentials file
cat > /etc/letsencrypt/cloudflare.ini << 'EOF'
dns_cloudflare_email = your@email.com
dns_cloudflare_api_key = your_global_api_key
# Or use API token (more secure):
# dns_cloudflare_api_token = your_api_token
EOF
chmod 600 /etc/letsencrypt/cloudflare.ini
# Issue wildcard certificate
sudo certbot certonly --dns-cloudflare --dns-cloudflare-credentials /etc/letsencrypt/cloudflare.ini -d example.com -d '*.example.com'
# Other DNS plugins: certbot-dns-route53, certbot-dns-digitalocean,
# certbot-dns-google, certbot-dns-linode
# Manual DNS-01 (when no plugin available)
sudo certbot certonly --manual --preferred-challenges dns -d '*.example.com'
# Follow prompts: add TXT record _acme-challenge.example.com
# Verify record propagated before pressing Enter:
# dig TXT _acme-challenge.example.com @8.8.8.8Auto-renewal
# Certbot installs a systemd timer or cron job automatically
# Verify it's active
sudo systemctl status certbot.timer
sudo systemctl list-timers | grep certbot
# Or check cron
sudo cat /etc/cron.d/certbot
# Test renewal (dry run — doesn't actually renew)
sudo certbot renew --dry-run
# Force renewal (even if cert isn't expiring soon)
sudo certbot renew --force-renewal
# Renew specific domain only
sudo certbot renew --cert-name example.com
# Hook: reload nginx after renewal
sudo certbot renew --deploy-hook "systemctl reload nginx"
# Or add to /etc/letsencrypt/renewal/example.com.conf:
# [renewalparams]
# post_hook = systemctl reload nginx
# List all certificates and expiry dates
sudo certbot certificatesDocker & Cloud Alternatives
# Traefik (auto-TLS for Docker/Kubernetes)
# docker-compose.yml
# traefik:
# image: traefik:v3
# command:
# - --providers.docker=true
# - --entrypoints.web.address=:80
# - --entrypoints.websecure.address=:443
# - --certificatesresolvers.le.acme.tlschallenge=true
# - --certificatesresolvers.le.acme.email=admin@example.com
# - --certificatesresolvers.le.acme.storage=/letsencrypt/acme.json
# volumes:
# - /var/run/docker.sock:/var/run/docker.sock
# - ./letsencrypt:/letsencrypt
# ports: ["80:80", "443:443"]
# Caddy (auto-TLS built-in — simplest option)
# Caddyfile:
# example.com {
# reverse_proxy localhost:3000
# }
# Caddy handles cert issuance and renewal automatically
# Kubernetes cert-manager
# kubectl apply -f https://github.com/cert-manager/cert-manager/releases/latest/download/cert-manager.yaml
# Then create Issuer + Certificate resources
# Cloud-managed (no Certbot needed)
# - AWS ACM (free, auto-renew, integrates with ALB/CloudFront)
# - GCP Certificate Manager (free, auto-renew)
# - Azure App Service Managed Certificates (free)90-day certificate lifetime forces automation — manual renewal would be unsustainable.
Always use --staging for testing to avoid rate limits (5 failures/hour, 50 certs/domain/week).
Monitor certificate expiry: set up alerts at 30 days (UptimeRobot, Datadog, StatusCake — many check TLS expiry for free).
Backup /etc/letsencrypt/ — contains all certificates and keys. Losing private keys requires re-issuance.
Prefer ECDSA keys: certbot --key-type ecdsa uses smaller, faster ECDSA instead of RSA 2048.
Keep your own version of these notes — editable, searchable, and organised by your stack.
Start free