{"record":{"id":"da2cb40c51b611a3","repo":"caddyserver/caddy","slug":"dns-challenge-enabled-but-no-dns-provider-configu","errorCode":null,"errorMessage":"DNS challenge enabled, but no DNS provider configured","messagePattern":"DNS challenge enabled, but no DNS provider configured","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"modules/caddytls/acmeissuer.go","lineNumber":216,"sourceCode":"\tif iss.Challenges != nil && iss.Challenges.DNS != nil && iss.Challenges.DNS.solver == nil {\n\t\tvar prov certmagic.DNSProvider\n\t\tif iss.Challenges.DNS.ProviderRaw != nil {\n\t\t\t// a challenge provider has been locally configured - use it\n\t\t\tval, err := ctx.LoadModule(iss.Challenges.DNS, \"ProviderRaw\")\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"loading DNS provider module: %v\", err)\n\t\t\t}\n\t\t\tprov = val.(certmagic.DNSProvider)\n\t\t} else if tlsAppIface, err := ctx.AppIfConfigured(\"tls\"); err == nil {\n\t\t\t// no locally configured DNS challenge provider, but if there is\n\t\t\t// a global DNS module configured with the TLS app, use that\n\t\t\ttlsApp := tlsAppIface.(*TLS)\n\t\t\tif tlsApp.dns != nil {\n\t\t\t\tprov = tlsApp.dns.(certmagic.DNSProvider)\n\t\t\t}\n\t\t}\n\t\tif prov == nil {\n\t\t\treturn fmt.Errorf(\"DNS challenge enabled, but no DNS provider configured\")\n\t\t}\n\t\tiss.Challenges.DNS.solver = &certmagic.DNS01Solver{\n\t\t\tDNSManager: certmagic.DNSManager{\n\t\t\t\tDNSProvider:        prov,\n\t\t\t\tTTL:                time.Duration(iss.Challenges.DNS.TTL),\n\t\t\t\tPropagationDelay:   time.Duration(iss.Challenges.DNS.PropagationDelay),\n\t\t\t\tPropagationTimeout: time.Duration(iss.Challenges.DNS.PropagationTimeout),\n\t\t\t\tResolvers:          iss.Challenges.DNS.Resolvers,\n\t\t\t\tOverrideDomain:     iss.Challenges.DNS.OverrideDomain,\n\t\t\t\tLogger:             iss.logger.Named(\"dns_manager\"),\n\t\t\t},\n\t\t}\n\t}\n\n\t// add any custom CAs to trust store\n\tif len(iss.TrustedRootsPEMFiles) > 0 {\n\t\tiss.rootPool = x509.NewCertPool()\n\t\tfor _, pemFile := range iss.TrustedRootsPEMFiles {","sourceCodeStart":198,"sourceCodeEnd":234,"githubUrl":"https://github.com/caddyserver/caddy/blob/50e54ee279aa1e504fe218ca49ab6ae16c100410/modules/caddytls/acmeissuer.go#L198-L234","documentation":"Returned by ACMEIssuer.Provision (modules/caddytls/acmeissuer.go:216) when the DNS-01 challenge is enabled but no provider could be found: neither a locally configured provider module (dns directive on the issuer) nor a global dns app referenced by the TLS app. Caddy refuses to create a DNS-01 solver without a provider because it could never clean up or propagate records.","triggerScenarios":"Enabling the DNS challenge implicitly (e.g. requesting a wildcard certificate forces DNS-01) without any dns provider configured; or via JSON where challenges.dns is a non-null object (e.g. only propagation_timeout set) with no provider_raw and no global dns app - merely enabling the dns challenge block triggers this error.","commonSituations":"Wildcard certs (*.example.com) requested with no DNS plugin installed; global dns app configured in a different config snippet that was not imported; JSON configs with \"challenges\": {\"dns\": {}} as an empty object after removing provider settings during cleanup.","solutions":["Add a DNS provider to the issuer: tls { dns cloudflare {env.CF_API_TOKEN} } (requires the plugin build)","Or configure the global dns app and reference it: apps { dns { provider ... } } so all issuers share it","If you did not intend DNS-01, remove the empty challenges.dns block or the wildcard hostname that forces it","Build the binary with the needed plugin via xcaddy if the module is missing"],"exampleFix":"# before: wildcard forces DNS-01, no provider\n *.example.com {\n   tls {\n     # nothing here\n   }\n }\n\n# after\n *.example.com {\n   tls {\n     dns cloudflare {env.CF_API_TOKEN}\n   }\n }","handlingStrategy":"validation","validationCode":"# config-time guard: any wildcard hostname or dns challenge block needs a provider\n# CI check (simplified):\n#   config contains 'dns' challenge or '*.' hostname  =>  config must contain\n#   a 'dns <provider>' directive or a global 'dns' app block\ngrep -q '^\\s*\\*\\.' Caddyfile && ! grep -qE '^\\s*dns (cloudflare|route53|desec|\\w+)' Caddyfile && {\n  echo \"wildcard cert requires a DNS provider\"; exit 1; }","typeGuard":null,"tryCatchPattern":"if err := issuer.Provision(ctx); err != nil {\n    if strings.Contains(err.Error(), \"DNS challenge enabled, but no DNS provider\") {\n        // add 'dns <provider> <creds>' to the issuer or a global dns app; or drop the wildcard\n    }\n    return err\n}","preventionTips":["Associate every wildcard hostname in config review with a visible dns provider directive","Never leave an empty challenges { dns { } } block in JSON configs - delete it instead","Prefer configuring the provider once in the global dns app so all issuers inherit it"],"tags":["tls","acme","dns-challenge","wildcard","configuration"],"backgroundTag":null,"analyzedSha":"50e54ee279aa1e504fe218ca49ab6ae16c100410","analyzedAt":"2026-08-15T09:20:21.641Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}