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:

  1. Navigate to https://dash.cloudflare.com/account/api-tokens
  2. Click “Create Token”
  3. Use the “Edit zone DNS” template
  4. 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
  5. 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:

  1. 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/directory
    
  2. Test your setup completely with staging certificates

  3. Once verified, switch back to production server and restart Traefik

  4. Delete the staging acme.json file before switching to production

This prevents hitting Let’s Encrypt’s rate limits (50 certificates per week) during testing.

Done!



Buy Me a Coffee