SSL & Certs Intermediate

Certbot DNS-01 Challenge TXT Record Mismatch Troubleshooting on CentOS Stream / Rocky Linux

Resolve Certbot's DNS-01 challenge TXT record mismatch error on CentOS Stream or Rocky Linux systems, ensuring successful Let's Encrypt certificate issuance.

👨‍💻
Senior Systems Architect • Verified in Staging Labs

Resolve Certbot's DNS-01 challenge TXT record mismatch error on CentOS Stream or Rocky Linux systems, ensuring successful Let's Encrypt certificate issuance.

Certbot's DNS-01 challenge method is a powerful way to obtain or renew Let's Encrypt SSL certificates, especially for wildcard domains or when HTTP-01 challenges are not feasible (e.g., internal servers, complex network configurations). This method relies on Certbot creating a special _acme-challenge.<yourdomain> TXT record in your domain's DNS zone to prove ownership to the Let's Encrypt ACME server. When this process fails with a "TXT records mismatch" error, it indicates that while a TXT record was found, its value did not match what Let's Encrypt expected, thereby halting the certificate issuance or renewal. This guide provides expert-level troubleshooting steps specifically for CentOS Stream and Rocky Linux environments.

Symptom & Error Signature

When attempting to obtain or renew a Let's Encrypt SSL certificate using Certbot with the DNS-01 challenge method, you might encounter an error similar to the following in your terminal output:

Saving debug log to /var/log/letsencrypt/letsencrypt.log
Plugins selected: Authenticator dns-cloudflare, Installer nginx
...
Performing the following challenges:
dns-01 challenge for example.com
Waiting for verification...
Challenge failed for domain example.com
dns-01 challenge for example.com failed
Cleaning up challenges
Some challenges have failed.

IMPORTANT NOTES:
 - The following errors were reported by the server:

   Domain: example.com
   Type:   dns
   Detail: During secondary validation: The TXT record recently added to verify domain ownership did not match the expected value. Expected: "EXPECTED_TXT_VALUE_HERE" but got: "ANOTHER_TXT_VALUE_HERE"
   To fix these errors, please make sure that your domain name was
   entered correctly and that the DNS A/AAAA record(s) for that domain
   contain the right IP address. Additionally, please check that the
   nameservers are configured correctly and that the DNS TXT records are
   propagated correctly.

This specific error clearly indicates that while a _acme-challenge.example.com TXT record was found by Let's Encrypt's validation servers, its value (ANOTHER_TXT_VALUE_HERE) did not match the value they expected (EXPECTED_TXT_VALUE_HERE). This means the DNS record was either incorrect, outdated, or not fully propagated at the time of validation.

Root Cause Analysis

The "TXT records mismatch" error typically stems from one or more of the following underlying issues:

  1. DNS Propagation Delays: This is the most common culprit. DNS changes, particularly newly created or updated TXT records, can take time (minutes to hours) to propagate across global DNS servers. Certbot might attempt to verify the record before it has fully propagated, leading to a mismatch (as an old or no record is seen).
  2. Incorrect or Stale DNS Records:
    • Multiple _acme-challenge TXT records: If previous failed Certbot attempts left behind old _acme-challenge records, or if another service is also attempting DNS-01 challenges, Let's Encrypt might pick up the wrong record from the existing set.
    • Typographical Errors (Manual Challenge): If you are performing a manual DNS-01 challenge (i.e., Certbot prompts you to manually create the TXT record), a simple typo in the record value is a straightforward cause for the mismatch.
  3. Certbot DNS Plugin Misconfiguration (for Automated Challenges):
    • Incorrect API Credentials: The credentials file (e.g., .ini or .json) for your specific DNS provider plugin (e.g., certbot-dns-cloudflare, certbot-dns-route53) might contain an incorrect API key, token, email, or zone ID, preventing Certbot from correctly creating or updating the TXT record.
    • Insufficient Permissions: The API credentials provided to Certbot might lack the necessary permissions within your DNS provider's system to modify TXT records for your domain.
    • Rate Limiting by DNS Provider: Excessive API calls to your DNS provider within a short period might trigger temporary rate limits, causing record updates to fail silently or incompletely.
  4. DNS Caching Issues: Your local server's DNS resolver (systemd-resolved, dnsmasq) or intermediate upstream DNS resolvers might be caching older DNS records, causing Certbot (or your manual dig queries) to see an outdated state.
  5. DNSSEC Misconfiguration: While less common for a direct "mismatch" of values, incorrectly configured DNSSEC can lead to validation failures, sometimes manifesting as issues with specific record types like TXT not being resolvable or causing SERVFAIL errors.
  6. Certbot or DNS Plugin Outdated: Bugs in older versions of Certbot or its DNS plugins could lead to issues interacting with DNS providers, generating incorrect challenge values, or handling the validation process correctly.

Step-by-Step Resolution

Follow these structured steps to diagnose and resolve the Certbot DNS-01 challenge TXT records mismatch on your CentOS Stream or Rocky Linux system.

1. Verify DNS Propagation and TXT Record Value

This is the most critical first step. You need to confirm what TXT record is actually visible to the world for your _acme-challenge subdomain.

  1. Identify the expected value: Carefully examine the Certbot error message for the Expected: "EXPECTED_TXT_VALUE_HERE" string. This is the exact value Let's Encrypt's validation server is looking for.

  2. Query DNS from your server: Use the dig utility to query public DNS resolvers directly, bypassing your local server's cache. Replace example.com with your actual domain.

    # Query Google's public DNS resolver
    dig +short TXT _acme-challenge.example.com @8.8.8.8
    # Query Cloudflare's public DNS resolver
    dig +short TXT _acme-challenge.example.com @1.1.1.1
    # Query your domain's authoritative nameservers (replace ns1.yourdns.com with your actual nameserver)
    dig +short TXT _acme-challenge.example.com @ns1.yourdns.com
    

    Examine the output carefully:

    • If you see no output or "NXDOMAIN", the record hasn't propagated or wasn't created by Certbot.
    • If you see multiple TXT records, note all of their values.
    • If you see a single TXT record, compare its value to the EXPECTED_TXT_VALUE_HERE from Certbot's error.
  3. Use Online DNS Checkers: Websites like dnschecker.org, whatsmydns.net, or mxtoolbox.com/txt.aspx allow you to check DNS propagation globally. Enter _acme-challenge.example.com and select the TXT record type to see what different regions report.

    If the dig output or online checkers show a different value than EXPECTED_TXT_VALUE_HERE, or if multiple _acme-challenge TXT records are present, this is the core issue. Proceed to step 4 and 5.

2. Increase Certbot's DNS Propagation Delay

If your DNS provider is known to be slow in propagating changes, Certbot might attempt validation before the record is globally visible. You can instruct Certbot to wait longer.

  1. Identify your DNS plugin: For example, dns-cloudflare, dns-route53, dns-digitalocean, etc.

  2. Add propagation delay: Append the --dns-<plugin>-propagation-seconds <seconds> flag to your Certbot command. A value between 30 and 120 seconds (e.g., --dns-cloudflare-propagation-seconds 60) is often sufficient.

    # Example for Cloudflare plugin, setting a 60-second delay for renewal
    sudo certbot renew --cert-name example.com 
      --dns-cloudflare 
      --dns-cloudflare-credentials /etc/letsencrypt/cloudflare.ini 
      --dns-cloudflare-propagation-seconds 60 
      --nginx # or --apache, or omit if using --webroot / -d without installer
    
  3. For Automatic Renewals: To make this propagation delay permanent for future automatic renewals, edit Certbot's global configuration file, typically located at /etc/letsencrypt/cli.ini, or within the specific renewal configuration file for your domain (/etc/letsencrypt/renewal/example.com.conf).

    # Example content for /etc/letsencrypt/cli.ini or /etc/letsencrypt/renewal/example.com.conf
    dns-cloudflare-propagation-seconds = 60
    

    After updating the configuration, attempt renewal:

    sudo certbot renew
    

3. Review Certbot DNS Plugin Configuration and Credentials

If you're using a Certbot DNS plugin for automated challenges, verify its setup meticulously.

  1. Check Credentials File Permissions: The credentials file containing your DNS provider's API keys must be readable only by root to protect sensitive information.

    sudo chmod 600 /etc/letsencrypt/cloudflare.ini
    

    Incorrect permissions (e.g., world-readable) will prevent Certbot from securely reading the file and could pose a significant security risk.

  2. Verify Credential File Content: Open your credentials file (e.g., /etc/letsencrypt/cloudflare.ini, /etc/letsencrypt/route53.ini) and double-check the API key/token, email, zone ID, or other specific identifiers required by your DNS provider. Even a single character typo will cause an authentication failure.

    • Cloudflare Example (cloudflare.ini):
      dns_cloudflare_email = [email protected]
      dns_cloudflare_api_key = YOUR_GLOBAL_API_KEY_OR_TOKEN_HERE
      
    • AWS Route53 Example (route53.ini):
      dns_route53_aws_access_key_id = AKIA...
      dns_route53_aws_secret_access_key = YOUR_SECRET_ACCESS_KEY_HERE
      

    Ensure you are using an API key/token with sufficient permissions to manage TXT records for the specific domain or zone you are trying to certify.

  3. Install/Update DNS Plugin: Ensure the relevant Certbot DNS plugin is installed and up-to-date.

    # Install with dnf (if available in EPEL, often for core plugins like Nginx/Apache)
    sudo dnf install python3-certbot-nginx # or python3-certbot-apache
    # Install specific DNS plugin (often via pip, as dnf might not have all)
    sudo pip3 install --upgrade certbot-dns-cloudflare # or -dns-route53, etc.
    

4. Remove Stale _acme-challenge TXT Records

Multiple or outdated _acme-challenge TXT records for your domain can confuse Let's Encrypt's validation servers, leading to a mismatch or failure to find the correct record.

  1. Access your DNS provider's control panel: Log in to your domain registrar or DNS hosting provider (e.g., Cloudflare, AWS Route 53, GoDaddy, DigitalOcean).
  2. Locate your domain's DNS zone: Find the DNS management section for the domain experiencing the error.
  3. Identify and delete old _acme-challenge TXT records: Look for any TXT records with the hostname _acme-challenge or _acme-challenge.www that are not the EXPECTED_TXT_VALUE_HERE from your current Certbot attempt (or that seem orphaned from previous failed attempts). Delete them.

    Be extremely careful not to delete legitimate TXT records used for other services (e.g., SPF, DKIM, DMARC for email validation, or Google Site Verification). These records typically do not begin with _acme-challenge.

  4. Verify Deletion: Use dig (as in Step 1) and online DNS checkers to confirm the old records have been removed and only the correct current record (if Certbot successfully placed one) or no _acme-challenge record exists.

5. Update Certbot and Python Packages

An outdated Certbot client or its underlying Python dependencies might contain bugs or have compatibility issues with newer Let's Encrypt API changes or DNS provider APIs.

  1. Update Certbot (via DNF):
    sudo dnf update certbot python3-certbot-nginx # or python3-certbot-apache, if installed this way
    
    If you installed Certbot via Snap (common on CentOS Stream/Rocky Linux for newer versions):
    sudo snap refresh certbot
    
  2. Update DNS Plugins (via pip): If you installed DNS plugins via pip, update them separately to ensure you have the latest bug fixes.
    sudo pip3 install --upgrade certbot certbot-dns-cloudflare # and any other installed DNS plugins
    

6. Clear Local DNS Cache (if applicable)

If your CentOS Stream/Rocky Linux server runs a local DNS caching service, it might be holding onto stale records, causing Certbot's local lookups to be incorrect.

  1. Restart systemd-resolved:
    sudo systemctl restart systemd-resolved
    
  2. Restart dnsmasq (if installed and used):
    sudo systemctl restart dnsmasq
    

7. Check for DNSSEC Issues

While less common for a direct "mismatch," improperly configured DNSSEC can cause validation failures by making DNS records unresolvable or untrustworthy.

  1. DNSSEC diagnostic tools: Visit dnsviz.net or zonemaster.net. Enter your domain name and check for any DNSSEC errors or warnings. Common issues include incorrect DS records at your registrar or expired DNSKEYs.
  2. Consult your DNS provider: If DNSSEC errors are identified, your DNS provider or domain registrar can help diagnose and correct DS record issues. If not managed carefully, it might be better to temporarily disable DNSSEC at your registrar while troubleshooting, then re-enable once Certbot is working.

8. Retest the Certbot Challenge

After carefully performing the above troubleshooting steps, reattempt the Certbot challenge.

  1. For existing certificates (renewal):

    sudo certbot renew
    

    If certbot renew has failed multiple times, you might need to force a new renewal attempt:

    sudo certbot renew --force-renewal
    

    Use --force-renewal sparingly. Repeated failures with --force-renewal can quickly trigger Let's Encrypt rate limits (e.g., 5 failures per week for a given domain), temporarily blocking further certificate issuance for your domain. Use --dry-run first (see below).

  2. For new certificates:

    sudo certbot certonly --dns-cloudflare -d example.com -d www.example.com --email [email protected] --agree-tos --no-eff-email --dns-cloudflare-credentials /etc/letsencrypt/cloudflare.ini --nginx # or --apache
    

    Remember to replace dns-cloudflare with your actual DNS plugin and adjust domain names and email as necessary.

    Before a live run, always test your configuration with --dry-run to ensure all parameters are correct and avoid hitting rate limits:

    sudo certbot renew --dry-run
    # Or for a new certificate:
    sudo certbot certonly --dns-cloudflare -d example.com --dry-run --debug # Add relevant options
    

    The --debug flag can provide more verbose output in /var/log/letsencrypt/letsencrypt.log, which may offer additional clues.

By systematically working through these expert-level steps, you should be able to identify and resolve the Certbot DNS-01 challenge TXT records mismatch, allowing you to successfully issue or renew your Let's Encrypt SSL certificates on CentOS Stream or Rocky Linux.

👨‍💻

Johnathon Wheeler

Senior Systems Architect & DevOps Engineer • Austin, TX

Connect on LinkedIn →

Johnathon has over 16 years of hands-on experience designing, debugging, and scaling Linux web hosting stacks, container clusters, and high-availability database architectures. Every guide on ButItWorkedLocal is independently tested against Debian 12, Ubuntu 24.04/22.04 LTS, Rocky Linux, and Docker environments to guarantee reproducibility in production.

🛡️

Our Production Verification Guarantee

Encountering a bug not covered here or running a non-standard kernel configuration? Our solutions are continually refined against real production incidents. Submit an environment trace for our editorial team to replicate.