Background#
I was previously using Cloudflare Tunnel (free plan) to reverse proxy my Nextcloud container. While convenient, the free tier has a 100MB upload limitation that became problematic for my usage.
This article documents my migration from Cloudflare Tunnel to direct Traefik reverse proxy with Let’s Encrypt TLS certificates, including the complete setup, issues encountered, and troubleshooting steps.
Why Migrate?#
Cloudflare Tunnel (Free Plan) Pros#
- ✅ Automatic SSL/TLS termination
- ✅ No port forwarding needed
- ✅ Works with dynamic IPs
- ✅ Built-in DDoS protection
Cloudflare Tunnel (Free Plan) Cons#
- ❌ 100MB upload limit (major issue)
- ❌ Bandwidth limits
- ❌ Tunnel overhead adds latency
- ❌ Dependency on Cloudflare infrastructure
Why Traefik + Let’s Encrypt#
- ✅ No upload limits (configurable)
- ✅ Direct connection (lower latency)
- ✅ Automatic certificate management
- ✅ Full control over routing
- ✅ Can handle multiple services
Current Architecture#
| |
Before: Internet → Cloudflare Tunnel → Traefik → Nextcloud
After: Internet → Traefik → Nextcloud
Prerequisites#
- Docker and docker-compose installed
- Traefik container running
- Domain name (e.g.,
nextcloud2.example.com) - Cloudflare account with domain DNS managed
- Server with public IP (or dynamic DNS)
Step 1: Remove Cloudflare Tunnel#
Stop the cloudflared container#
| |
Update cloudflared config (optional)#
If you want to keep cloudflared for other services, remove the Nextcloud entry from config.yaml:
| |
Step 2: Update DNS in Cloudflare#
For each domain you want to use with Traefik:
- Go to Cloudflare Dashboard → DNS
- Find the DNS record (e.g.,
nextcloud2.example.com) - Change from Orange Cloud (Proxied) to Gray Cloud (DNS Only)
- Point to your server’s public IP address
Important: For Let’s Encrypt DNS challenge to work, traffic must go directly to your server, not through Cloudflare’s proxy.
Step 3: Configure Traefik’s Let’s Encrypt DNS Challenge#
Update traefik.yml#
Ensure you have the ACME configuration with Cloudflare DNS challenge:
| |
Set Cloudflare API Token#
Create or update .env file in the traefik directory:
| |
Note: For DNS challenge, you need a Cloudflare API Token (not Global API Key) with the following permissions:
- Zone DNS: Edit zone DNS
Update docker-compose.yml#
Ensure environment variables are passed to Traefik:
| |
Step 4: Configure Dynamic Router with TLS#
Create a file in traefik/dynamic/ directory (e.g., nextcloud2.yml):
| |
Key Configuration Points#
Two routers needed:
- HTTP router for redirect (port 80)
- HTTPS router for actual traffic (port 443)
certResolver: leis crucial - without this, Traefik uses its internal self-signed certificatepassHostHeader: true- preserves the original Host header
Step 5: Restart Traefik#
| |
Traefik will:
- Watch for new config files
- Request Let’s Encrypt certificate via DNS challenge
- Create
_acme-challenge.nextcloud2.example.comTXT record - Wait for DNS propagation
- Verify ownership
- Download certificate
Step 6: Verify Setup#
Check if certificate was issued#
| |
Test HTTPS connection#
| |
Add More Domains#
To add additional domains, simply create more config files:
| |
Traefik auto-reloads every 10 seconds, so no restart needed.
Common Issues & Solutions#
1. Certificate Shows as Self-Signed#
Problem: Browser shows “Not Secure” or self-signed certificate warning
Solution: Add certResolver: le to your router config. Without this, Traefik uses its internal certificate.
2. DNS Challenge Failed#
Problem: Certificate not being issued, errors in logs
Solution:
- Verify Cloudflare API token has correct permissions
- Check Traefik has access to API token via environment variables
- Ensure DNS is not proxied through Cloudflare (gray cloud)
- Check logs:
docker logs traefik
3. Certificate Exists but Not Valid for Domain#
Problem: Let’s Encrypt cert exists, but not for your domain
Solution: Traefik creates certificates on-demand. Simply access the domain and Traefik will automatically request the certificate.
4. HTTP to HTTPS Redirect Not Working#
Problem: HTTP requests don’t redirect to HTTPS
Solution: Ensure you have both HTTP and HTTPS routers configured with the redirect middleware.
5. Nextcloud Shows “Trusted Domain Error”#
Problem: Nextcloud doesn’t trust the new domain
Solution: Update Nextcloud’s trusted domains:
In nextcloud/docker-compose.yml, update:
| |
Or in Nextcloud config (config/config.php):
| |
Advanced Configuration#
Enable Debug Logging#
| |
Configure Certificate TTL#
| |
Multiple Domains for Single Service#
| |
Monitoring and Maintenance#
Check Certificate Expiry#
| |
Manual Certificate Renewal#
Let’s Encrypt certs auto-renew 30 days before expiry. To force renewal:
| |
Backup acme.json#
| |
Performance Comparison#
| Metric | Cloudflare Tunnel | Traefik + Let’s Encrypt |
|---|---|---|
| Upload Limit | 100MB ❌ | Unlimited ✅ |
| Latency | Higher (tunnel overhead) | Lower (direct) ✅ |
| SSL Certs | Automatic ✅ | Automatic ✅ |
| Cost | Free (limited) | Free (unlimited) ✅ |
| Configuration | Simple | Moderate |
| DDoS Protection | Built-in ✅ | Must add separately |
Conclusion#
Migrating from Cloudflare Tunnel to Traefik with Let’s Encrypt was straightforward and provides:
- No upload limitations
- Better performance
- Full control
- Automatic certificate management
The key takeaways:
- Remove Cloudflare Tunnel for the domain
- Update DNS to DNS only (not proxied)
- Configure Traefik with ACME + Cloudflare DNS challenge
- Set
certResolver: lein router config - Restart Traefik to request certificates
This setup is ideal for self-hosted services and scales well as you add more domains.
References#
Changelog#
- 2026-07-05 - Initial publication, documented the migration process

