LetsEncrypt Staging & DNS Challenge Not Working

1. Caddy version (caddy version):

Caddy 2.3.0

2. How I run Caddy:

a. System environment:

Ubuntu 20.04 / Docker 20.10.2

Dockerfile to build Caddy with support for Cloudflare DNS challenge:

FROM caddy:2.3.0-builder AS builder

RUN xcaddy build \
    --with github.com/caddy-dns/cloudflare

FROM caddy:2.3.0

COPY --from=builder /usr/bin/caddy /usr/bin/caddy

b. Command:

docker-compose up

c. Service/unit/compose file:

version: '3'
services:
  caddy:
    container_name: caddy
    build:
      context: ./caddy
    volumes:
      - ./caddy:/etc/caddy
    ports:
      - 11443:11443
    environment:
      - DOMAIN=${DOMAIN}
      - HA_IP=${HA_IP}
      - CLOUDFLARE_API_TOKEN=${CLOUDFLARE_API_TOKEN}

d. My complete Caddyfile or JSON config:

{env.DOMAIN}:11443 {
    tls {
        issuer acme {
            dir https://acme-staging-v02.api.letsencrypt.org/directory
            disable_http_challenge
            disable_tlsalpn_challenge
            dns cloudflare {env.CLOUDFLARE_API_TOKEN}
        }
    }

   reverse_proxy {env.HA_IP}:8123
}

In case it’s helpful, here is the JSON output of docker exec -it caddy caddy adapt --config /etc/caddy/Caddyfile --pretty:

{
	"apps": {
		"http": {
			"servers": {
				"srv0": {
					"listen": [
						":11443"
					],
					"routes": [
						{
							"match": [
								{
									"host": [
										"{env.DOMAIN}"
									]
								}
							],
							"handle": [
								{
									"handler": "subroute",
									"routes": [
										{
											"handle": [
												{
													"handler": "reverse_proxy",
													"upstreams": [
														{
															"dial": "{env.HA_IP}:8123"
														}
													]
												}
											]
										}
									]
								}
							],
							"terminal": true
						}
					]
				}
			}
		},
		"tls": {
			"automation": {
				"policies": [
					{
						"subjects": [
							"{env.DOMAIN}"
						],
						"issuers": [
							{
								"ca": "https://acme-staging-v02.api.letsencrypt.org/directory",
								"challenges": {
									"dns": {
										"provider": {
											"api_token": "{env.CLOUDFLARE_API_TOKEN}",
											"name": "cloudflare"
										}
									},
									"http": {
										"disabled": true
									},
									"tls-alpn": {
										"disabled": true
									}
								},
								"module": "acme"
							}
						]
					}
				]
			}
		}
	}
}

3. The problem I’m having:

I am having two problems:

  1. Caddy keeps trying to use the HTTP and TLS challenges even when I have specifically disabled them and specified I wish to use DNS instead. Not once has it tried to use the DNS challenge.

  2. After continually failing to set this up, I decided to switch to LetsEncrypt’s staging environment for testing. Caddy continues to use LetsEncrypt’s production environment and seems to be ignoring the dir directive.

4. Error messages and/or full log output:

Creating caddy ... done
Attaching to caddy
caddy    | {"level":"info","ts":1611381613.9336128,"msg":"using provided configuration","config_file":"/etc/caddy/Caddyfile","config_adapter":"caddyfile"}
caddy    | {"level":"info","ts":1611381613.936756,"logger":"admin","msg":"admin endpoint started","address":"tcp/localhost:2019","enforce_origin":false,"origins":["localhost:2019","[::1]:2019","127.0.0.1:2019"]}
caddy    | {"level":"info","ts":1611381613.9373271,"logger":"http","msg":"enabling automatic HTTP->HTTPS redirects","server_name":"srv0"}
caddy    | {"level":"info","ts":1611381613.9377863,"logger":"tls.cache.maintenance","msg":"started background certificate maintenance","cache":"0xc0002d1110"}
caddy    | {"level":"info","ts":1611381613.937993,"logger":"http","msg":"enabling automatic TLS certificate management","domains":["example.com"]}
caddy    | {"level":"info","ts":1611381613.9381752,"logger":"tls","msg":"cleaned up storage units"}
caddy    | {"level":"info","ts":1611381613.939109,"msg":"autosaved config","file":"/config/caddy/autosave.json"}
caddy    | {"level":"info","ts":1611381613.9394476,"msg":"serving initial configuration"}
caddy    | {"level":"info","ts":1611381613.9396424,"logger":"tls.obtain","msg":"acquiring lock","identifier":"example.com"}
caddy    | {"level":"info","ts":1611381613.9404516,"logger":"tls.obtain","msg":"lock acquired","identifier":"example.com"}
caddy    | {"level":"info","ts":1611381614.5644536,"logger":"tls.issuance.acme","msg":"waiting on internal rate limiter","identifiers":["example.com"]}
caddy    | {"level":"info","ts":1611381614.5645325,"logger":"tls.issuance.acme","msg":"done waiting on internal rate limiter","identifiers":["example.com"]}
caddy    | {"level":"info","ts":1611381614.9442313,"logger":"tls.issuance.acme.acme_client","msg":"trying to solve challenge","identifier":"example.com","challenge_type":"tls-alpn-01","ca":"https://acme-v02.api.letsencrypt.org/directory"}

5. What I already tried:

I have tried on both Caddy 2.2.1 and Caddy 2.3.0.

Issue 1 (DNS challenge not working):

  • Originally I tried specifying only the tls.dns directive because the DNS section of Caddy’s automatic HTTPS documentation states that “If the DNS challenge is enabled, other challenges are disabled by default.”
  • When that did not work, I tried specifically disabling other challenges within the tls.issuer acme block. This made no difference.

Issue 2 (Not using LetsEncrypt staging as specified):

I have tried specifying this configuration as:

  • acme_ca in the global block as specified here
  • the ca directive in the tls block as specified here
  • the dir directive in the tls.issuer acme as shown above in my configuration and specified here

In each case, Caddy keeps using LetsEncrypt’s production environment. A few times it has tried to use ZeroSSL as well.


Since I cannot seem to get any of the TLS options working properly, I thought maybe it’s not the configuration but something else and therefore tried the following as well:

  • Putting “garbage” into my configuration (e.g. aksdfjkasf at the top). Caddy refused to start because of a bad config, so I know it’s at least reading my configuration
  • Using Caddy 2.2.1 instead of 2.3.0
  • Wiping my Docker containers and volumes to ensure nothing was cached and trying again

I’m at a loss and not quite sure what to try next. I’ve never had issues with Caddy before - it’s always worked first time for me, but I’ve also never tried using the DNS challenge before nor have I ever needed to use the LetsEncrypt staging server before. Suggestions?

Please don’t forget to persist the /data directory as a volume as well. This is important to avoid losing your certs and keys when the container goes away. It also puts you at risk of hitting rate limits.

FYI you can use the syntax {$DOMAIN} here instead, which is the Caddyfile-specific env var syntax. The difference is that the {$VAR} syntax is replaced at adapt-time, whereas {env.VAR} is replaced at runtime.

The issues you’re having with TLS may actually because of this. Caddy doesn’t support placeholder replacement everywhere, because certain fields are necessary to be static for various reasons.

Just a hunch, but it might be that the hostname is replaced with your env var, then that host is looked up in the TLS automation policies config and no matching subject is found (because the env var might not be replaced there), so Caddy uses its defaults from automatic HTTPS instead.

Yes, definitely. I normally put the /data directory into a named volume - in this case, I specifically removed my volumes from my Docker configuration as when I was debugging I wanted to avoid data persisting between reboots.

This was it. It works with the other environment variable syntax. I never even considered that - I just copied and pasted the {env.__} syntax from the DNS Cloudflare example and went with it.

Thank you very much - I’ve spent more time than I should probably admit trying to get this to work!

1 Like

This topic was automatically closed after 30 days. New replies are no longer allowed.