Skip to main content

ACME / Let's Encrypt Integration

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.read as well as records.read and records.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_xxx or set the NEXDNS_TOKEN environment variable.

We use cookies to ensure the proper functioning of this website and to improve your experience. Some cookies are strictly necessary for the site to operate, while others are optional.

You can accept all cookies or limit your choice to strictly necessary ones. For details, see our Privacy Policy and Cookie Policy.