Let's Encrypt wildcard certificates with Gyro DNS: acme.sh, Certbot, Caddy, Traefik and Kubernetes
1. Why DNS validation, and why a separate key?
Let's Encrypt checks that you control a domain before it issues a certificate. A wildcard certificate (*.example.com) can only be obtained with the DNS-01 method: your certificate tool writes a temporary TXT record at _acme-challenge.example.com, Let's Encrypt reads it, and the record is removed afterwards. DNS validation has other advantages too: the server does not need port 80 open to the internet, and internal or not-yet-public servers can get certificates.
To write that record the tool needs access to your DNS. Putting a full API key on every server is risky, so Gyro DNS has an ACME key for exactly this job:
| An ACME key can | An ACME key cannot |
|---|---|
Add and remove TXT values at names starting with _acme-challenge (TTL 60 s) |
Read your zones, or change or delete any other record (A, MX, CNAME ...) |
| Work only in the zones you select (optional) | Reach your other services, invoices or account data |
| Be used only from IP addresses you list (optional) | Create new keys |
Even if a key leaks, the worst case is a validation record; delete the key in the panel. We recommend one key per server or cluster.
ACME keys do not work on the read-only zones copied from your hosting (cPanel) account; those domains use cPanel's own free SSL (AutoSSL). ACME keys are for the zones you add to Gyro DNS.
2. Create an ACME key
- In the client area open My Services → Gyro DNS and go to the Management tab.
- In the left menu choose Advanced → ACME / Let's Encrypt.
- Enter a Name you will recognise (for example
web-01ork8s-prod). - Select the Zones the key may use. If you select none, the key works in every zone of this service.
- Optionally enter the IP address of the server that will request certificates in Allowed IPs (comma separated).
- Click Create key. The key (
wck_...) is shown only once: copy it and store it safely.
The same from the command line or Terraform:
gyro dns acme-key create <service-id> --name web-01 --zones example.com
gyro dns acme-key list <service-id>
resource "gyro_dns_acme_key" "web" {
service_id = 12345
name = "web-01"
zones = ["example.com"]
}
To revoke a key click the bin icon in the table or run gyro dns acme-key delete <service-id> <key-id>; it stops working at once.
3. acme.sh
With acme.sh installed, download the Gyro DNS hook and pass the key:
curl -fsSL -o ~/.acme.sh/dnsapi/dns_gyro.sh https://raw.githubusercontent.com/Gyro-Hosting/gyro-acme/main/acme.sh/dns_gyro.sh
export GYRO_Token="wck_..."
acme.sh --issue --dns dns_gyro -d example.com -d '*.example.com'
After adding the record the hook waits until both of our name servers publish it (usually under a minute). The key is saved in the acme.sh configuration, and renewals run with acme.sh's own scheduler. Use acme.sh --install-cert to copy the certificate to your web server.
4. Certbot
Install the plugin into the Python environment of Certbot:
pip install "certbot-dns-gyro @ git+https://github.com/Gyro-Hosting/[email protected]#subdirectory=certbot-dns-gyro"
Store the key in a file only root can read:
printf 'dns_gyro_token = wck_...\n' > /etc/letsencrypt/gyro.ini
chmod 600 /etc/letsencrypt/gyro.ini
certbot certonly --authenticator dns-gyro --dns-gyro-credentials /etc/letsencrypt/gyro.ini -d example.com -d '*.example.com'
The snap package of Certbot cannot load this plugin; use a pip-installed Certbot or acme.sh.
5. Caddy
Build Caddy with the Gyro DNS module and use dns gyro in your Caddyfile:
xcaddy build --with github.com/Gyro-Hosting/caddy-dns-gyro
*.example.com, example.com {
tls {
dns gyro {env.GYRO_TOKEN}
}
reverse_proxy 127.0.0.1:8080
}
Put your ACME key in the GYRO_TOKEN environment variable. Caddy obtains, renews and replaces the certificate before it expires.
6. Traefik and lego
Traefik runs the small gyro-acme-exec program through its exec DNS provider. Download it from the libdns-gyro releases and mount it into the container:
# traefik.yml
certificatesResolvers:
le:
acme:
email: [email protected]
storage: /letsencrypt/acme.json
dnsChallenge:
provider: exec
Environment of the Traefik container:
EXEC_PATH=/usr/local/bin/gyro-acme-exec
GYRO_TOKEN=wck_...
EXEC_PROPAGATION_TIMEOUT=300
EXEC_POLLING_INTERVAL=10
On the router set tls.certresolver=le, tls.domains[0].main=example.com and tls.domains[0].sans=*.example.com. The same program works with the lego CLI: EXEC_PATH=./gyro-acme-exec GYRO_TOKEN=wck_... lego --dns exec -d example.com -d '*.example.com' --email [email protected] run.
7. Kubernetes (cert-manager)
With cert-manager installed, add the Gyro DNS webhook with Helm, store the key as a Secret and define a ClusterIssuer:
helm install cert-manager-webhook-gyro oci://ghcr.io/gyro-hosting/charts/cert-manager-webhook-gyro --namespace cert-manager
kubectl -n cert-manager create secret generic gyro-acme --from-literal=token='wck_...'
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-gyro
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: [email protected]
privateKeySecretRef:
name: letsencrypt-gyro-account
solvers:
- dns01:
webhook:
groupName: acme.gyrohosting.com
solverName: gyro
config:
tokenSecretRef:
name: gyro-acme
key: token
Then use dnsNames: ["example.com", "*.example.com"] and issuerRef: {name: letsencrypt-gyro, kind: ClusterIssuer} in a Certificate. The webhook can read Secrets only in the namespaces you allow in Helm and runs as a non-root user.
8. Any other tool: the API
If your tool is not listed, three simple calls are enough. The service and the zone are found from the record name; only the ACME key is needed:
API=https://www.gyrohosting.com/api/v1/client
# add
curl -X POST $API/cloud/dns/acme-challenge -H "Authorization: Bearer $GYRO_TOKEN" \
-H "Content-Type: application/json" -d '{"name":"_acme-challenge.example.com","value":"<VALUE>"}'
# ready? ("ready": true once both name servers publish it)
curl "$API/cloud/dns/acme-challenge?name=_acme-challenge.example.com&value=<VALUE>" -H "Authorization: Bearer $GYRO_TOKEN"
# remove
curl -X DELETE "$API/cloud/dns/acme-challenge?name=_acme-challenge.example.com&value=<VALUE>" -H "Authorization: Bearer $GYRO_TOKEN"
Adding keeps the other values at the same name, so example.com and *.example.com can be validated together. CLI equivalents: gyro dns acme-challenge add|status|remove. Full reference: developer documentation.
9. Frequently asked questions
Which names should I request for a wildcard certificate?
Request both example.com and *.example.com. A wildcard covers one level only: it is valid for www.example.com and api.example.com, not for example.com or a.b.example.com.
How long does validation take?
Both of our name servers usually publish the record in under a minute after it is written. The plugins wait for this themselves; the whole process mostly finishes in 1-2 minutes.
What if my ACME key is stolen?
It can only write _acme-challenge TXT records; it cannot touch your website or e-mail records or your account. Still, delete it in the panel and create a new one. A key limited to IP addresses cannot be used from anywhere else.
I get "zone_not_allowed" or "zone_not_found". The domain is not a live zone of this service (it is pending, in another service, or a hosting zone), or the key is limited to other zones. Check the key's zone list in the panel.
Are certificates renewed before they expire?
Yes. acme.sh, Certbot, Caddy, Traefik and cert-manager renew in the last weeks of validity automatically, with the same ACME key.
Does it work with certificate authorities other than Let's Encrypt?
Yes. DNS-01 is a standard; the same key works with any ACME CA such as ZeroSSL, Google Trust Services or your own ACME server.
You can create ACME keys with Terraform and pass them straight into a Kubernetes Secret: the token of gyro_dns_acme_key is read only at creation and kept as a sensitive value in state.
Next step
- Plugin source code and releases: github.com/Gyro-Hosting/gyro-acme
- Plans: Gyro DNS
Thanks for your feedback!