SSD Nodes Learn
Guides Matt ConnorBy Matt Connor

Certbot wildcard certificates with DNS-01

Issue a wildcard certificate with Certbot using the DNS challenge. How the TXT record proof works, which plugin to install, and how renewal stays automatic.

Why a wildcard certificate needs DNS-01

A wildcard certificate covers every first-level subdomain of a domain: *.example.com matches app.example.com, blog.example.com, and any other name one label deep. Let's Encrypt issues wildcard certificates only through the DNS-01 challenge, so Certbot has to prove control of the domain's DNS by publishing a TXT record at _acme-challenge.example.com. The HTTP-01 challenge cannot qualify, because serving a token file proves control of one hostname, the one the validation server fetched the file from. A wildcard is a claim about every possible name under the domain, and the only public record that speaks for the whole namespace is DNS itself.

That one requirement decides everything else on this page. To pass DNS-01 you must be able to create TXT records in the domain's zone, either by hand or through your DNS provider's API (application programming interface). The by-hand route works once and then fails at renewal, for a concrete reason shown below. The API route, through a Certbot DNS plugin, renews unattended, and it is the setup you should end with.

This is the wildcard chapter of our Certbot guides. Ordinary single-hostname certificates, the web server configuration and the port 80 rules are covered in Certbot with nginx on Ubuntu 24.04 and Certbot with Apache on Ubuntu 24.04.

How the _acme-challenge TXT record works

When Certbot requests *.example.com, Let's Encrypt answers with a random token. Certbot combines that token with your ACME (automatic certificate management environment) account key, hashes the result with SHA-256, and produces a short text value. That value must appear as a TXT record at _acme-challenge.example.com. Let's Encrypt then queries your domain's authoritative name servers from its own infrastructure. If the record it reads matches the value it expects, you have proved you control the zone, and control of the zone is accepted as control of every name under it.

Two details cause most failures:

  • Requesting example.com and *.example.com on the same certificate means two separate challenges, and both TXT records live at the same name, _acme-challenge.example.com. Both must exist at the same time. Adding the second record is correct; replacing the first with the second fails the first challenge.
  • Validation reads your authoritative servers, but provider control panels can take a minute or more to push a new record out to them. Check from the outside before you let validation run:
dig +short TXT _acme-challenge.example.com @1.1.1.1

When that prints the value Certbot asked for, validation can succeed. When it prints nothing, wait and run it again.

See it work once: manual mode

Manual mode makes you do the DNS edit yourself, which is the best way to understand the mechanism before you automate it:

sudo certbot certonly --manual --preferred-challenges dns -d example.com -d '*.example.com'

The quotes around the wildcard stop your shell from treating * as a filename pattern. Certbot pauses with instructions:

Please deploy a DNS TXT record under the name:
_acme-challenge.example.com.
with the following value:
Jx9mQ2wLr8vTn5cKp0aYdG3hB7fZs4eN1oiRuXqMk6E

Create that TXT record in your DNS provider's panel, confirm it is visible with the dig command above, and only then press Enter. Because this run asks for the bare domain and the wildcard, Certbot prompts twice; keep both records in place until issuance finishes. Success ends with the familiar lines:

Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/example.com/fullchain.pem

Why manual mode cannot renew itself

Every renewal is a fresh challenge with a fresh token, so the TXT value changes every time. The record you pasted today is useless in 60 days. The renewal timer runs Certbot unattended twice a day, and nobody is at the keyboard to paste the new value, so a manually issued certificate fails its renewal with this exact error:

Failed to renew certificate example.com with error: The manual plugin is not
working; there may be problems with your existing configuration.
The error was: PluginError('An authentication script must be provided with
--manual-auth-hook when using the manual plugin non-interactively.')

You can satisfy that requirement by writing --manual-auth-hook scripts that call your DNS provider's API, but at that point you are rebuilding a DNS plugin by hand. Use manual mode to learn the flow, or for a genuine one-off on a domain whose DNS you cannot automate yet, and set a reminder well before day 90, because Let's Encrypt no longer sends expiry emails. For everything else, use a plugin.

The plugin route: certbot-dns-cloudflare on Ubuntu 24.04

A DNS plugin holds an API credential for your DNS provider and performs the whole TXT record dance itself, at issuance and again at every renewal. Cloudflare is the worked example here because it is the provider plugin most people need, and it is packaged in Ubuntu.

Our Certbot guides recommend the apt packages on Ubuntu 24.04, and that stance holds for Cloudflare:

sudo apt update
sudo apt install certbot python3-certbot-dns-cloudflare

One honest note on versions. The 24.04 archive ships this plugin at version 2.0.0 next to Certbot 2.9.0; apt policy python3-certbot-dns-cloudflare shows yours. The mismatch is harmless, and scoped API tokens work, because the underlying python3-cloudflare library in 24.04 is 2.11.1, above the 2.3.1 the plugin needs for token support. On older Ubuntu releases that library was too old for tokens, which is where the warnings you may find online about the apt plugin forcing the Global API Key come from. On 24.04 they no longer apply.

In the Cloudflare dashboard create a scoped API token, not the Global API Key: My Profile, then API Tokens, then Create Token, with the single permission Zone / DNS / Edit, limited to the one zone you are issuing for. Put it in a file only root can read:

sudo mkdir -p /root/.secrets
sudo tee /root/.secrets/cloudflare.ini > /dev/null <<'EOF'
dns_cloudflare_api_token = paste_your_scoped_token_here
EOF
sudo chmod 600 /root/.secrets/cloudflare.ini

Certbot checks the mode and warns about Unsafe permissions on credentials configuration file if the file is readable by others. Now issue:

sudo certbot certonly \
  --dns-cloudflare \
  --dns-cloudflare-credentials /root/.secrets/cloudflare.ini \
  -d example.com -d '*.example.com'

The plugin creates the TXT records through the API, waits a short propagation delay, lets validation run, then deletes the records again. If your zone's name servers are slow to pick up changes, raise the wait with --dns-cloudflare-propagation-seconds 60. The certificate lands in /etc/letsencrypt/live/example.com/, and you point nginx or Apache at fullchain.pem and privkey.pem exactly as the base guides show, deploy hook included.

If your provider's plugin is not in apt

The 24.04 archive only packages plugins for a handful of providers, with Cloudflare, Route 53, DigitalOcean and the generic RFC 2136 interface among them. Run apt search certbot-dns to see the list. If your provider is missing, this is the one place where our apt-first advice bends: install Certbot and the plugin from snap instead, and remove the apt Certbot first so two renewal timers never fight over /etc/letsencrypt:

sudo apt remove certbot python3-certbot-dns-cloudflare
sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbot
sudo snap set certbot trust-plugin-with-root=ok
sudo snap install certbot-dns-yourprovider

A snap plugin only connects to the snap Certbot; it cannot extend the apt one, which is why the two installs must not coexist. And if your DNS host offers no API at all, your realistic options are moving the domain's DNS to a provider that has one, or running your own name server and pointing the rfc2136 plugin at it.

Renewal: prove it now, not in 60 days

Certbot records how each certificate was issued in /etc/letsencrypt/renewal/example.com.conf, including authenticator = dns-cloudflare and the credentials path, so the standard twice-daily timer renews it with no help from you. Rehearse the whole thing against the staging environment:

sudo certbot renew --dry-run

A pass means the credential works and validation completes end to end; the real renewal in 60 days follows the same path. Two follow-ups are worth doing today. First, a renewed certificate on disk changes nothing until the web server reloads it, so wire the deploy hook described in the nginx and Apache guides. Second, treat the credentials file with respect: anyone who can read it can edit your DNS zone, which is enough to redirect your mail or pass DNS-01 challenges of their own. Keep it at mode 600 under /root, scope the token to one zone, and rotate it if you ever suspect a leak.

When you do not need a wildcard

A wildcard is the right tool for many subdomains, or for subdomains you cannot predict. It is the wrong default for everything else.

  • One subdomain, or a handful of known ones: a normal SAN (subject alternative name) certificate is simpler. certbot --nginx -d example.com -d www.example.com -d app.example.com covers up to 100 names over plain HTTP-01, and no DNS API credential ever sits on the server.
  • A wildcard matches exactly one label. *.example.com does not cover the bare example.com, which is why the commands above request both, and it does not cover a.b.example.com either; that would need *.b.example.com.
  • One private key stands behind every subdomain. If the machine holding it is compromised, every name the wildcard covers is affected at once.
  • If Traefik terminates TLS (transport layer security) for your containers, you do not need Certbot in the picture at all: Traefik requests wildcard certificates itself over DNS-01, using the same kind of provider token.

Where the wildcard genuinely earns its keep: per-customer or per-app subdomains created faster than you want to reissue certificates, and internal hosts with no public port 80, such as services reachable only across a WireGuard VPN. DNS-01 never connects to the host being certified, so even a fully private machine can hold a publicly trusted certificate.

FAQ

Can Certbot issue a wildcard certificate with HTTP-01?

No. HTTP-01 proves control of one hostname, because the validation server fetches a token file from that exact name. A wildcard covers every name under the domain, so Let's Encrypt requires the DNS-01 challenge for it, and the --nginx, --apache, --webroot and --standalone authenticators are all HTTP-based. The only route is a TXT record at _acme-challenge.example.com, placed manually or by a DNS plugin.

Does a wildcard certificate cover the root domain?

No. The wildcard matches exactly one label, so *.example.com covers www.example.com but not the bare example.com, and not a.b.example.com. Request both names on one certificate with -d example.com -d '*.example.com'. That creates two challenges, and both TXT records sit at the same _acme-challenge.example.com name, so add the second record without deleting the first.

Why does my wildcard certificate not renew automatically?

Because it was issued with --manual. Each renewal needs a brand-new TXT value, and the unattended timer has no way to paste it, so renewal stops with the error An authentication script must be provided with --manual-auth-hook when using the manual plugin non-interactively. Reissue the certificate with a DNS plugin such as certbot-dns-cloudflare, or provide --manual-auth-hook and --manual-cleanup-hook scripts that edit the record through your provider's API.

How long does the _acme-challenge TXT record take to appear?

It depends on your DNS provider: anywhere from seconds to several minutes. Validation reads your zone's authoritative servers, so check with dig +short TXT _acme-challenge.example.com @1.1.1.1 and wait until the expected value appears before continuing a manual run. With a plugin, raise the built-in wait through the plugin's propagation option, for example --dns-cloudflare-propagation-seconds 60, if validation reports that the record was not found.

Is a wildcard certificate less secure than a normal certificate?

The cryptography is identical. The differences are operational: one private key covers every subdomain, so a compromise reaches further, and the DNS API credential that automation requires is itself a sensitive secret stored on the server. If you only run a few known subdomains, a SAN certificate avoids both concerns, which is exactly when this guide recommends skipping the wildcard.