Integrations
How each ACME client issues a certificate for a DNSMint hostname over DNS-01, which is what a wildcard or a machine on a private address needs. A hostname on a public address that wants no wildcard needs none of this: point the client at the hostname and it validates over HTTP-01, as in the quickstart.
Caddy
Our Caddy module takes an API key carrying dns01:write and nothing else. No credential to mint, and no file for Caddy to read. Compile it in with xcaddy:
$ xcaddy build --with github.com/dnsmint/caddy-dnsmint*.q7k4m2.dnsmint-a3f9c1.dev, q7k4m2.dnsmint-a3f9c1.dev {
tls {
dns dnsmint {env.DNSMINT_KEY}
}
reverse_proxy localhost:3000
}An API key is the whole configuration. To use it for every site rather than one, put acme_dns dnsmint {env.DNSMINT_KEY} in the global options block instead.
acme.sh
The dns_dnsmint plugin takes the API key and nothing else. It ships with acme.sh from the release noted below; until then, drop dns_dnsmint.sh into ~/.acme.sh/dnsapi/.
$ export DNSMINT_API_KEY=$DNSMINT_KEY
acme.sh --issue --dns dns_dnsmint \
-d q7k4m2.dnsmint-a3f9c1.dev -d '*.q7k4m2.dnsmint-a3f9c1.dev'acme.sh saves the key after the first run, so renewals need no environment at all.
lego
lego's built-in httpreq provider posts the challenge to an endpoint we serve, authenticated with your API key as the basic-auth password. No credential to mint and nothing to pre-seed: the endpoint, a username of any value, and the key carrying dns01:write for the hostname as the password. The username has to be set even though we ignore it: lego sends basic auth only when both variables are.
$ export HTTPREQ_ENDPOINT=https://dnsmint.com/api/httpreq
export HTTPREQ_USERNAME=dnsmint # any value; lego sends auth only when both are set
export HTTPREQ_PASSWORD=$DNSMINT_KEY
lego run --email you@example.com --dns httpreq \
--accept-tos -d q7k4m2.dnsmint-a3f9c1.dev -d '*.q7k4m2.dnsmint-a3f9c1.dev'The acme-dns route still works if you prefer it; it needs the registration file written to lego's storage before the first run, as described in the API reference.
Traefik
Traefik embeds lego, so the same three environment variables apply. The resolver names the httpreq provider; a router then selects it with tls.certresolver=dnsmint and lists the hostname and its wildcard as its domains.
# traefik.yml (static configuration)
certificatesResolvers:
dnsmint:
acme:
email: you@example.com
storage: /letsencrypt/acme.json
dnsChallenge:
provider: httpreqHTTPREQ_ENDPOINT=https://dnsmint.com/api/httpreq HTTPREQ_USERNAME=dnsmint HTTPREQ_PASSWORD=<your DNSMint API key>
certbot
certbot needs no plugin. It hands the challenge to a hook script of yours, which is all our endpoints want. Write two, identical but for the last path segment, and make them executable.
#!/bin/sh
# /etc/letsencrypt/dnsmint-auth.sh (chmod +x)
# Swap present for cleanup in the second copy.
exec curl -fsS -X POST https://dnsmint.com/api/httpreq/present \
-H "Authorization: Bearer $DNSMINT_KEY" \
-H "Content-Type: application/json" \
-d "{\"fqdn\":\"_acme-challenge.$CERTBOT_DOMAIN.\",\"value\":\"$CERTBOT_VALIDATION\"}"Put the key carrying dns01:write in DNSMINT_KEY in the environment certbot runs under, then:
$ certbot certonly --manual --preferred-challenges dns \
--manual-auth-hook /etc/letsencrypt/dnsmint-auth.sh \
--manual-cleanup-hook /etc/letsencrypt/dnsmint-cleanup.sh \
-d q7k4m2.dnsmint-a3f9c1.dev -d '*.q7k4m2.dnsmint-a3f9c1.dev'CERTBOT_DOMAIN is never the wildcard form, so the hostname and its wildcard produce one challenge name and two values, which is what DNS-01 wants. This works the same whether certbot came from pip, a package manager, or snap.
Posh-ACME
The PowerShell client, and the way to do this on Windows Server. The DNSMint plugin takes the API key as a SecureString and nothing else. It ships with Posh-ACME from the release noted below; until then, save DNSMint.ps1 to a folder of your own and point POSHACME_PLUGINS at it.
$env:POSHACME_PLUGINS = 'C:\posh-acme-plugins'
$pArgs = @{ DNSMintToken = (Read-Host 'API Key' -AsSecureString) }
New-PACertificate q7k4m2.dnsmint-a3f9c1.dev,*.q7k4m2.dnsmint-a3f9c1.dev `
-Plugin DNSMint,DNSMint -PluginArgs $pArgsPosh-ACME keeps the key with the order, encrypted, so Submit-Renewal takes no arguments. Windows PowerShell 5.1 is enough; nothing here needs PowerShell 7.
cert-manager
Our webhook solver runs in your cluster and reads one API key from a Secret. Install it alongside cert-manager, which issues the webhook its own serving certificate:
$ helm install cert-manager-webhook-dnsmint \
oci://ghcr.io/dnsmint/charts/cert-manager-webhook-dnsmint \
--namespace cert-managerPut the key beside the Issuer, reference it from a solver, and request a Certificate for the hostname and its wildcard.
$ kubectl create secret generic dnsmint-api-key \
--namespace cert-manager --from-literal=api-key="$DNSMINT_KEY"apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: dnsmint
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: you@example.com
privateKeySecretRef:
name: dnsmint-issuer-key
solvers:
- dns01:
webhook:
groupName: acme.dnsmint.com
solverName: dnsmint
config:
apiKeySecretRef:
name: dnsmint-api-key
key: api-keyapiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: q7k4m2
spec:
secretName: q7k4m2-tls
issuerRef:
name: dnsmint
dnsNames:
- q7k4m2.dnsmint-a3f9c1.dev
- "*.q7k4m2.dnsmint-a3f9c1.dev"The acme-dns route
No recipe above needs it any more, but the DNS-01 API also speaks acme-dns, for a client that expects it. Mint a credential with a key carrying dns01:write; the password appears in this response and never again.
$ curl -X POST https://dnsmint.com/api/v1/hostnames/HOST_ID/acme-credential \
-H "Authorization: Bearer $DNSMINT_KEY"{
"username": "2f1e6a9c-8b3d-4e5f-9a1b-6c7d8e9f0a1b",
"password": "f3a9...",
"fulldomain": "_acme-challenge.q7k4m2.dnsmint-a3f9c1.dev",
"subdomain": "2f1e6a9c-8b3d-4e5f-9a1b-6c7d8e9f0a1b",
"server_url": "https://dnsmint.com/api/acme",
"allowfrom": []
}Clients store it keyed by the hostname, without any wildcard prefix; one entry covers the hostname and *.hostname.
{
"q7k4m2.dnsmint-a3f9c1.dev": {
"username": "2f1e6a9c-8b3d-4e5f-9a1b-6c7d8e9f0a1b",
"password": "f3a9...",
"fulldomain": "_acme-challenge.q7k4m2.dnsmint-a3f9c1.dev",
"subdomain": "2f1e6a9c-8b3d-4e5f-9a1b-6c7d8e9f0a1b",
"server_url": "https://dnsmint.com/api/acme"
}
}lego and Traefik auto-register against acme-dns when their storage is empty, which we do not support, so use httpreq there rather than this. The protocol is documented in the API reference.
Kubernetes
Everything above is about certificates. For the other half of a cluster's DNS, a Service or Ingress that should have a hostname at all, there is an external-dns provider. It runs as a sidecar beside external-dns, takes the same kind of API key, and mints a hostname for each annotated Service. Pair it with the cert-manager solver above and the cluster handles both the name and its certificate.
Terraform and OpenTofu
A hostname is worth most at the moment the machine is created, and machines are created in Terraform. The provider is on the registry, so terraform init fetches it by name. OpenTofu takes the same source.
terraform {
required_providers {
dnsmint = {
source = "dnsmint/dnsmint"
}
}
}
resource "dnsmint_hostname" "agent" {
ip = aws_instance.agent.public_ip
}Changing the address repoints in place; changing the subdomain or domain replaces, because those are what the name is. Worth knowing before your first plan: destroy releases the hostname, and a release is permanent. That is not how a DNS record behaves, so put prevent_destroy on anything you would mind losing.
MCP
An endpoint rather than a package: https://dnsmint.com/mcp. Everything else here assumes a person wrote the config first. This one does not: an agent asks for the hostname itself, mid-task, and can repoint it and read back why it is or is not resolving.
In Claude or ChatGPT, add the URL as a connector and sign in. Signing in is an OAuth flow: you choose what the connector may do, and can hold it to a single domain. Claude Code, Cursor and VS Code can send an API key instead:
$ claude mcp add --transport http dnsmint https://dnsmint.com/mcp \
--header "Authorization: Bearer $DNSMINT_KEY"On a server with no browser, use the device flow instead. The machine asks for a code, prints it, and polls; you open dnsmint.com/device on a phone or laptop, type the code, and approve it there. Nothing is decided on the machine, and no long-lived secret has to be copied onto it.
$ curl -s -X POST https://dnsmint.com/api/oauth/device_authorization \
-d client_id=$CLIENT_ID -d 'scope=hostnames:read hostnames:write'
{ "user_code": "WDJB-MJHT",
"verification_uri": "https://dnsmint.com/device",
"interval": 5, ... }The tools a connector is offered follow what it was granted, so a read-only connection is never shown minting or releasing at all. Releasing asks for the hostname spelled out, because it is permanent. Disconnect a connector from your API keys page and it stops on its next call.
If one of these does not work
Every recipe here is verified against the client's current release, and the modules are ours to fix. If one of them fails, or a client you use is missing, write to hello@dnsmint.com. Bugs in the packages can also go straight to their issue trackers on GitHub.