1. The problem I’m having:
We see the _acme-challenge records stay in our dns records forever. We also see an error in the caddy output that probably explains why (error deleting temporary record for name (…)).
2. Error messages and/or full log output:
2026/03/10 17:17:10.418 INFO tls.obtain acquiring lock {"identifier": "ksa.stamhoofd.dev"}
2026/03/10 17:17:10.420 INFO tls.obtain lock acquired {"identifier": "ksa.stamhoofd.dev"}
2026/03/10 17:17:10.421 INFO tls.obtain obtaining certificate {"identifier": "ksa.stamhoofd.dev"}
2026/03/10 17:17:10.422 INFO http waiting on internal rate limiter {"identifiers": ["ksa.stamhoofd.dev"], "ca": "https://acme-v02.api.letsencrypt.org/directory", "account": ""}
2026/03/10 17:17:10.422 INFO http done waiting on internal rate limiter {"identifiers": ["ksa.stamhoofd.dev"], "ca": "https://acme-v02.api.letsencrypt.org/directory", "account": ""}
2026/03/10 17:17:10.422 INFO http using ACME account {"account_id": "https://acme-v02.api.letsencrypt.org/acme/acct/xxxxxxxx", "account_contact": []}
2026/03/10 17:18:15.326 ERROR cleaning up solver {"identifier": "ksa.stamhoofd.dev", "challenge_type": "dns-01", "error": "deleting temporary record for name \"stamhoofd.dev.\" in zone {\"_acme-challenge.ksa\" \"0s\" \"TXT\" \"HVliU-L8zpCEHL8X9bkbONdLUCyW5AM2e8r7M2N72-g\"}: strconv.Atoi: parsing \"\": invalid syntax"}
github.com/mholt/acmez/v3.(*Client).pollAuthorization
github.com/mholt/acmez/v3@v3.1.6/client.go:557
github.com/mholt/acmez/v3.(*Client).solveChallenges
github.com/mholt/acmez/v3@v3.1.6/client.go:391
github.com/mholt/acmez/v3.(*Client).ObtainCertificate
github.com/mholt/acmez/v3@v3.1.6/client.go:149
github.com/caddyserver/certmagic.(*ACMEIssuer).doIssue
github.com/caddyserver/certmagic@v0.25.2/acmeissuer.go:498
github.com/caddyserver/certmagic.(*ACMEIssuer).Issue
github.com/caddyserver/certmagic@v0.25.2/acmeissuer.go:391
github.com/caddyserver/caddy/v2/modules/caddytls.(*ACMEIssuer).Issue
github.com/caddyserver/caddy/v2@v2.11.2/modules/caddytls/acmeissuer.go:292
github.com/caddyserver/certmagic.(*Config).obtainCert.func2
github.com/caddyserver/certmagic@v0.25.2/config.go:662
github.com/caddyserver/certmagic.doWithRetry
github.com/caddyserver/certmagic@v0.25.2/async.go:104
github.com/caddyserver/certmagic.(*Config).obtainCert
github.com/caddyserver/certmagic@v0.25.2/config.go:736
github.com/caddyserver/certmagic.(*Config).ObtainCertAsync
github.com/caddyserver/certmagic@v0.25.2/config.go:532
github.com/caddyserver/certmagic.(*Config).manageOne.func1
github.com/caddyserver/certmagic@v0.25.2/config.go:415
github.com/caddyserver/certmagic.(*jobManager).worker
github.com/caddyserver/certmagic@v0.25.2/async.go:73
2026/03/10 17:18:15.326 INFO authorization finalized {"identifier": "ksa.stamhoofd.dev", "authz_status": "valid"}
2026/03/10 17:18:15.326 INFO validations succeeded; finalizing order {"order": "https://acme-v02.api.letsencrypt.org/acme/order/3133281997/489162114987"}
2026/03/10 17:18:17.000 INFO got renewal info {"names": ["ksa.stamhoofd.dev"], "window_start": "2026/05/08 19:03:49.000", "window_end": "2026/05/10 14:14:39.000", "selected_time": "2026/05/09 20:53:51.000", "recheck_after": "2026/03/10 23:12:41.000", "explanation_url": ""}
3. Caddy version:
v2.11.2 h1:iOlpsSiSKqEW+SIXrcZsZ/NO74SzB/ycqqvAIEfIm64=
Modules:
github.com/caddy-dns/digitaloceangithub.com/mholt/caddy-l4
4. How I installed and ran Caddy:
Manual build in combination with systemd file.
GOOS=linux GOARCH=amd64 xcaddy build v2.11.2 --output caddy --with github.com/caddy-dns/digitalocean --with github.com/mholt/caddy-l4
Creating users and log directory
sudo groupadd --system caddy
sudo useradd --system --gid caddy --create-home --home-dir /var/lib/caddy --shell /usr/sbin/nologin --comment "Caddy web server" caddy
sudo mkdir -p /var/log/caddy
sudo chown caddy:caddy /var/log/caddy
Systemd config file attached below
sudo systemctl enable caddy
a. System environment:
Ubuntu 24.04.4 LTS, amd64 (no docker)
b. Command:
sudo systemctl restart caddy
c. Service/unit/compose file:
# caddy-api.service
#
# For using Caddy with its API.
#
# This unit is "durable" in that it will automatically resume
# the last active configuration if the service is restarted.
#
# See https://caddyserver.com/docs/install for instructions.
[Unit]
Description=Caddy
Documentation=https://caddyserver.com/docs/
After=network.target network-online.target
Requires=network-online.target
[Service]
Type=notify
User=caddy
Group=caddy
ExecStart=/usr/bin/caddy run --environ --resume
TimeoutStopSec=5s
LimitNOFILE=1048576
PrivateTmp=true
ProtectSystem=full
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
[Install]
WantedBy=multi-user.target
d. My complete Caddy config:
We don’t use Caddyfiles, but json only configs.
{
"apps": {
"http": {
"servers": {
"redirect": {
"listen": [
":80"
],
"routes": [
{
"handle": [
{
"handler": "static_response",
"headers": {
"Location": [
"https://{http.request.hostport}{http.request.uri}"
]
},
"status_code": "301"
}
]
}
]
},
"stamhoofd": {
"listen": [
":443"
],
"routes": [
{
"handle": [
{
"encodings": {
"gzip": {
"level": 6
},
"zstd": {}
},
"handler": "encode"
},
{
"handler": "reverse_proxy",
"headers": {
"request": {
"set": {
"x-real-ip": [
"{http.request.remote}"
]
}
}
},
"upstreams": [
{
"dial": "127.0.0.1:9091"
}
]
}
],
"match": [
{
"host": [
"api.staging.keeo.fos.be",
"*.api.staging.keeo.fos.be"
]
}
]
},
{
"handle": [
{
"encodings": {
"gzip": {
"level": 6
},
"zstd": {}
},
"handler": "encode"
},
{
"handler": "reverse_proxy",
"headers": {
"request": {
"set": {
"x-real-ip": [
"{http.request.remote}"
]
}
}
},
"upstreams": [
{
"dial": "127.0.0.1:5000"
}
]
}
],
"match": [
{
"host": [
"renderer.staging.keeo.fos.be"
]
}
]
},
{
"handle": [
{
"handler": "static_response",
"headers": {
"Cache-Control": [
"no-store"
],
"Location": [
"https://staging.keeo.fos.be{http.request.uri}"
]
},
"status_code": "302"
}
],
"match": [
{
"host": [
"ksa.stamhoofd.dev"
]
}
],
"terminal": true
},
{
"handle": [
{
"encodings": {
"gzip": {
"level": 6
},
"zstd": {}
},
"handler": "encode"
},
{
"handler": "subroute",
"routes": [
{
"handle": [
{
"handler": "headers",
"response": {
"set": {
"Cache-Control": [
"no-store"
]
}
}
}
],
"match": [
{
"not": [
{
"path": [
"*.js",
"*.css",
"*.png",
"*.jpg",
"*.jpeg",
"*.gif",
"*.ico",
"*.webm",
"*.mp4",
"*.webp",
"*.avif",
"*.svg",
"*.ttf",
"*.woff",
"*.woff2",
"*.map",
"*.pdf",
"*.doc",
"*.docx",
"*.xls",
"*.xlsx",
"*.ppt",
"*.pptx",
"*.zip",
"*.rar",
"*.7z",
"*.gz",
"*.tar",
"*.mp3",
"*.m4a",
"*.avi",
"*.mkv",
"*.mov",
"*.wmv"
]
}
]
}
],
"terminal": false
},
{
"handle": [
{
"handler": "headers",
"response": {
"set": {
"Cache-Control": [
"max-age=31536000"
]
}
}
}
],
"match": [
{
"path": [
"*.js",
"*.css",
"*.png",
"*.jpg",
"*.jpeg",
"*.gif",
"*.ico",
"*.webm",
"*.mp4",
"*.webp",
"*.avif",
"*.svg",
"*.ttf",
"*.woff",
"*.woff2",
"*.map",
"*.pdf",
"*.doc",
"*.docx",
"*.xls",
"*.xlsx",
"*.ppt",
"*.pptx",
"*.zip",
"*.rar",
"*.7z",
"*.gz",
"*.tar",
"*.mp3",
"*.m4a",
"*.avi",
"*.mkv",
"*.mov",
"*.wmv"
]
}
],
"terminal": false
}
]
},
{
"handler": "file_server",
"pass_thru": true,
"root": "/var/www/stamhoofd/dashboard/"
},
{
"handler": "rewrite",
"uri": "/index.html"
},
{
"handler": "file_server",
"root": "/var/www/stamhoofd/dashboard/"
}
],
"match": [
{
"host": [
"staging.keeo.fos.be"
]
}
]
},
{
"handle": [
{
"handler": "static_response",
"headers": {
"Cache-Control": [
"no-store"
],
"Location": [
"https://staging.keeo.fos.be{http.request.uri}"
]
},
"status_code": "302"
}
],
"match": [
{
"host": [
"www.staging.keeo.fos.be"
]
}
]
},
{
"handle": [
{
"encodings": {
"gzip": {
"level": 6
},
"zstd": {}
},
"handler": "encode"
},
{
"handler": "subroute",
"routes": [
{
"handle": [
{
"handler": "headers",
"response": {
"set": {
"Cache-Control": [
"no-store"
]
}
}
}
],
"match": [
{
"not": [
{
"path": [
"*.js",
"*.css",
"*.png",
"*.jpg",
"*.jpeg",
"*.gif",
"*.ico",
"*.webm",
"*.mp4",
"*.webp",
"*.avif",
"*.svg",
"*.ttf",
"*.woff",
"*.woff2",
"*.map",
"*.pdf",
"*.doc",
"*.docx",
"*.xls",
"*.xlsx",
"*.ppt",
"*.pptx",
"*.zip",
"*.rar",
"*.7z",
"*.gz",
"*.tar",
"*.mp3",
"*.m4a",
"*.avi",
"*.mkv",
"*.mov",
"*.wmv"
]
}
]
}
],
"terminal": false
},
{
"handle": [
{
"handler": "headers",
"response": {
"set": {
"Cache-Control": [
"max-age=31536000"
]
}
}
}
],
"match": [
{
"path": [
"*.js",
"*.css",
"*.png",
"*.jpg",
"*.jpeg",
"*.gif",
"*.ico",
"*.webm",
"*.mp4",
"*.webp",
"*.avif",
"*.svg",
"*.ttf",
"*.woff",
"*.woff2",
"*.map",
"*.pdf",
"*.doc",
"*.docx",
"*.xls",
"*.xlsx",
"*.ppt",
"*.pptx",
"*.zip",
"*.rar",
"*.7z",
"*.gz",
"*.tar",
"*.mp3",
"*.m4a",
"*.avi",
"*.mkv",
"*.mov",
"*.wmv"
]
}
],
"terminal": false
}
]
},
{
"handler": "file_server",
"pass_thru": true,
"root": "/var/www/stamhoofd/webshop/"
},
{
"handler": "rewrite",
"uri": "/index.html"
},
{
"handler": "file_server",
"root": "/var/www/stamhoofd/webshop/"
}
],
"match": [
{
"host": [
"shop.staging.keeo.fos.be"
]
}
]
},
{
"handle": [
{
"encodings": {
"gzip": {
"level": 6
},
"zstd": {}
},
"handler": "encode"
},
{
"handler": "subroute",
"routes": [
{
"handle": [
{
"handler": "headers",
"response": {
"set": {
"Cache-Control": [
"no-store"
]
}
}
}
],
"match": [
{
"not": [
{
"path": [
"*.js",
"*.css",
"*.png",
"*.jpg",
"*.jpeg",
"*.gif",
"*.ico",
"*.webm",
"*.mp4",
"*.webp",
"*.avif",
"*.svg",
"*.ttf",
"*.woff",
"*.woff2",
"*.map",
"*.pdf",
"*.doc",
"*.docx",
"*.xls",
"*.xlsx",
"*.ppt",
"*.pptx",
"*.zip",
"*.rar",
"*.7z",
"*.gz",
"*.tar",
"*.mp3",
"*.m4a",
"*.avi",
"*.mkv",
"*.mov",
"*.wmv"
]
}
]
}
],
"terminal": false
},
{
"handle": [
{
"handler": "headers",
"response": {
"set": {
"Cache-Control": [
"max-age=31536000"
]
}
}
}
],
"match": [
{
"path": [
"*.js",
"*.css",
"*.png",
"*.jpg",
"*.jpeg",
"*.gif",
"*.ico",
"*.webm",
"*.mp4",
"*.webp",
"*.avif",
"*.svg",
"*.ttf",
"*.woff",
"*.woff2",
"*.map",
"*.pdf",
"*.doc",
"*.docx",
"*.xls",
"*.xlsx",
"*.ppt",
"*.pptx",
"*.zip",
"*.rar",
"*.7z",
"*.gz",
"*.tar",
"*.mp3",
"*.m4a",
"*.avi",
"*.mkv",
"*.mov",
"*.wmv"
]
}
],
"terminal": false
}
]
},
{
"handler": "file_server",
"pass_thru": true,
"root": "/var/www/stamhoofd/webshop/"
},
{
"handler": "rewrite",
"uri": "/index.html"
},
{
"handler": "file_server",
"root": "/var/www/stamhoofd/webshop/"
}
]
}
]
}
}
},
"tls": {
"automation": {
"on_demand": {
"permission": {
"endpoint": "https://api.staging.keeo.fos.be/v394/check-domain-cert",
"module": "http"
}
},
"policies": [
{
"issuers": [
{
"challenges": {
"dns": {
"propagation_delay": "1m",
"propagation_timeout": "10m",
"provider": {
"auth_token": "xxxxxxxxxxxx",
"name": "digitalocean"
}
}
},
"module": "acme"
}
],
"on_demand": false,
"subjects": [
"api.staging.keeo.fos.be",
"*.api.staging.keeo.fos.be"
]
},
{
"issuers": [
{
"challenges": {
"dns": {
"propagation_delay": "1m",
"propagation_timeout": "10m",
"provider": {
"auth_token": "xxxxxxxxxxxx",
"name": "digitalocean"
}
}
},
"module": "acme"
}
],
"on_demand": false,
"subjects": [
"renderer.staging.keeo.fos.be"
]
},
{
"issuers": [
{
"challenges": {
"dns": {
"propagation_delay": "1m",
"propagation_timeout": "10m",
"provider": {
"auth_token": "xxxxxxxxxxxx2",
"name": "digitalocean"
}
}
},
"module": "acme"
}
],
"on_demand": false,
"subjects": [
"ksa.stamhoofd.dev"
]
},
{
"issuers": [
{
"challenges": {
"dns": {
"propagation_delay": "1m",
"propagation_timeout": "10m",
"provider": {
"auth_token": "xxxxxxxxxxxx",
"name": "digitalocean"
}
}
},
"module": "acme"
}
],
"on_demand": false,
"subjects": [
"www.staging.keeo.fos.be",
"staging.keeo.fos.be"
]
},
{
"issuers": [
{
"challenges": {
"dns": {
"propagation_delay": "1m",
"propagation_timeout": "10m",
"provider": {
"auth_token": "xxxxxxxxxxxx",
"name": "digitalocean"
}
}
},
"module": "acme"
}
],
"on_demand": false,
"subjects": [
"shop.staging.keeo.fos.be"
]
},
{
"on_demand": true
}
]
}
}
},
"logging": {
"logs": {
"default": {
"encoder": {
"format": "console"
},
"writer": {
"filename": "/var/log/caddy/caddy.log",
"output": "file",
"roll_size_mb": 50
}
}
},
"sink": {
"writer": {
"filename": "/var/log/caddy/caddy-sink.log",
"output": "file",
"roll_size_mb": 50
}
}
}
}