Overview
The ACME protocol (used by Let's Encrypt and other certificate authorities) supports the DNS-01 challenge for domain validation. DNS-01 is the only challenge type that supports wildcard certificates (*.example.com), and it does not require an HTTP server on the target machine.
NexDNS provides native integrations with popular ACME clients, as well as a generic CLI hook for any client that supports manual DNS hooks.
All ACME integrations require an API token, which is available on Pro plans and above. Every client looks the zone up before writing the challenge, so the token needs
zones.readas well asrecords.readandrecords.write. Create one at nexdns.tech/settings/api-keys.
How a wildcard is validated
The challenge is a TXT record at _acme-challenge under the name being validated. A certificate covering both example.com and *.example.com produces two different challenge values at the same name, and both have to be present at once - every integration below handles that, which is why you should never delete the record set between the two validations.
Allow for propagation
A challenge record needs roughly 30 seconds to reach the nameservers, so a 30-second wait leaves no margin at all - we have watched one fail with NXDOMAIN. Give the certificate authority at least 60 seconds: the certbot plugin and lego both default to 60, and for acme.sh and the CLI hook the wait is yours to set.
acme.sh
acme.sh is a pure shell ACME client. Our DNS hook is not part of an acme.sh release yet, so put it in ~/.acme.sh/dnsapi/ before the first run: curl -fsSL https://get.nexdns.tech/acme/dns_nexdns.sh -o ~/.acme.sh/dnsapi/dns_nexdns.sh. The same file is in the CLI repository if you would rather read it first.
Issue a Certificate
export NEXDNS_Token="nxd_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
acme.sh --issue --server letsencrypt --dns dns_nexdns \
--dnssleep 60 \
-d example.com -d '*.example.com'
The token is saved to ~/.acme.sh/account.conf after the first run, so you do not need to export it again for renewals.
Renew
Renewals happen automatically via cron. To force a manual renewal:
acme.sh --renew -d example.com
lego / Traefik
lego is a Go-based ACME client that also powers Traefik’s automatic certificate management. The provider ships with lego v5.4.0 and later.
Standalone Usage
NEXDNS_API_TOKEN=nxd_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx \
NEXDNS_PROPAGATION_TIMEOUT=300 \
lego --dns nexdns \
--domains example.com \
--domains '*.example.com' \
--email admin@example.com \
--accept-tos \
run
lego reads its timeouts as a plain number of seconds: NEXDNS_PROPAGATION_TIMEOUT=300 works, while 300s fails to parse and is silently replaced by the 60-second default.
Traefik Configuration
Traefik embeds lego’s provider table at build time and still vendors an older release, so this needs a Traefik built against lego v5.4.0. Add the NexDNS DNS challenge resolver to your Traefik configuration - the following docker-compose.yml shows a typical setup:
services:
traefik:
image: traefik:v3 # lego >= v5.4.0
command:
- "--certificatesresolvers.nexdns.acme.dnschallenge=true"
- "--certificatesresolvers.nexdns.acme.dnschallenge.provider=nexdns"
- "--certificatesresolvers.nexdns.acme.email=admin@example.com"
- "--certificatesresolvers.nexdns.acme.storage=/letsencrypt/acme.json"
environment:
NEXDNS_API_TOKEN: "nxd_xxxxxxxxxxxxxxxxxxxx"
volumes:
- letsencrypt:/letsencrypt
- /var/run/docker.sock:/var/run/docker.sock:ro
ports:
- "443:443"
volumes:
letsencrypt:
Then use the resolver in your service labels:
labels:
- "traefik.http.routers.myapp.tls.certresolver=nexdns"
- "traefik.http.routers.myapp.tls.domains[0].main=example.com"
- "traefik.http.routers.myapp.tls.domains[0].sans=*.example.com"
certbot
certbot is the official Let's Encrypt client. Use the NexDNS DNS authenticator plugin for automated DNS-01 validation.
Install the Plugin
Install the plugin from PyPI, or use the Docker image below, which already bundles it.
pip install certbot-dns-nexdns
Create Credentials File
Create ~/.nexdns/certbot-credentials.ini with your API token:
dns_nexdns_token = nxd_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Restrict file permissions:
chmod 600 ~/.nexdns/certbot-credentials.ini
Issue a Certificate
certbot certonly \
--authenticator dns-nexdns \
--dns-nexdns-credentials ~/.nexdns/certbot-credentials.ini \
-d example.com \
-d '*.example.com'
Docker Usage
docker run --rm \
-v /etc/letsencrypt:/etc/letsencrypt \
-v ~/.nexdns/certbot-credentials.ini:/credentials.ini:ro \
nexdns/certbot certonly \
--non-interactive --agree-tos --email admin@example.com \
--authenticator dns-nexdns \
--dns-nexdns-credentials /credentials.ini \
-d example.com \
-d '*.example.com'
CLI Hook
If your ACME client supports manual DNS hooks, you can use the NexDNS CLI as a hook script. This works with any client that provides --manual-auth-hook and --manual-cleanup-hook options (e.g. certbot in manual mode).
certbot with CLI Hook
certbot certonly --manual --preferred-challenges dns \
--manual-auth-hook "nexdns acme hook --action create && sleep 60" \
--manual-cleanup-hook "nexdns acme hook --action delete" \
-d example.com \
-d '*.example.com'
The hook reads the CERTBOT_DOMAIN and CERTBOT_VALIDATION environment variables set by certbot and creates or removes the _acme-challenge TXT record automatically.
The CLI must be authenticated before using hooks. Run
nexdns auth token nxd_xxxor set theNEXDNS_TOKENenvironment variable.