1. The problem I’m having:
I’m trying to set up external access to services such as Frigate and Home Assistant. I could set up access to one service easily, but after I found that No-IP doesn’t support subdomains, I’m to switching to DuckDNS. Now can’t get Caddy to successfully negotiate with the domain. Starting the docker container generates the errors below and the domain cannot be reached in a browser.
For now, I’m only trying with Home Assistant, and I’ll deal with subdomains and the other services once I get past this.
Nothing in the logs seems to point me in a particular direction except that the URL presented in the error messages contains
domains=net
Simply copy-pasting the entire URL into my browser but replacing ‘net’ with the correct domain provides an ‘OK’ response, but I have no idea where Caddy is getting ‘net’ from or how to correct it.
Also I am completely new to networking in general, so while I’m decent with Linux and Docker, I fully admit that I have no idea what I’m doing here.
2. Error messages and/or full log output:
caddy-1 | 2026/09/28 17:43:05.635 INFO maxprocs: Leaving GOMAXPROCS=4: CPU quota undefined
caddy-1 | 2026/09/28 17:43:05.635 INFO GOMEMLIMIT is updated {"GOMEMLIMIT": 14794795008, "previous": 9223372036854775807}
caddy-1 | 2026/09/28 17:43:05.635 INFO using config from file {"file": "/etc/caddy/Caddyfile"}
caddy-1 | 2026/09/28 17:43:05.635 INFO adapted config to JSON {"adapter": "caddyfile"}
caddy-1 | 2026/09/28 17:43:05.635 INFO redirected default logger {"from": "stderr", "to": "stdout"}
caddy-1 | 2026/09/28 17:43:05.636 INFO admin admin endpoint started {"address": "localhost:2019", "enforce_origin": false, "origins": ["//localhost:2019", "//[::1]:2019", "//127.0.0.1:2019"]}
caddy-1 | 2026/09/28 17:43:05.636 INFO http.auto_https server is listening only on the HTTPS port but has no TLS connection policies; adding one to enable TLS {"server_name": "srv0", "https_port": 443}
caddy-1 | 2026/09/28 17:43:05.636 INFO http.auto_https enabling automatic HTTP->HTTPS redirects {"server_name": "srv0"}
caddy-1 | 2026/09/28 17:43:05.636 INFO tls.cache.maintenance started background certificate maintenance {"cache": "0x9edcf5ac80"}
caddy-1 | 2026/09/28 17:43:05.636 DEBUG http.auto_https adjusted config {"tls": {"automation":{"policies":[{"subjects":["{domain}.duckdns.net"]},{}]}}, "http": {"servers":{"remaining_auto_https_redirects":{"listen":[":80"],"routes":[{},{}]},"srv0":{"listen":[":443"],"routes":[{"handle":[{"handler":"subroute","routes":[{"handle":[{"handler":"reverse_proxy","upstreams":[{"dial":"192.168.1.154:8123"}]}]}]}],"terminal":true}],"tls_connection_policies":[{}],"automatic_https":{}}}}}
caddy-1 | 2026/09/28 17:43:05.637 DEBUG http starting server loop {"address": "[::]:443", "tls": true, "http3": false}
caddy-1 | 2026/09/28 17:43:05.637 INFO http enabling HTTP/3 listener {"addr": ":443"}
caddy-1 | 2026/09/28 17:43:05.637 INFO http.log server running {"name": "srv0", "protocols": ["h1", "h2", "h3"]}
caddy-1 | 2026/09/28 17:43:05.637 DEBUG http starting server loop {"address": "[::]:80", "tls": false, "http3": false}
caddy-1 | 2026/09/28 17:43:05.637 WARN http HTTP/2 skipped because it requires TLS {"network": "tcp", "addr": ":80"}
caddy-1 | 2026/09/28 17:43:05.637 WARN http HTTP/3 skipped because it requires TLS {"network": "tcp", "addr": ":80"}
caddy-1 | 2026/09/28 17:43:05.637 INFO http.log server running {"name": "remaining_auto_https_redirects", "protocols": ["h1", "h2", "h3"]}
caddy-1 | 2026/09/28 17:43:05.637 INFO http enabling automatic TLS certificate management {"domains": ["{domain}.duckdns.net"]}
caddy-1 | 2026/09/28 17:43:05.637 DEBUG events event {"name": "started", "id": "dbc7dd27-394c-4098-9974-c96fbf957ff8", "origin": "", "data": null}
caddy-1 | 2026/09/28 17:43:05.637 INFO autosaved config (load with --resume flag) {"file": "/config/caddy/autosave.json"}
caddy-1 | 2026/09/28 17:43:05.637 INFO serving initial configuration
caddy-1 | 2026/09/28 17:43:05.640 INFO tls.obtain acquiring lock {"identifier": "{domain}.duckdns.net"}
caddy-1 | 2026/09/28 17:43:05.643 INFO tls storage cleaning happened too recently; skipping for now {"storage": "FileStorage:/data/caddy", "instance": "e4fa6d30-8367-48b1-87f6-995f3c79497e", "try_again": "2026/09/29 17:43:05.643", "try_again_in": 86399.999999703}
caddy-1 | 2026/09/28 17:43:05.643 INFO tls finished cleaning storage units
caddy-1 | 2026/09/28 17:43:05.643 INFO tls.obtain lock acquired {"identifier": "{domain}.duckdns.net"}
caddy-1 | 2026/09/28 17:43:05.643 INFO tls.obtain obtaining certificate {"identifier": "{domain}.duckdns.net"}
caddy-1 | 2026/09/28 17:43:05.643 DEBUG events event {"name": "cert_obtaining", "id": "c5fa3bc4-3281-4954-b023-7124cc189720", "origin": "tls", "data": {"identifier":"{domain}.duckdns.net"}}
caddy-1 | 2026/09/28 17:43:05.643 DEBUG tls created CSR {"identifiers": ["{domain}.duckdns.net"], "san_dns_names": ["{domain}.duckdns.net"], "san_emails": [], "common_name": "", "extra_extensions": 0}
caddy-1 | 2026/09/28 17:43:05.644 DEBUG tls.obtain trying issuer 1/1 {"issuer": "acme-v02.api.letsencrypt.org-directory"}
caddy-1 | 2026/09/28 17:43:05.646 DEBUG http using existing ACME account because key found in storage associated with email {"email": "default", "ca": "https://acme-v02.api.letsencrypt.org/directory"}
caddy-1 | 2026/09/28 17:43:05.646 DEBUG http using existing ACME account because key found in storage associated with email {"email": "", "ca": "https://acme-v02.api.letsencrypt.org/directory"}
caddy-1 | 2026/09/28 17:43:05.646 INFO http waiting on internal rate limiter {"identifiers": ["{domain}.duckdns.net"], "ca": "https://acme-v02.api.letsencrypt.org/directory", "account": ""}
caddy-1 | 2026/09/28 17:43:05.646 INFO http done waiting on internal rate limiter {"identifiers": ["{domain}.duckdns.net"], "ca": "https://acme-v02.api.letsencrypt.org/directory", "account": ""}
caddy-1 | 2026/09/28 17:43:05.646 INFO http using ACME account {"account_id": "https://acme-v02.api.letsencrypt.org/acme/acct/3801910666", "account_contact": []}
caddy-1 | 2026/09/28 17:43:05.884 DEBUG http.acme_client http request {"method": "GET", "url": "https://acme-v02.api.letsencrypt.org/directory", "headers": {"User-Agent":["Caddy/2.11.4 CertMagic acmez (linux; amd64)"]}, "response_headers": {"Cache-Control":["public, max-age=0, no-cache"],"Content-Length":["961"],"Content-Type":["application/json"],"Date":["Mon, 28 Sep 2026 17:43:05 GMT"],"Server":["nginx"],"Strict-Transport-Security":["max-age=604800"],"X-Frame-Options":["DENY"]}, "status_code": 200}
caddy-1 | 2026/09/28 17:43:05.885 DEBUG http.acme_client creating order {"account": "https://acme-v02.api.letsencrypt.org/acme/acct/3801910666", "identifiers": ["{domain}.duckdns.net"]}
caddy-1 | 2026/09/28 17:43:05.931 DEBUG http.acme_client http request {"method": "HEAD", "url": "https://acme-v02.api.letsencrypt.org/acme/new-nonce", "headers": {"User-Agent":["Caddy/2.11.4 CertMagic acmez (linux; amd64)"]}, "response_headers": {"Cache-Control":["public, max-age=0, no-cache"],"Date":["Mon, 28 Sep 2026 17:43:05 GMT"],"Link":["<https://acme-v02.api.letsencrypt.org/directory>;rel=\"index\""],"Replay-Nonce":["ZNNrQVmEkVypnuh2_5IqwMoUSlIWvbf9qinD34ADEflE4TRrj9w"],"Server":["nginx"],"Strict-Transport-Security":["max-age=604800"],"X-Frame-Options":["DENY"]}, "status_code": 200}
caddy-1 | 2026/09/28 17:43:06.080 DEBUG http.acme_client http request {"method": "POST", "url": "https://acme-v02.api.letsencrypt.org/acme/new-order", "headers": {"Content-Type":["application/jose+json"],"User-Agent":["Caddy/2.11.4 CertMagic acmez (linux; amd64)"]}, "response_headers": {"Boulder-Requester":["3801910666"],"Cache-Control":["public, max-age=0, no-cache"],"Content-Length":["358"],"Content-Type":["application/json"],"Date":["Mon, 28 Sep 2026 17:43:06 GMT"],"Link":["<https://acme-v02.api.letsencrypt.org/directory>;rel=\"index\""],"Location":["https://acme-v02.api.letsencrypt.org/acme/order/3801910666/562911743466"],"Replay-Nonce":["ZNNrQVmEvsf7sekwXkXysBMQw_W-tXMQsc2JqwVFoLcsXyxWoGg"],"Server":["nginx"],"Strict-Transport-Security":["max-age=604800"],"X-Frame-Options":["DENY"]}, "status_code": 201}
caddy-1 | 2026/09/28 17:43:06.133 DEBUG http.acme_client http request {"method": "POST", "url": "https://acme-v02.api.letsencrypt.org/acme/authz/3801910666/789152183216", "headers": {"Content-Type":["application/jose+json"],"User-Agent":["Caddy/2.11.4 CertMagic acmez (linux; amd64)"]}, "response_headers": {"Boulder-Requester":["3801910666"],"Cache-Control":["public, max-age=0, no-cache"],"Content-Length":["832"],"Content-Type":["application/json"],"Date":["Mon, 28 Sep 2026 17:43:06 GMT"],"Link":["<https://acme-v02.api.letsencrypt.org/directory>;rel=\"index\""],"Replay-Nonce":["ZNNrQVmEqzFttuUPmXdM-WISQGolUOpFGG-ZTIx3bXbBVaDJWSM"],"Server":["nginx"],"Strict-Transport-Security":["max-age=604800"],"X-Frame-Options":["DENY"]}, "status_code": 200}
caddy-1 | 2026/09/28 17:43:06.133 INFO http.acme_client trying to solve challenge {"identifier": "{domain}.duckdns.net", "challenge_type": "dns-01", "ca": "https://acme-v02.api.letsencrypt.org/directory"}
caddy-1 | 2026/09/28 17:43:06.135 DEBUG tls.issuance.acme.dns_manager.dns_manager.soa_lookup fetched SOA {"msg": ";; opcode: QUERY, status: NOERROR, id: 39723\n;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1\n\n;; OPT PSEUDOSECTION:\n; EDNS: version 0; flags:; udp: 65494\n\n;; QUESTION SECTION:\n;_acme-challenge.{domain}.duckdns.net.\tIN\t SOA\n\n;; ANSWER SECTION:\n_acme-challenge.{domain}.duckdns.net.\t81998\tIN\tSOA\tns1.sedoparking.com. hostmaster.sedo.de. 2018051601 86400 10800 604800 86400\n"}
caddy-1 | 2026/09/28 17:43:06.136 DEBUG tls.issuance.acme.dns_manager.dns_manager creating DNS record {"dns_name": "_acme-challenge.{domain}.duckdns.net", "zone": "_acme-challenge.{domain}.duckdns.net.", "record_name": "@", "record_type": "TXT", "record_data": "JuteU7OmfrdWiAtf--61-W9-BrN-EXHWXrn6sBVgZtE", "record_ttl": 0}
caddy-1 | 2026/09/28 17:43:06.308 ERROR http.acme_client cleaning up solver {"identifier": "{domain}.duckdns.net", "challenge_type": "dns-01", "error": "no memory of presenting a DNS record for \"_acme-challenge.{domain}.duckdns.net\" (usually OK if presenting also failed)"}
caddy-1 | 2026/09/28 17:43:06.388 DEBUG http.acme_client http request {"method": "POST", "url": "https://acme-v02.api.letsencrypt.org/acme/authz/3801910666/789152183216", "headers": {"Content-Type":["application/jose+json"],"User-Agent":["Caddy/2.11.4 CertMagic acmez (linux; amd64)"]}, "response_headers": {"Boulder-Requester":["3801910666"],"Cache-Control":["public, max-age=0, no-cache"],"Content-Length":["836"],"Content-Type":["application/json"],"Date":["Mon, 28 Sep 2026 17:43:06 GMT"],"Link":["<https://acme-v02.api.letsencrypt.org/directory>;rel=\"index\""],"Replay-Nonce":["b_8FQRl9KLOhnrINUmEfZ8B_wn-bgiAGc-6rki8LvVKAmSxaBX4"],"Server":["nginx"],"Strict-Transport-Security":["max-age=604800"],"X-Frame-Options":["DENY"]}, "status_code": 200}
caddy-1 | 2026/09/28 17:43:06.389 ERROR tls.obtain could not get certificate from issuer {"identifier": "{domain}.duckdns.net", "issuer": "acme-v02.api.letsencrypt.org-directory", "error": "[{domain}.duckdns.net] solving challenges: presenting for challenge: adding temporary record for zone \"_acme-challenge.{domain}.duckdns.net.\": DuckDNS request failed, expected (OK) but got (KO), url: [https://www.duckdns.org/update?domains=net&token={token}&txt=JuteU7OmfrdWiAtf--61-W9-BrN-EXHWXrn6sBVgZtE&verbose=true], body: KO (order=https://acme-v02.api.letsencrypt.org/acme/order/3801910666/562911743466) (ca=https://acme-v02.api.letsencrypt.org/directory)"}
caddy-1 | 2026/09/28 17:43:06.389 DEBUG events event {"name": "cert_failed", "id": "95517aca-a7fd-437e-bbcd-88cf476ec8ca", "origin": "tls", "data": {"error":{},"identifier":"{domain}.duckdns.net","issuers":["acme-v02.api.letsencrypt.org-directory"],"renewal":false}}
caddy-1 | 2026/09/28 17:43:06.389 ERROR tls.obtain will retry {"error": "[{domain}.duckdns.net] Obtain: [{domain}.duckdns.net] solving challenges: presenting for challenge: adding temporary record for zone \"_acme-challenge.{domain}.duckdns.net.\": DuckDNS request failed, expected (OK) but got (KO), url: [https://www.duckdns.org/update?domains=net&token={token}&txt=JuteU7OmfrdWiAtf--61-W9-BrN-EXHWXrn6sBVgZtE&verbose=true], body: KO (order=https://acme-v02.api.letsencrypt.org/acme/order/3801910666/562911743466) (ca=https://acme-v02.api.letsencrypt.org/directory)", "attempt": 1, "retrying_in": 60, "elapsed": 0.745959225, "max_duration": 2592000}
3. Caddy version:
v2.11.4 h1:XKxkMTgNSizEvKG6QHue6cAsFOteU2qA61w2tKkCWi0=
4. How I installed and ran Caddy:
Docker compose. docker-compose.yaml posted below.
a. System environment:
Zorin OS 18.1 Kernel 7.0.0-31-generic.
b. Command:
sudo docker compose up caddy
c. docker-compose.yaml file:
services:
homeassistant:
container_name: homeassistant
image: "ghcr.io/home-assistant/home-assistant:stable"
volumes:
- /home/randy/homeassistant/config:/config
- /etc/localtime:/etc/localtime:ro
- /run/dbus:/run/dbus:ro
restart: unless-stopped
#privileged: true
network_mode: host
environment:
TZ: America/Indianapolis
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
frigate:
container_name: frigate
runtime: nvidia
restart: unless-stopped
stop_grace_period: 30s
image: ghcr.io/blakeblackshear/frigate:stable-tensorrt
shm_size: "256mb"
volumes:
- /home/randy/frigate/config:/config
- /mnt/surveil/media:/media/frigate
- type: tmpfs # 1GB In-memory filesystem for recording segment storage
target: /tmp/cache
tmpfs:
size: 1000000000
ports:
- "8971:8971"
- "8554:8554" # RTSP feeds
- "1984:1984" # go2rtc web interface
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
environment:
- NVIDIA_VISIBLE_DEVICES=all
- NVIDIA_DRIVER_CAPABILITIES=compute,utility,video
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: 1
capabilities: [gpu]
matterjs-server:
container_name: matterjs-server
image: ghcr.io/matter-js/matterjs-server:stable
read_only: true
network_mode: host
restart: unless-stopped
# Optional Node.js heap hint. No default — size depends on the number of
# commissioned nodes. See docs/docker.md "Node.js Memory Limit".
# environment:
# NODE_OPTIONS: "--max-old-space-size=512"
volumes:
- /home/randy/matterjs-server:/data
music-assistant-server:
image: ghcr.io/music-assistant/server:stable
container_name: music-assistant-server
restart: unless-stopped
# Network mode must be set to host for MA to discover and stream to players (see networking note below)
network_mode: host
volumes:
- /home/music-assistant-server/data:/data/
# Optional: expose local music to MA by bind-mounting it read-only
- /mnt/media/Music:/media:ro
environment:
# Provide logging level as environment variable.
# default=info, possible=(critical, error, warning, info, debug)
- LOG_LEVEL=info
mosquitto:
image: eclipse-mosquitto
container_name: mosquitto
volumes:
- /home/randy/mosquitto:/mosquitto
- /home/randy/mosquitto/data:/mosquitto/data
- /home/randy/mosquitto/log:/mosquitto/log
ports:
- 1883:1883
- 9001:9001
caddy:
image: serfriz/caddy-duckdns:latest
restart: unless-stopped
ports:
- "80:80"
- "443:443"
- "443:443/udp"
volumes:
- /home/randy/caddy/conf:/etc/caddy
- /home/randy/caddy/data:/data
- /home/randy/caddy/config:/config
duckdns:
image: lscr.io/linuxserver/duckdns:latest
container_name: duckdns
network_mode: host #optional
environment:
#- PUID=1000 #optional
#- PGID=1000 #optional
#- TZ=Etc/UTC #optional
- SUBDOMAINS={domain}
- TOKEN={token}
- UPDATE_IP= #optional
- LOG_FILE=false #optional
volumes:
- /home/randy/duckdns/config:/config #optional
restart: unless-stopped
d. My complete Caddy config:
{
debug
log {
output stdout
format console
}
}
{domain}.duckdns.net {
reverse_proxy 192.168.1.154:8123
tls {
dns duckdns {token}
}
}
5. Links to relevant resources:
Type here