How to: Setup Let's Encrypt Wildcard Certificates with Cloudflare DNS Challenge on Ubuntu 24.04

Obtaining Wildcard SSL/TLS Certificates Using Cloudflare DNS

example.com is a placeholder for your actual domain name.

Replace example.com with your actual domain name.

Required Tools

  • Ubuntu 24.04 LTS Server
  • Cloudflare DNS with API token or Global API Key
  • Python 3.12.3+ python3 --version
  • certbot 5.2.2+ installed via pip certbot --version
  • certbot-dns-cloudflare certbot plugins

Installing System Dependencies

sudo apt update sudo apt install python3 python3-dev python3-venv libaugeas-dev gcc -y

Installing certbot via pip

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

Creating Cloudflare API Token

Log into Cloudflare at https://dash.cloudflare.com

Navigate to My ProfileAPI TokensCreate Token

Token name: certbot-dns

Permissions:

  • Zone - DNS - Edit
  • Zone - Zone - Read

Zone Resources:

  • Include - Specific zone - example.com

Copy the generated token.

Alternatively, use the Global API Key at My ProfileAPI TokensGlobal API KeyView

Storing Cloudflare Credentials

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

Obtaining Wildcard Certificates

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.

Configuring Automatic Renewal

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

Post-Renewal Hooks

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

Using Certificates with nginx

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

Using Certificates with Postfix

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

Using Certificates with Dovecot

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

Verifying Certificates

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

Updating certbot

sudo /opt/certbot/bin/pip install --upgrade certbot certbot-dns-cloudflare

Troubleshooting

DNS Propagation Issues

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

Certificate Directories with -0001, -0002 Suffixes

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.

Viewing Logs

sudo tail -f /var/log/letsencrypt/letsencrypt.log