Traefik wildcard Cloudflare without docker

minimalistic setup

by intent not using docker and/or kube, aka plain brew install traefik or even better download latest binary from Github

before proceeding to launchd and/or other stuff make sure you can run it manually

IMPORTANT: make sure there are no configuration files in working directory, they will override your command line arguments

huge thanks to @bluepuma77 for pointing this out :folded_hands:

here is minimal providers.yml file:

http:
  routers:
    foo:
      entryPoints:
        - http # 👈 you may want to remove this as well in future
        - https
      service: foo
      rule: Host(`foo.example.com`)
      # 👆 note: we are not passing any tls here, instead, entrypoint tls is used
  services:
    foo:
      loadBalancer:
        servers:
          - url: http://localhost:5000/

and here is how to run it:

CLOUDFLARE_DNS_API_TOKEN=xxxxx traefik \
--log.level=INFO \
--providers.file.filename=/opt/homebrew/etc/traefik/provider.yml \
--certificatesresolvers.letsencrypt.acme.dnschallenge.provider=cloudflare \
--certificatesResolvers.letsencrypt.acme.dnschallenge.resolvers=1.1.1.1:53,1.0.0.1:53 \
--certificatesresolvers.letsencrypt.acme.email=hello@gmail.com \
--certificatesresolvers.letsencrypt.acme.storage=/opt/homebrew/etc/traefik/acme.json \
--entrypoints.http.address=:80 \
--entrypoints.https.address=:443 \
--entrypoints.https.http.tls.certResolver=letsencrypt \
--entrypoints.https.http.tls.domains[0].main=example.com \
--entrypoints.https.http.tls.domains[0].sans=*.example.com

after a while, acme.json will be filled with desired wildcard certificate, and you may start adding other routes :tada:

learning point

Traefik does not log if it did load some configurations from files, but it does log final configuration, to see it, make sure to switch log level to DEBUG and you should see something like:

2025-03-13T11:13:09+02:00 DBG github.com/traefik/traefik/v3/cmd/traefik/traefik.go:114 > Static configuration loaded [json] staticConfiguration={...materialized json configuration will be here, have careful look if everything is configured as you expect}

launchd

note for macos users, brew will create its own plist file and override each time, for this setup to work you need modify it and pass environment variable for cloudflare

so instead of relying on brew services, just copy and modify /opt/homebrew/Cellar/traefik/3.3.4/homebrew.mxcl.traefik.plist instead, aka:

cp /opt/homebrew/Cellar/traefik/3.3.4/homebrew.mxcl.traefik.plist ~/Library/LaunchAgents/traefik.plist
# vim traefik.plist # 👈 change label, add environment variables
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/traefik.plist

for environment variables, inside plist element add following:

<key>EnvironmentVariables</key>
<dict>
  <key>CLOUDFLARE_DNS_API_TOKEN</key>
  <string>xxxxx</string>
</dict>

also, depending on your preferences, pass cli params or config file


original question

Hello, trying to figure out how to get Traefik behind Cloudflare up and running and stuck with very first steps of wildcard certs

Here is what I'm trying to do

CLOUDFLARE_DNS_API_TOKEN=xxxxxxxxxxxx traefik \
--log.level=INFO \
--providers.file.filename=/opt/homebrew/etc/traefik/provider.yml \
--certificatesresolvers.letsencrypt.acme.dnschallenge.provider=cloudflare \
--certificatesResolvers.letsencrypt.acme.dnschallenge.resolvers=1.1.1.1:53,1.0.0.1:53 \
--certificatesresolvers.letsencrypt.acme.email=hello@gmail.com \
--certificatesresolvers.letsencrypt.acme.storage=/opt/homebrew/etc/traefik/acme.json \
--entrypoints.http.address=:80 \
--entrypoints.https.address=:443 \
--entrypoints.https.http.tls.certResolver=letsencrypt \
--entrypoints.https.http.tls.domains[0].main=example.com \
--entrypoints.https.http.tls.domains[0].sans=*.example.com

by intent running exactly like this no docker, no kube, no unrelevant config options

For provider, as you may gues I have something as simple as:

http:
  routers:
    foo:
      entryPoints:
        - http
        - https
      service: foo
      rule: Host(`foo.example.com`)

    bar:
      entryPoints:
        - http
        - https
      service: bar
      rule: Host(`bar.example.com`)
      tls:
        certresolver: cloudflare

  services:
    foo:
      loadBalancer:
        servers:
          - url: http://localhost:5000/

    bar:
      loadBalancer:
        servers:
          - url: http://192.168.105.109:3000/

I was expecting wildcard certificate to be created and reused for all routers but that's not a case, if we do not pass tls route does not terminate ssl at all, if we do pass tls it seems like it does override the one defined in entrypoint :thinking: (which is also mentioned in the docs)

also there is:

--tls.stores.default.defaultGeneratedCert.resolver=letsencrypt \
--tls.stores.default.defaultGeneratedCert.domain.main=example.com \
--tls.stores.default.defaultGeneratedCert.domain.sans=*.example.com

which seems to do nothing

so, configuration it self is applied, I see that in the logs, certificate for bar.example.com is generatd, i see that in acme.json, so, technically, setup is working, but not the way expected - e.g. it will create certs for each and every route, which I want to bypass


temporary workaround

So far, I was able to achieve what i want with following setup:

CLOUDFLARE_DNS_API_TOKEN=xxxxxxxxxx traefik \
--log.level=INFO \
--providers.file.filename=/opt/homebrew/etc/traefik/provider.yml \
--certificatesresolvers.letsencrypt.acme.dnschallenge.provider=cloudflare \
--certificatesResolvers.letsencrypt.acme.dnschallenge.resolvers=1.1.1.1:53,1.0.0.1:53 \
--certificatesresolvers.letsencrypt.acme.email=hello@gmail.com \
--certificatesresolvers.letsencrypt.acme.storage=/opt/homebrew/etc/traefik/acme.json \
--entrypoints.http.address=:80 \
--entrypoints.https.address=:443

Note: I have removed domains and sans from entrypoint

and in my providers I do:

    bar:
      entryPoints:
        - http
        - https
      service: bar
      rule: Host(`bar.example.com`)
      tls:
        certresolver: cloudflare
        domains:
          - main: example.com
            sans:
              - "*.example.com"
    baz:
      entryPoints:
        - http
        - https
      service: bar
      rule: Host(`baz.example.com`)
      tls:
        certresolver: cloudflare
        domains:
          - main: example.com
            sans:
              - "*.example.com"

which is kind of fine, but i was wondering if I can keep it on entrypoint level so no need to copy paste it every time

1 Like

You already enabled TLS on entrypoint, so no need to do it again on router. Also note that you enable TLS in your example on both http and https entrypoints.

When the TLS certs are already created in acme.json, they might be served by Traefik. So make sure they are not in the file. Be aware that you have a LetsEncrypt limit of 5 TLS creations per domain per week, in case you erase the file.

Check simple Traefik example for doc and the example with dnsChallenge.

Thanks for notes, indeed i have tried to add tls to route but just because tls on entrypoints seems to be not working as expected, as you may see in my provider there are two services, one with tls and one without. Also, I have edited initial post a little bit, to better describe what I want to achieve, e.g. - everything works if i remove tls from entrypoints and start copy pasting tls to each route - I was wondering if it is possible to avoid that.

also, just in case, did tried run route without unserucre entrypoint which is good point, thank you

ps: and interestingly if i run following

    demo:
      entryPoints:
        #- http # <- removed this one
        - https
      service: demo
      rule: Host(`whatever.example.com`)
      # tls: ... # <- completely removed tls

I see from logs "Configuration received" message, everything seems to be fine, but

curl --resolve whatever.example.com:443:192.168.105.4 https://whatever.example.com/

responds with 404 from Traefik

You don't have a traefik.yml file on disk that might override CLI arguments (doc)?

1 Like

oh my gosh, thank you :folded_hands: indeed i have messed up with configuration files :man_facepalming: for anyone landing here in future:

  • make sure there are no traefik.toml, traefik.yml files in working directory
  • switch log level to DEBUG and have careful look at DBG github.com/traefik/traefik/v3/cmd/traefik/traefik.go:114 > Static configuration loaded [json] staticConfiguration={ to see if everything configured as intended

just in case, edited the post itsefl to contain all this as well

once agin, thank you, I still do not believe how silly it was always in front of me :man_shrugging:

This topic was automatically closed 3 days after the last reply. New replies are no longer allowed.