1. The problem I’m having:
I’m trying to get Caddy set up to serve my site and use DNS-01 challenges. The site I’m testing with is publicly accessible so HTTPS-01 would work, but I’m just testing before I try with my other domain that has a number of pages that are only available locally.
2. Error messages and/or full log output:
{"level":"info","ts":1729533169.102502,"logger":"tls.obtain","msg":"obtaining certificate","identifier":"api.planesover.me"}
{"level":"info","ts":1729533169.103499,"logger":"tls.obtain","msg":"obtaining certificate","identifier":"planesover.me"}
{"level":"info","ts":1729533169.2818322,"logger":"tls.issuance.acme.acme_client","msg":"trying to solve challenge","identifier":"planesover.me","challenge_type":"dns-01","ca":"https://acme-staging-v02.api.letsencrypt.org/directory"}
{"level":"info","ts":1729533169.2861934,"logger":"tls.issuance.acme.acme_client","msg":"trying to solve challenge","identifier":"api.planesover.me","challenge_type":"dns-01","ca":"https://acme-staging-v02.api.letsencrypt.org/directory"}
{"level":"error","ts":1729533169.7402678,"logger":"tls.issuance.acme.acme_client","msg":"cleaning up solver","identifier":"api.planesover.me","challenge_type":"dns-01","error":"no memory of presenting a DNS record for \"_acme-challenge.api.planesover.me\" (usually OK if presenting also failed)"}
{"level":"error","ts":1729533169.740531,"logger":"tls.issuance.acme.acme_client","msg":"cleaning up solver","identifier":"planesover.me","challenge_type":"dns-01","error":"no memory of presenting a DNS record for \"_acme-challenge.planesover.me\" (usually OK if presenting also failed)"}
{"level":"error","ts":1729533169.7955222,"logger":"tls.obtain","msg":"could not get certificate from issuer","identifier":"planesover.me","issuer":"acme-staging-v02.api.letsencrypt.org-directory","error":"[planesover.me] solving challenges: presenting for challenge: adding temporary record for zone \"me.\": Invalid http response status, {\"status\":\"ERROR\",\"message\":\"All API requests require an API key or API token.\"} (order=https://acme-staging-v02.api.letsencrypt.org/acme/order/168079483/19893903583) (ca=https://acme-staging-v02.api.letsencrypt.org/directory)"}
{"level":"error","ts":1729533169.7956219,"logger":"tls.obtain","msg":"will retry","error":"[planesover.me] Obtain: [planesover.me] solving challenges: presenting for challenge: adding temporary record for zone \"me.\": Invalid http response status, {\"status\":\"ERROR\",\"message\":\"All API requests require an API key or API token.\"} (order=https://acme-staging-v02.api.letsencrypt.org/acme/order/168079483/19893903583) (ca=https://acme-staging-v02.api.letsencrypt.org/directory)","attempt":4,"retrying_in":300,"elapsed":302.606371562,"max_duration":2592000}
{"level":"error","ts":1729533169.7995355,"logger":"tls.obtain","msg":"could not get certificate from issuer","identifier":"api.planesover.me","issuer":"acme-staging-v02.api.letsencrypt.org-directory","error":"[api.planesover.me] solving challenges: presenting for challenge: adding temporary record for zone \"me.\": Invalid http response status, {\"status\":\"ERROR\",\"message\":\"All API requests require an API key or API token.\"} (order=https://acme-staging-v02.api.letsencrypt.org/acme/order/168079483/19893903593) (ca=https://acme-staging-v02.api.letsencrypt.org/directory)"}
{"level":"error","ts":1729533169.799625,"logger":"tls.obtain","msg":"will retry","error":"[api.planesover.me] Obtain: [api.planesover.me] solving challenges: presenting for challenge: adding temporary record for zone \"me.\": Invalid http response status, {\"status\":\"ERROR\",\"message\":\"All API requests require an API key or API token.\"} (order=https://acme-staging-v02.api.letsencrypt.org/acme/order/168079483/19893903593) (ca=https://acme-staging-v02.api.letsencrypt.org/directory)","attempt":4,"retrying_in":300,"elapsed":302.610580374,"max_duration":2592000}
3. Caddy version:
2.7.6
4. How I installed and ran Caddy:
With portage
on Gentoo (www-servers/caddy::gentoo
). I also used this guide to help enable the dynamic-dns and porkbun modules.
a. System environment:
Gentoo x86-64 with Open RC. Running natively (not in a container).
b. Command:
$ rc-service caddy start
c. Service/unit/compose file:
#!/sbin/openrc-run
# Copyright 1999-2023 Gentoo Authors
# Distributed under the terms of the GNU General Public License v2
extra_commands="checkconfig"
extra_started_commands="reload"
description="Caddy web server"
pidfile=${pidfile:-"/run/${RC_SVCNAME}.pid"}
command="/usr/bin/caddy"
command_user="${command_user:-"http:http"}"
caddy_config="${caddy_config:-"/etc/caddy/Caddyfile"}"
command_args="${command_args:-"run --config ${caddy_config}"}"
command_background="true"
logfile="${logfile:-"/var/log/${RC_SVCNAME}/${RC_SVCNAME}.log"}"
start_stop_daemon_args="--user ${command_user%:*} --group ${command_user#*:}
--stdout ${logfile} --stderr ${logfile}"
: "${supervisor:=supervise-daemon}"
: "${respawn_delay:=5}"
: "${respawn_max:=10}"
: "${respawn_period:=60}"
depend() {
need net
}
checkconfig() {
if [ ! -f "${caddy_config}" ] ; then
ewarn "${caddy_config} does not exist."
return 1
fi
"${command}" validate --config "${caddy_config}" >> "${logfile}" 2>&1
}
start() {
checkconfig || { eerror "Invalid configuration file !" && return 1; }
checkpath --directory --mode 755 --owner root "${pidfile%/*}"
checkpath --directory --mode 755 --owner "${command_user}" "${logfile%/*}"
default_start
}
reload() {
if ! service_started "${SVCNAME}" ; then
eerror "${SVCNAME} isn't running"
return 1
fi
checkconfig || { eerror "Invalid configuration file !" && return 1; }
ebegin "Reloading ${SVCNAME}"
"${command}" reload --force --config "${caddy_config}" > /dev/null 2>&1
eend $?
}
d. My complete Caddy config:
{
email me@benbuhse.com
acme_ca https://acme-staging-v02.api.letsencrypt.org/directory
acme_dns porkbun {
api_key {file./etc/caddy/creds/porkbun_api_key}
api_secret_key {file./etc/caddy/creds/porkbun_secret_key}
}
dynamic_dns {
provider porkbun {
api_key {file./etc/caddy/creds/porkbun_api_key}
api_secret_key {file./etc/caddy/creds/porkbun_secret_key}
}
domains {
planesover.me
}
}
}
# planesover.me server
planesover.me {
root * /var/www/planesoverme-client/
try_files {path} /
file_server
encode zstd gzip
}
# planesover.me client reverse-proxy
api.planesover.me {
reverse_proxy http://192.168.1.2:3000 {
header_down "Access-Control-Allow-Origin" "https://planesover.me"
header_down "Access-Control-Allow-Methods" "GET, POST, OPTIONS"
header_down "Access-Control-Allow-Headers" "Authorization"
}
encode zstd gzip
}
The API key and secret key files each contain nothing but their respective keys.