example.com is a placeholder for your actual domain name.
Replace example.com with your actual domain name.
python3 --versioncertbot --versioncertbot pluginssudo apt update sudo apt install python3 python3-dev python3-venv libaugeas-dev gcc -y
Create a virtual environment for certbot in /opt/certbot/
sudo python3 -m venv /opt/certbot sudo /opt/certbot/bin/pip install --upgrade pip sudo /opt/certbot/bin/pip install certbot certbot-dns-cloudflare
Symlink certbot to make it accessible system-wide
sudo ln -s /opt/certbot/bin/certbot /usr/local/bin/certbot
Verify installation
certbot --version certbot plugins
Log into Cloudflare at https://dash.cloudflare.com
Navigate to My Profile → API Tokens → Create Token
Token name: certbot-dns
Permissions:
Zone Resources:
example.comCopy the generated token.
Alternatively, use the Global API Key at My Profile → API Tokens → Global API Key → View
Create the credentials directory
sudo mkdir -p /root/.secrets sudo chmod 700 /root/.secrets
Create /root/.secrets/cloudflare.ini with API token (recommended):
dns_cloudflare_api_token = "your_api_token_here"
Or with Global API Key (alternative):
dns_cloudflare_email = "your-cloudflare-email@example.com" dns_cloudflare_api_key = "your_global_api_key_here"
Set read-only permissions
sudo chmod 400 /root/.secrets/cloudflare.ini
Request a wildcard certificate covering the apex domain and all subdomains
sudo certbot certonly \ --dns-cloudflare \ --dns-cloudflare-credentials /root/.secrets/cloudflare.ini \ -d example.com,*.example.com
With ECDSA keys (recommended):
sudo certbot certonly \ --dns-cloudflare \ --dns-cloudflare-credentials /root/.secrets/cloudflare.ini \ --key-type ecdsa \ -d example.com,*.example.com
Certificates are stored in /etc/letsencrypt/live/example.com/
/etc/letsencrypt/live/example.com/ ├── cert.pem ├── chain.pem ├── fullchain.pem └── privkey.pem
Do not run certbot certonly again for this domain. Use certbot renew for renewals to prevent creating -0001, -0002 directories that break configurations.
Add a cron job for automatic renewal
echo "0 0,12 * * * root /opt/certbot/bin/python -c 'import random; import time; time.sleep(random.random() * 3600)' && sudo certbot renew -q" | sudo tee -a /etc/crontab > /dev/null
Verify the cron job
sudo grep certbot /etc/crontab
Test renewal
sudo certbot renew --dry-run
Create /etc/letsencrypt/renewal-hooks/deploy/reload-services.sh
#!/bin/bash systemctl reload nginx systemctl reload postfix systemctl reload dovecot
Make executable
sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/reload-services.sh
Edit nginx server block configuration
server { listen 443 ssl http2; server_name mail.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 on; ssl_ciphers EECDH+AESGCM:EDH+AESGCM; }
Test and reload nginx
sudo nginx -t sudo systemctl reload nginx
Edit /etc/postfix/main.cf
smtpd_tls_cert_file=/etc/letsencrypt/live/example.com/fullchain.pem smtpd_tls_key_file=/etc/letsencrypt/live/example.com/privkey.pem smtpd_use_tls=yes smtpd_tls_auth_only=yes
Reload Postfix
sudo systemctl reload postfix
Edit /etc/dovecot/conf.d/10-ssl.conf
ssl = required ssl_cert = </etc/letsencrypt/live/example.com/fullchain.pem ssl_key = </etc/letsencrypt/live/example.com/privkey.pem
Reload Dovecot
sudo systemctl reload dovecot
Check certificate expiration
sudo openssl x509 -in /etc/letsencrypt/live/example.com/cert.pem -noout -dates
Verify wildcard coverage
sudo openssl x509 -in /etc/letsencrypt/live/example.com/cert.pem -noout -text | grep -A1 "Subject Alternative Name"
Output should show:
X509v3 Subject Alternative Name: DNS:*.example.com, DNS:example.com
sudo /opt/certbot/bin/pip install --upgrade certbot certbot-dns-cloudflare
Increase propagation wait time
sudo certbot certonly \ --dns-cloudflare \ --dns-cloudflare-credentials /root/.secrets/cloudflare.ini \ --dns-cloudflare-propagation-seconds 60 \ -d example.com,*.example.com
Remove orphaned renewal configs
sudo ls -la /etc/letsencrypt/renewal/ sudo rm /etc/letsencrypt/renewal/example.com-0001.conf sudo rm -rf /etc/letsencrypt/live/example.com-0001/ sudo rm -rf /etc/letsencrypt/archive/example.com-0001/
Only use certbot renew for renewals, never certbot certonly again.
sudo tail -f /var/log/letsencrypt/letsencrypt.log