Let’s Encrypt (Certbot)

Installation

Ubuntu/Debian

# Update system
sudo apt update
 
# Install Certbot
sudo apt install certbot python3-certbot-nginx python3-certbot-apache -y
 
# Verify installation
certbot --version

CentOS/RHEL

# Install Certbot
sudo yum install certbot python3-certbot-nginx -y
 
# Verify installation
certbot --version

Create SSL Certificate

Standalone (Without Running Web Server)

# Stop web server (if running)
sudo systemctl stop nginx
# Or for Apache
sudo systemctl stop apache2
 
# Create certificate (interactive)
sudo certbot certonly --standalone
 
# Or non-interactive
sudo certbot certonly --standalone \
  -d domain.com \
  -d www.domain.com \
  -m [email protected] \
  --agree-tos \
  --non-interactive
 
# Restart web server
sudo systemctl start nginx
# Create certificate with Nginx plugin
sudo certbot certonly --nginx \
  -d domain.com \
  -d www.domain.com \
  -m [email protected] \
  --agree-tos
 
# Or interactive
sudo certbot certonly --nginx

Apache Plugin

# Create certificate with Apache plugin
sudo certbot certonly --apache \
  -d domain.com \
  -d www.domain.com \
  -m [email protected] \
  --agree-tos

Webroot (For Already Running Server)

# Create certificate
sudo certbot certonly --webroot \
  -w /var/www/html \
  -d domain.com \
  -d www.domain.com
 
# For multiple domains with different roots
sudo certbot certonly --webroot \
  -w /var/www/domain1.com -d domain1.com \
  -w /var/www/domain2.com -d domain2.com

Certificate Location

# Certificate location
/etc/letsencrypt/live/domain.com/
 
# Full chain
/etc/letsencrypt/live/domain.com/fullchain.pem
 
# Private key
/etc/letsencrypt/live/domain.com/privkey.pem
 
# Certificate only
/etc/letsencrypt/live/domain.com/cert.pem
 
# Chain only
/etc/letsencrypt/live/domain.com/chain.pem
 
# All files are symlinks to actual files in /etc/letsencrypt/archive

Nginx Configuration

server {
    listen 443 ssl http2;
    server_name domain.com www.domain.com;
 
    ssl_certificate /etc/letsencrypt/live/domain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/domain.com/privkey.pem;
 
    # SSL configuration
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers on;
    ssl_ciphers HIGH:!aNULL:!MD5;
 
    # Rest of configuration
    root /var/www/domain.com/public;
    index index.php index.html;
 
    location / {
        # Your configuration
    }
}
 
# Redirect HTTP to HTTPS
server {
    listen 80;
    server_name domain.com www.domain.com;
    return 301 https://$server_name$request_uri;
}

Apache Configuration

<VirtualHost *:443>
    ServerName domain.com
    ServerAlias www.domain.com
 
    SSLEngine on
    SSLCertificateFile /etc/letsencrypt/live/domain.com/cert.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/domain.com/privkey.pem
    SSLCertificateChainFile /etc/letsencrypt/live/domain.com/chain.pem
 
    # Rest of configuration
    DocumentRoot /var/www/domain.com/public
</VirtualHost>
 
# Redirect HTTP to HTTPS
<VirtualHost *:80>
    ServerName domain.com
    ServerAlias www.domain.com
    Redirect permanent / https://domain.com/
</VirtualHost>

Automatic Renewal

Systemd Timer (Automatic)

# Check renewal status
sudo certbot renew --dry-run
 
# Start renewal service
sudo systemctl start certbot.timer
sudo systemctl enable certbot.timer
 
# Check timer status
sudo systemctl status certbot.timer
sudo systemctl list-timers certbot.timer

Manual Renewal

# Renew all certificates
sudo certbot renew
 
# Renew specific certificate
sudo certbot renew --cert-name domain.com
 
# Force renewal
sudo certbot renew --force-renewal

Cron Job (Manual Renewal)

# Edit crontab
EDITOR=nano crontab -e
 
# Add renewal cron (runs daily at 2:30 AM)
30 2 * * * sudo certbot renew --post-hook "systemctl reload nginx" >> /var/log/le-renew.log 2>&1
 
# For Apache
30 2 * * * sudo certbot renew --post-hook "systemctl reload apache2" >> /var/log/le-renew.log 2>&1
 
# Or with pre-hook and post-hook
30 2 * * * sudo certbot renew --pre-hook "systemctl stop nginx" --post-hook "systemctl start nginx" >> /var/log/le-renew.log 2>&1

Renew Hooks

# Pre-renewal hook (stop service)
certbot renew --pre-hook "systemctl stop nginx"
 
# Post-renewal hook (restart service)
certbot renew --post-hook "systemctl reload nginx"
 
# Both hooks
certbot renew --pre-hook "systemctl stop nginx" --post-hook "systemctl start nginx"
 
# Renew all with hooks
sudo certbot renew --post-hook "systemctl reload nginx"

Certificate Management

List Certificates

# List all certificates
sudo certbot certificates
 
# Show certificate details
sudo certbot show domain.com

Renew Specific Certificate

# Renew one certificate
sudo certbot renew --cert-name domain.com

Delete Certificate

# Delete certificate
sudo certbot delete --cert-name domain.com

Expand Certificate (Add Domains)

# Add domains to existing certificate
sudo certbot certonly --nginx --cert-name domain.com -d domain.com -d www.domain.com -d new-domain.com

Troubleshooting

# Test renewal (dry-run)
sudo certbot renew --dry-run -v
 
# Check certificate info
sudo openssl x509 -text -noout -in /etc/letsencrypt/live/domain.com/cert.pem
 
# Check expiration
sudo openssl x509 -enddate -noout -in /etc/letsencrypt/live/domain.com/cert.pem
 
# View renewal log
tail -f /var/log/letsencrypt/letsencrypt.log
 
# View renewal status
systemctl status certbot.timer
 
# Check for renewal errors
journalctl -u certbot.service

Rate Limits

# Let's Encrypt rate limits:
# - 50 certificates per domain per week
# - 5 certificates per domain per week (duplicates)
# - 5 failures per account per hour
 
# Use staging environment for testing
sudo certbot certonly --staging --nginx -d domain.com
 
# Check rate limit status
# https://crt.sh/?q=domain.com

Renewal Notifications

# Certbot emails notifications when certificate expires in 20 days
# Set email in configuration file:
sudo nano /etc/letsencrypt/renewal/domain.com.conf
 
# Or update existing
sudo certbot update_account --email [email protected]