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

ConceptDescription
ACME protocolAutomated Certificate Management Environment, used to request and renew
Certificate authorityLet's Encrypt
ValidationProves you control the domain
Validity90 days, renewed automatically
Rate limits50 certificates per registered domain per week

Validation Methods

MethodHow it worksUse case
HTTP-01Places a file at /.well-known/acme-challenge/Standard web servers
DNS-01Creates a TXT recordWildcard certificates, no public HTTP
TLS-ALPN-01Responds on port 443 with a special certificateOnly 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:

  1. Verify domain ownership via HTTP-01.
  2. Obtain the certificate.
  3. Modify your Nginx configuration to listen on 443 and redirect HTTP to HTTPS.
  4. 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

FilePurpose
fullchain.pemCertificate plus intermediate chain; use this for ssl_certificate
privkey.pemPrivate key
cert.pemLeaf certificate only
chain.pemIntermediate 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

ErrorCauseFix
Timeout during connectPort 80 blockedOpen the firewall or use DNS-01
Invalid response from ...Challenge file not servedCheck Nginx serves /.well-known/acme-challenge/
too many certificates already issuedWeekly rate limit reachedWait or use staging
No such authorizationStale ACME account stateRe-register with certbot register
Certificate expiredRenewal failed silentlyTest renewal monthly and monitor expiry

Best Practices

  • Always test with --dry-run after 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/letsencrypt including account credentials.