Automated SSL with Let us Encrypt and Cloudflare DNS Challenge
Set up fully automated SSL certificates using Let us Encrypt with Cloudflare DNS validation. Covers wildcard certificates, Traefik integration, certbot...
Why DNS Challenge?
The standard HTTP challenge requires port 80 to be publicly accessible. The DNS challenge validates domain ownership by creating a DNS TXT record — no open ports needed. This is perfect for:
Encryption transforms readable plaintext into unreadable ciphertext, reversible only with the correct key.
- Servers behind Cloudflare Tunnel (no public ports)
- Wildcard certificates (*.example.com)
- Internal services not accessible from the internet
Method 1: Traefik + Cloudflare (Recommended)
If you use Traefik as your reverse proxy, SSL is automatic:
# docker-compose.yml
services:
traefik:
image: traefik:v3
command:
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
- "--certificatesresolvers.cloudflare.acme.dnschallenge=true"
- "--certificatesresolvers.cloudflare.acme.dnschallenge.provider=cloudflare"
- "[email protected]"
- "--certificatesresolvers.cloudflare.acme.storage=/letsencrypt/acme.json"
- "--providers.docker=true"
environment:
CF_DNS_API_TOKEN: your-cloudflare-api-token
ports:
- "80:80"
- "443:443"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- letsencrypt:/letsencrypt
Then any service with Docker labels automatically gets SSL:
services:
myapp:
image: myapp:latest
labels:
- "traefik.enable=true"
- "traefik.http.routers.myapp.rule=Host(`app.example.com`)"
- "traefik.http.routers.myapp.tls.certresolver=cloudflare"
Get more insights on Security
Join 2,000+ engineers who get our weekly deep-dives. No spam, unsubscribe anytime.
Wildcard Certificate
labels:
- "traefik.http.routers.wildcard.rule=HostRegexp(`{subdomain:.+}.example.com`)"
- "traefik.http.routers.wildcard.tls.certresolver=cloudflare"
- "traefik.http.routers.wildcard.tls.domains[0].main=example.com"
- "traefik.http.routers.wildcard.tls.domains[0].sans=*.example.com"
Method 2: Certbot + Cloudflare Plugin
For non-Traefik setups, use certbot with the Cloudflare DNS plugin:
# Install certbot and Cloudflare plugin
sudo apt install certbot python3-certbot-dns-cloudflare
# Create Cloudflare credentials file
cat > /etc/letsencrypt/cloudflare.ini << 'EOF'
dns_cloudflare_api_token = your-cloudflare-api-token
EOF
chmod 600 /etc/letsencrypt/cloudflare.ini
# Obtain a certificate
certbot certonly \
--dns-cloudflare \
--dns-cloudflare-credentials /etc/letsencrypt/cloudflare.ini \
-d example.com \
-d "*.example.com" \
--preferred-challenges dns-01
# Auto-renewal is set up automatically via systemd timer
systemctl list-timers | grep certbot
Renewal Hook (Reload Services)
# /etc/letsencrypt/renewal-hooks/deploy/reload-services.sh
#!/bin/bash
systemctl reload nginx
# Or for Docker:
docker kill --signal=HUP traefik
A reverse proxy terminates TLS, routes requests by hostname, and load-balances across backend services.
Method 3: acme.sh
# Install acme.sh
curl https://get.acme.sh | sh
# Set Cloudflare API token
export CF_Token="your-cloudflare-api-token"
# Issue certificate
acme.sh --issue --dns dns_cf -d example.com -d "*.example.com"
# Install certificate to a specific location
acme.sh --install-cert -d example.com \
--key-file /etc/ssl/private/example.com.key \
--fullchain-file /etc/ssl/certs/example.com.crt \
--reloadcmd "systemctl reload nginx"
Cloudflare API Token Setup
Create a scoped API token (not the global API key):
- Go to Cloudflare Dashboard > My Profile > API Tokens
- Click "Create Token"
- Use the "Edit zone DNS" template
- Scope to your specific zone
- Copy the token
Required permissions:
- Zone > DNS > Edit
- Zone > Zone > Read
Certificate Monitoring
Check Certificate Expiry
Free Resource
Infrastructure Security Audit Template
The exact audit template we use with clients: 60+ checks across network, identity, secrets management, and compliance.
#!/bin/bash
# check-certs.sh
DOMAINS="example.com api.example.com app.example.com"
for domain in \$DOMAINS; do
EXPIRY=\$(echo | openssl s_client -servername "\$domain" -connect "\$domain:443" 2>/dev/null | openssl x509 -noout -enddate | cut -d= -f2)
DAYS_LEFT=\$(( (\$(date -d "\$EXPIRY" +%s) - \$(date +%s)) / 86400 ))
if [ "\$DAYS_LEFT" -lt 14 ]; then
echo "WARNING: \$domain expires in \$DAYS_LEFT days (\$EXPIRY)"
else
echo "OK: \$domain - \$DAYS_LEFT days remaining"
fi
done
Monitor with Uptime Kuma
Add HTTPS monitors in Uptime Kuma with "Certificate Expiry" notification enabled. It will alert you when certificates are within 14 days of expiry.
Defense in depth: multiple security layers protect your infrastructure from threats.
Security Headers
Once you have HTTPS, add security headers:
# Traefik middleware
http:
middlewares:
security-headers:
headers:
stsSeconds: 31536000
stsIncludeSubdomains: true
stsPreload: true
forceSTSHeader: true
contentTypeNosniff: true
browserXssFilter: true
frameDeny: true
referrerPolicy: "strict-origin-when-cross-origin"
At TechSaaS, we use Traefik with Cloudflare DNS challenge for all our certificates. Combined with Cloudflare Tunnel, our servers have zero open ports while still serving HTTPS for every service. Certificate provisioning and renewal is fully automatic.
Need help with SSL and security? Contact [email protected].
Related Service
Security & Compliance
Zero-trust architecture, compliance automation, and incident response planning.
Need help with security?
TechSaaS provides expert consulting and managed services for cloud infrastructure, DevOps, and AI/ML operations.
We Will Build You a Demo Site — For Free
Like it? Pay us. Do not like it? Walk away, zero complaints. You will spend way less than hiring developers or any agency.
No spam. No contracts. Just a free demo.