Lets Encrypt Certs renewal with DirectAdmin and Cloudflare.

Fixing Let’s Encrypt on DirectAdmin Behind Cloudflare Full/Strict Mode

, ,

Bonus: Cloudflare Tunnel support also explained

Running a modern hosting stack with DirectAdminLet’s EncryptCloudflare, and optionally Cloudflare Tunnel, usually works without issue. But once you enable Cloudflare Full or Full (Strict) mode, Let’s Encrypt renewals inside DirectAdmin often break.

This guide explains the cause, shows how to fix it cleanly with certbot + DNS-01, and includes automation scripts, Cloudflare Tunnel setup, and a domain-map generator for full automation.


The Core Problem: DirectAdmin ACME + Cloudflare Strict Mode Don’t Mix

DirectAdmin uses HTTP-01 validation to issue and renew Let’s Encrypt certificates. Cloudflare, when proxying traffic, interferes in several ways that make DirectAdmin’s built-in ACME system unreliable (or completely non-functional).

  • Cloudflare proxy blocks or alters ACME challenge requests
  • Cloudflare Strict mode requires a valid public CA certificate on the origin
  • When renewal fails, Cloudflare blocks traffic entirely
  • Cloudflare Origin Certificates aren’t standard certificates
  • Cloudflare Tunnel hides ports 80/443, making HTTP-01 impossible

Bottom line: DirectAdmin’s built-in Let’s Encrypt cannot be used reliably behind Cloudflare Strict or Cloudflare Tunnel.


Solution Overview

  • Use certbot to issue Let’s Encrypt certificates outside DirectAdmin
  • Validate domain ownership using Cloudflare DNS-01 (works even with proxy/tunnel)
  • Automatically copy renewed certificates into DirectAdmin’s SSL storage paths
  • Reload Apache automatically after renewal
  • Optional: Put DirectAdmin behind Cloudflare Tunnel for enhanced security

Step 1 — Install certbot + Cloudflare DNS Plugin (Rocky Linux 9)

sudo dnf install certbot python3-certbot-dns-cloudflare

If the DNS plugin isn’t available:

sudo pip3 install certbot-dns-cloudflare

Step 2 — Create Your Cloudflare API Credentials

Create the credentials file:

sudo mkdir -p /root/.secrets
sudo vi /root/.secrets/cloudflare.ini

Paste this inside:

dns_cloudflare_api_token = YOUR_TOKEN_HERE
sudo chmod 600 /root/.secrets/cloudflare.ini

Step 3 — Issue a DNS-01 Certificate with certbot

sudo certbot certonly \
  --dns-cloudflare \
  --dns-cloudflare-credentials /root/.secrets/cloudflare.ini \
  -d yourdomain.com \
  -d www.yourdomain.com

Certbot now stores certificates in:

/etc/letsencrypt/live/yourdomain.com/

Step 4 — Understanding DirectAdmin SSL Paths

DirectAdmin expects the following certificate files:

<domain>.key
<domain>.cert
<domain>.cacert
<domain>.cert.combined

Stored under:

/usr/local/directadmin/data/users/<DAUSER>/domains/

We don’t modify Apache or DirectAdmin vhosts — we simply replace the certificate files where DirectAdmin expects these.


Step 5 — Multi-Domain DirectAdmin Certificate Deployment Script

Save the script as:

/usr/local/sbin/deploy-da-cert.sh
#!/usr/bin/env bash
set -euo pipefail

# Map: DOMAIN -> DA_USER
declare -A DOMAINS=(
  # ["example.com"]="user1"
  # ["domain.net"]="user2"
)

APACHE_SERVICE="httpd"
reload_needed=false

for DOMAIN in "${!DOMAINS[@]}"; do
  DA_USER="${DOMAINS[$DOMAIN]}"
  LE_PATH="/etc/letsencrypt/live/${DOMAIN}"
  DA_PATH="/usr/local/directadmin/data/users/${DA_USER}/domains"

  echo "Processing ${DOMAIN} (DA user: ${DA_USER})"

  if [[ ! -d "${LE_PATH}" ]]; then
    echo "  Missing LE path: ${LE_PATH} — skipping"
    continue
  fi

  if [[ ! -d "${DA_PATH}" ]]; then
    echo "  Missing DA path: ${DA_PATH} — skipping"
    continue
  fi

  if [[ ! -f "${LE_PATH}/privkey.pem" || ! -f "${LE_PATH}/fullchain.pem" ]]; then
    echo "  Missing fullchain or privkey — skipping"
    continue
  fi

  DA_KEY="${DA_PATH}/${DOMAIN}.key"
  DA_CERT="${DA_PATH}/${DOMAIN}.cert"
  DA_CACERT="${DA_PATH}/${DOMAIN}.cacert"
  DA_COMBINED="${DA_PATH}/${DOMAIN}.cert.combined"

  echo "  Installing key and certificates…"

  install -o diradmin -g diradmin -m 600 "${LE_PATH}/privkey.pem"   "${DA_KEY}"
  install -o diradmin -g access   -m 640 "${LE_PATH}/fullchain.pem" "${DA_CERT}"

  if [[ -f "${LE_PATH}/chain.pem" ]]; then
    install -o diradmin -g access -m 640 "${LE_PATH}/chain.pem" "${DA_CACERT}"

    if [[ -s "${LE_PATH}/fullchain.pem" && -s "${LE_PATH}/chain.pem" ]]; then
      cat "${DA_CERT}" "${DA_CACERT}" | \
        install -o diradmin -g access -m 640 /dev/stdin "${DA_COMBINED}"
      echo "  Created combined certificate bundle."
    fi
  else
    : > "${DA_CACERT}"
    chown diradmin:access "${DA_CACERT}"
    chmod 0640 "${DA_CACERT}"
  fi

  reload_needed=true
  echo "  Updated SSL for ${DOMAIN}"
done

if [[ "${reload_needed}" == true ]]; then
  echo "Reloading Apache..."
  systemctl reload "${APACHE_SERVICE}"
fi
sudo chmod +x /usr/local/sbin/deploy-da-cert.sh

Step 6 — Automate Renewal with cron

0 3 * * * certbot renew --quiet --deploy-hook "/usr/local/sbin/deploy-da-cert.sh"

Cloudflare Tunnel Setup for DirectAdmin


Authenticate and Create Tunnel

Cloudflare Tunnel is the best modern way to securely expose DirectAdmin (port 2222) on Rocky Linux 9 withoutleaking your server’s IP and without needing Cloudflare’s limited proxy ports.

Below is a clean, step-by-step setup specifically for:

  • Rocky Linux 9
  • DirectAdmin running on https://yourdomain.com:2222
  • Desired Cloudflare hostname like panel.example.com
  • Fully hidden origin IP
  • Cloudflare protection, WAF, Bot Filtering, Access rules working

✅ STEP 1 — Install cloudflared on Rocky Linux 9

sudo dnf install cloudflared

(If repo is missing, use Cloudflare’s official repo:)

sudo tee /etc/yum.repos.d/cloudflared.repo <<EOF
name=cloudflared baseurl=https://pkg.cloudflare.com/cloudflared/rpm enabled=1 gpgcheck=1 gpgkey=https://pkg.cloudflare.com/cloudflare-main.gpg EOF

sudo dnf install cloudflared

Check version:

cloudflared --version

✅ STEP 2 — Authenticate with Cloudflare

Run:

cloudflared tunnel login

This opens a browser window → log into Cloudflare → choose the domain.

This authorizes your server to create/manage tunnels for that domain.


✅ STEP 3 — Create a Tunnel

Name your tunnel:

cloudflared tunnel create directadmin

Output example:

Created tunnel directadmin
Tunnel ID: 123abc456def789...

Cloudflared creates a credentials file here:

/root/.cloudflared/123abc456def789.json

Keep this safe.


✅ STEP 4 — Create the Tunnel Configuration File

Create:

sudo nano /etc/cloudflared/config.yml

Put this:

tunnel: 123abc456def789   # <-- your actual tunnel ID
credentials-file: /root/.cloudflared/123abc456def789.json

ingress:
  - hostname: panel.example.com
    service: https://localhost:2222
    originRequest:
      noTLSVerify: true        # DirectAdmin uses a self- or custom-signed cert
  - service: http_status:404

Replace:

  • panel.example.com with your desired DA hostname
  • 123abc456def789 with your actual tunnel ID

✅ STEP 5 — Connect the hostname in Cloudflare

Run:

cloudflared tunnel route dns directadmin panel.example.com

This creates a DNS entry like:

panel.example.com → 123abc456def789.cfargotunnel.com

This hostname is:

  • proxied
  • hidden
  • protected by Cloudflare WAF
  • routed through your tunnel

✅ STEP 6 — Start the Tunnel

Use the systemd integration:

sudo cloudflared service install
sudo systemctl enable --now cloudflared
sudo systemctl status cloudflared

You should see:

cloudflared tunnel running
serving ingress at panel.example.com → https://localhost:2222

🎉 Tunnel is live! Test it:

Open:

https://panel.example.com

You should see your DirectAdmin login page, without adding :2222 — Cloudflare will serve it over port 443.

✔ No need to expose 2222 publically
✔ Cloudflare WAF & security apply
✔ You can add Cloudflare Access login in front of DA
✔ Your server IP is hidden
✔ DA is still using its own SSL inside the tunnel (even if it’s self-signed)


🔐 Optional (Recommended): Secure DirectAdmin Behind Cloudflare Access

Add an SSO login page before anyone can reach DirectAdmin.

In Cloudflare:

  1. Zero Trust → Access → Applications
  2. Create an app:
    • Name: DirectAdmin
    • Domain: panel.example.com
  3. Choose a policy:
    • Allow only your email
    • or allow your IP ranges
    • or require 2FA

This protects DirectAdmin with Cloudflare Identity.


Bonus: Automatic DirectAdmin Domain Map Generator Script

This script auto-detects all domains + their DA users, then prints a ready-to-paste “declare -A DOMAINS” map.

#!/usr/bin/env bash
echo "declare -A DOMAINS=("
for userdir in /usr/local/directadmin/data/users/*; do
  user=$(basename "$userdir")
  domfile="$userdir/domains.list"
  if [[ -f "$domfile" ]]; then
    while read -r domain; do
      [[ -z "$domain" ]] && continue
      echo "  [\"$domain\"]=\"$user\""
    done < "$domfile"
  fi
done
echo ")"

Conclusion

With certbot + DNS-01 validation and the automated deploy script, you now have a fully reliableCloudflare-compatibleDirectAdmin-friendly Let’s Encrypt renewal system.

It works flawlessly in:

  • Cloudflare Full mode
  • Cloudflare Full (Strict) mode
  • Cloudflare Tunnel
  • Proxy On/Off scenarios

Your SSL is now completely automated and rock-solid.

Smart reads for curious minds

We don’t spam! Read more in our privacy policy

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *