In today’s digital landscape, securing your web applications with HTTPS is not just a recommendation but a necessity. In this blog post, I’ll share how I setup automated SSL certificate issuance for my domains, including wildcard certificates, using Traefik, Let’s Encrypt, and Cloudflare.
The Setup
My goal was to automate the management of SSL certificates for various subdomains and wildcard domains like:
co.example.com
io.example.com
*.co.example.com
*.io.example.com
By combining Traefik (a modern HTTP reverse proxy), Let’s Encrypt (for free SSL certificates), and Cloudflare (for DNS management), I created a fully automated SSL certificate renewal system for my homelab setup.
Prerequisites
- A domain registered with any registrar (my domains are with Namecheap and OVHCloud), but DNS aka nameservers managed through Cloudflare
- Docker and Docker Compose installed on your server (Portainer if you are interested)
- Basic understanding of Docker, DNS, and networking concepts
Step 1: Configure Traefik in Docker Compose or in Portainer
First, I setup Traefik using Portainer. Here’s a simplified version of my configuration: (I user Portainer, so my environment variables are handled in Portainer itself. You may have to handle them with .env file if you are using docker-compose.yml)
---
services:
traefik:
image: traefik:latest
container_name: traefik
restart: unless-stopped
security_opt:
- no-new-privileges:true
networks:
- proxy
ports:
- 80:80
- 443:443
environment:
CF_DNS_API_TOKEN: ${CF_DNS_API_TOKEN}
TRAEFIK_DASHBOARD_CREDENTIALS: ${TRAEFIK_DASHBOARD_CREDENTIALS}
volumes:
- /etc/localtime:/etc/localtime:ro
- /var/run/docker.sock:/var/run/docker.sock:ro
- /home/docker/traefik/data/traefik.yml:/traefik.yml:ro
- /home/docker/traefik/data/acme.json:/acme.json
- /home/docker/traefik/data/config.yml:/config.yml:ro
labels:
- "traefik.enable=true"
- "traefik.http.routers.traefik-http.entrypoints=http"
- "traefik.http.routers.traefik-http.rule=Host(`traefik.io.example.com`)"
- "traefik.http.middlewares.traefik-auth.basicauth.users=${TRAEFIK_DASHBOARD_CREDENTIALS}"
- "traefik.http.middlewares.traefik-https-redirect.redirectscheme.scheme=https"
- "traefik.http.middlewares.sslheader.headers.customrequestheaders.X-Forwarded-Proto=https"
- "traefik.http.routers.traefik-http.middlewares=traefik-https-redirect"
- "traefik.http.routers.traefik-https.entrypoints=https"
- "traefik.http.routers.traefik-https.rule=Host(`traefik.io.example.com`)"
- "traefik.http.routers.traefik-https.middlewares=traefik-auth"
- "traefik.http.routers.traefik-https.tls=true"
- "traefik.http.routers.traefik-https.tls.certresolver=cloudflare"
- "traefik.http.routers.traefik-https.tls.domains[0].main=io.example.com"
- "traefik.http.routers.traefik-https.tls.domains[0].sans=*.io.example.com"
- "traefik.http.routers.traefik-https.tls.domains[1].main=co.example.com"
- "traefik.http.routers.traefik-https.tls.domains[1].sans=*.co.example.com"
- "traefik.http.routers.traefik-https.service=api@internal"
networks:
proxy:
external: true
Step 2: Create the Traefik configuration file
Next, I created a traefik.yml
file to configure the core Traefik settings:
api:
dashboard: true
debug: true
entryPoints:
http:
address: ":80"
http:
redirections:
entryPoint:
to: https
scheme: https
https:
address: ":443"
serversTransport:
insecureSkipVerify: true
providers:
docker:
endpoint: "unix:///var/run/docker.sock"
exposedByDefault: false
file:
filename: /config.yml
certificatesResolvers:
cloudflare:
acme:
email: [email protected]
storage: acme.json
caServer: https://acme-v02.api.letsencrypt.org/directory # prod (default)
# caServer: https://acme-staging-v02.api.letsencrypt.org/directory # staging
dnsChallenge:
provider: cloudflare
#disablePropagationCheck: true # uncomment this if you have issues pulling certificates through cloudflare, By setting this flag to true disables the need to wait for the propagation of the TXT record to all authoritative name servers.
#delayBeforeCheck: 60s # uncomment along with disablePropagationCheck if needed to ensure the TXT record is ready before verification is attempted
resolvers:
- "1.1.1.1:53"
- "1.0.0.1:53"
Step 3: Create an empty ACME JSON file
Traefik needs a file to store the certificates:
touch acme.json
chmod 600 acme.json
Step 4: Setup DNS records in Cloudflare
In the Cloudflare dashboard for my domain, I haven’t added any DNS records. Why? Because, I do not run anything in co.example.com
or io.example.com
. Because my sole purpose is to issue wildcard SSL certificates for the services under *.co.example.com
and *.io.example.com
. If you are running anything on your domains, please feel free to add your A record
or CNAME
For the wildcard subdomains, I didn’t need to create separate DNS records since Cloudflare supports wildcard certificates through the DNS challenge.
Step 5: Generate Cloudflare API tokens
To allow Traefik to automatically verify domain ownership, I created a Cloudflare API token with the following permissions: You can access it from your Cloudflare Dashboard
- Zone > Zone > Read
- Zone > DNS > Edit
Step 6: Launch Traefik
With everything configured, I launched Traefik:
docker-compose up -d
How it works?
Here’s the magic behind this setup:
- When a service that needs HTTPS is deployed, Traefik automatically detects it through Docker labels
- If a certificate doesn’t exist, Traefik uses Let’s Encrypt to issue one
- For wildcard certificates, Traefik uses the DNS challenge method with Cloudflare
- Traefik handles certificate renewal automatically
The Result: Fully automated certificate management
After setting this up, I received confirmation emails from Cloudflare about new certificate issuance:
Hello,
Cloudflare has observed issuance of the following certificate for example.com or one of its subdomains:
Log date: 2025-02-10 17:13:32 UTC
Issuer: CN=R11,O=Let's Encrypt,C=US
Validity: 2025-02-10 16:15:02 UTC - 2025-05-06 16:15:01 UTC
DNS Names: *.io.example.com, io.example.com
Similarly, I received another email for the co.example.com
and *.co.example.com
wildcard certificate.
Adding new wildcard domains
When I needed to add another subdomain (something.example.com
) and wildcard domain (*.something.example.com
), I simply added two new label lines to my Traefik service in docker-compose.yml:
- "traefik.http.routers.traefik-https.tls.domains[2].main=something.example.com"
- "traefik.http.routers.traefik-https.tls.domains[2].sans=*.something.example.com"
After restarting Traefik, it automatically obtained the new wildcard certificate.
The Benefits
This setup provides several key advantages:
- Automation: No manual certificate renewal or installation
- Scalability: Easy to add new domains and subdomains
- Security: Always up-to-date SSL certificates
- Cost Efficiency: Free certificates from Let’s Encrypt
- Flexibility: Works with wildcard domains and multiple subdomains
Conclusion
By combining Traefik, Let’s Encrypt, and Cloudflare, I’ve created a robust and automated system for managing SSL certificates across all my domains and subdomains. This approach not only saves time but also ensures that my web applications are always securely served over HTTPS.
Whether you’re running a small personal project or managing multiple services, this setup provides a scalable and maintainable solution for SSL certificate management.
Note: Remember to replace placeholder values like example.com
, [email protected]
and API tokens with your actual information when implementing this solution.