Here’s a guide on how to set up Traefik with Cloudflare to handle automated TLS certificates using DNS challenges, same as my previous post using route53.
Requirements
Before proceeding, ensure you have:
- Cloudflare account with a managed domain
- Docker and Docker Compose installed
- Basic understanding of Docker networking
Cloudflare API Token
Create an API token with proper permissions for DNS challenge verification:
- Navigate to https://dash.cloudflare.com/account/api-tokens
- Click “Create Token”
- Use the “Edit zone DNS” template
- Configure the token:
- Zone Resources: Include → Specific zone → [your domain]
- Account Resources: Include → All accounts (or your specific account)
- Zone Permissions: Zone:Read, Zone:Edit
- Client IP Address Filtering: (optional) restrict to your server IP
- Save the token - you’ll need this for the CF_DNS_API_TOKEN environment variable
Traefik Docker-Compose Configuration
Create a Docker-Compose file to run Traefik. Include the Cloudflare API Token, email associated with your Cloudflare account, and your Cloudflare managed domain.
networks:
traefik:
services:
traefik:
image: traefik:v3
container_name: traefik
security_opt:
- no-new-privileges:true
restart: always
networks:
- traefik
ports:
- 80:80
- 443:443
- 8443:8443 # Dashboard access
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- /some_volume/traefik/acme:/etc/traefik/acme/
environment:
- TZ=Europe/Stockholm
- CF_DNS_API_TOKEN=YOUR_CLOUDFLARE_API_TOKEN_HERE
command:
# Enable the Traefik dashboard and API
- --api.dashboard=true
- --global.sendAnonymousUsage=false
- --entrypoints.https.address=:443
- --entrypoints.traefik.address=:8443
- --entrypoints.http.address=:80
- --providers.docker=true
- --providers.docker.exposedByDefault=false
- --log.level=INFO
- --serversTransport.insecureSkipVerify=true
- --serversTransport.maxIdleConnsPerHost=5
- --serversTransport.forwardingTimeouts.idleConnTimeout=60s
# Cloudflare ACME configuration
- --certificatesResolvers.cloudflare.acme.email=your.email@example.com
- --certificatesResolvers.cloudflare.acme.caServer=https://acme-v02.api.letsencrypt.org/directory
# For testing, use staging first: --certificatesResolvers.cloudflare.acme.caServer=https://acme-staging-v02.api.letsencrypt.org/directory
- --certificatesResolvers.cloudflare.acme.storage=/etc/traefik/acme/cloudflare.json
- --certificatesResolvers.cloudflare.acme.dnsChallenge.provider=cloudflare
- --certificatesResolvers.cloudflare.acme.dnsChallenge.disablePropagationCheck=false
- --certificatesResolvers.cloudflare.acme.dnsChallenge.resolvers=beau.ns.cloudflare.com:53,tara.ns.cloudflare.com:53
- --certificatesResolvers.cloudflare.acme.dnsChallenge.delayBeforeCheck=10
labels:
- "traefik.enable=true"
- "traefik.http.routers.traefik.rule=Host(`traefik.yourdomain.com`)"
- "traefik.http.routers.traefik.service=api@internal"
- "traefik.http.routers.traefik.entrypoints=traefik"
- "traefik.http.routers.traefik.tls=true"
- "traefik.http.routers.traefik.tls.certresolver=cloudflare"
- "traefik.http.routers.traefik.tls.domains[0].main=yourdomain.com"
- "traefik.http.routers.traefik.tls.domains[0].sans=*.yourdomain.com"
Service Configuration
Add certificates to any service with these labels:
some-service:
image: some-service:1.0.0
networks:
- traefik
labels:
- "traefik.enable=true"
- "traefik.http.routers.some-service.rule=Host(`service.yourdomain.com`)"
- "traefik.http.routers.some-service.entrypoints=https"
- "traefik.http.routers.some-service.tls=true"
- "traefik.http.routers.some-service.tls.certresolver=cloudflare"
- "traefik.http.services.some-service.loadbalancer.server.port=3030"
Testing with Staging Environment
Important: Before deploying to production, test your configuration with Let’s Encrypt staging to avoid hitting rate limits:
Comment out the production server line and uncomment the staging line:
# - --certificatesResolvers.cloudflare.acme.caServer=https://acme-v02.api.letsencrypt.org/directory - --certificatesResolvers.cloudflare.acme.caServer=https://acme-staging-v02.api.letsencrypt.org/directoryTest your setup completely with staging certificates
Once verified, switch back to production server and restart Traefik
Delete the staging
acme.jsonfile before switching to production
This prevents hitting Let’s Encrypt’s rate limits (50 certificates per week) during testing.
Done!
Buy Me a Coffee