When you need TLS certificates for domains that aren’t directly accessible via HTTP — wildcard certificates, internal services, or machines behind NAT — the ACME DNS-01 challenge is the only reliable option. Unlike the HTTP-01 challenge which requires a publicly accessible web server, DNS-01 proves domain ownership by creating a specific DNS TXT record that the Certificate Authority (CA) can verify.
This guide compares the four leading open-source ACME clients that support DNS-01 challenges: Certbot, acme.sh, lego, and Dehydrated. We’ll cover installation, Docker deployment, DNS provider integration, and automated renewal so you can pick the right tool for your infrastructure.
For a broader look at certificate automation including Kubernetes-native approaches, see our cert-manager vs lego vs acme.sh comparison.
What Is the ACME DNS-01 Challenge?
The ACME protocol (RFC 8555) defines how clients can automatically obtain TLS certificates from CAs like Let’s Encrypt, ZeroSSL, and Google Trust Services. The DNS-01 challenge works as follows:
- The ACME client requests a certificate for
*.example.com - The CA responds with a challenge token and a key authorization
- The client creates a TXT record at
_acme-challenge.example.comwith the key authorization - The CA queries DNS for that TXT record
- If the record matches, the CA issues the certificate
This process is ideal for:
- Wildcard certificates — the only challenge type that supports
*.domain.com - Internal services — no HTTP endpoint needs to be publicly accessible
- Load-balanced environments — avoids the need for shared storage or coordinated HTTP responses
- Offline issuance — certificates can be generated on a separate machine and deployed later
Why Use DNS-01 Instead of HTTP-01?
The HTTP-01 challenge is simpler to set up but has significant limitations:
| Feature | HTTP-01 Challenge | DNS-01 Challenge |
|---|---|---|
| Wildcard certificates | Not supported | Fully supported |
| Requires public HTTP port | Yes (port 80) | No |
| Works behind NAT/firewall | No | Yes |
| Requires web server access | Yes | No |
| DNS provider API needed | No | Yes |
| Ideal for internal services | No | Yes |
| Supports multiple CAs | Yes | Yes |
If you run a reverse proxy like nginx, Caddy, or Traefik and need wildcard certs for multiple subdomains, DNS-01 is the only practical choice.
Certbot vs acme.sh vs lego vs Dehydrated: Comparison
Here is how the four tools stack up as of April 2026:
| Feature | Certbot | acme.sh | lego | Dehydrated |
|---|---|---|---|---|
| GitHub stars | 33,012 | 46,409 | 9,496 | 6,198 |
| Language | Python | Shell | Go | Shell |
| License | Apache 2.0 | GPL 3.0 | MIT | MIT |
| DNS-01 support | Yes (via plugins) | Yes (native) | Yes (native) | Yes (via hooks) |
| Wildcard certs | Yes | Yes | Yes | Yes |
| ZeroSSL support | Yes | Yes | Yes | No |
| Google Trust Services | Yes | Yes | Yes | Limited |
| Docker image | Official | Community | Official | None |
| DNS providers | ~20 plugins | 100+ built-in | 60+ built-in | Via custom hooks |
| Auto-renewal | systemd timer | Built-in cron | systemd/cron | Manual or cron |
| Configuration | INI/CLI flags | Environment vars | CLI flags + env | Config file |
| Key size options | RSA + ECDSA | RSA + ECDSA | RSA + ECDSA + ED25519 | RSA + ECDSA |
| Multi-domain SANs | Yes | Yes | Yes | Yes |
| Staging environment | Yes | Yes | Yes | Yes |
| ACME v2 support | Yes | Yes | Yes | Yes |
| Last active | 2026-04-24 | 2026-04-26 | 2026-04-25 | 2026-02-03 |
Key takeaways:
- Certbot is the most widely known ACME client with official Docker images and strong plugin support, but DNS plugins are separate packages that need individual installation.
- acme.sh has the broadest DNS provider support with 100+ integrations built in, making it the easiest choice for uncommon DNS providers.
- lego is the most lightweight binary (single Go executable) with strong ED25519 key support and clean API design.
- Dehydrated is the simplest shell script but requires the most manual configuration for DNS hooks.
Installing Each Tool
Certbot
Certbot is available through system package managers on most distributions:
| |
For DNS-01 challenges, you also need the corresponding DNS plugin. Available plugins include python3-certbot-dns-cloudflare, python3-certbot-dns-route53, python3-certbot-dns-google, and more.
acme.sh
acme.sh installs to ~/.acme.sh and manages its own cron job:
| |
lego
lego is distributed as a single static binary:
| |
Dehydrated
Dehydrated is a single bash script:
| |
Docker Deployment Examples
Certbot with Docker
Certbot provides official Docker images that handle certificate issuance and renewal:
| |
Create the Cloudflare credentials file:
| |
acme.sh with Docker
While acme.sh doesn’t have an official Docker image, the community maintains reliable images:
| |
To install certificates to a persistent volume after issuance:
| |
lego with Docker
lego provides official Docker images:
| |
The certificates are stored in /data/certificates/ within the container and mapped to ./certs/ on the host.
DNS Provider API Setup
Most DNS providers require an API token with specific permissions for DNS record management:
Cloudflare
Create an API token at the Cloudflare dashboard with these permissions:
- Zone → DNS → Edit
- Zone → Zone → Read
| |
AWS Route 53
Create an IAM policy with these permissions:
| |
| |
Google Cloud DNS
Create a service account with the DNS Administrator role and export the key:
| |
Automated Renewal Setup
Certbot (systemd timer)
Certbot includes a systemd timer that runs twice daily:
| |
For DNS plugins, add the credentials to /etc/letsencrypt/renewal/*.conf:
| |
acme.sh (built-in cron)
acme.sh automatically installs a cron entry during setup:
| |
lego (systemd timer)
Create a systemd timer for automatic renewal:
| |
| |
| |
Dehydrated (cron)
| |
Which Tool Should You Choose?
| Use Case | Recommended Tool | Reason |
|---|---|---|
| Quick setup with popular DNS providers | acme.sh | 100+ providers built in, zero config |
| Kubernetes/infrastructure automation | lego | Single binary, clean API, ED25519 support |
| Enterprise/Compliance environments | Certbot | Well-audited, official packages, broad CA support |
| Minimal dependencies (bare bash) | Dehydrated | Single script, no runtime dependencies |
| Multi-cloud DNS providers | acme.sh | Broadest provider coverage |
| CI/CD pipeline integration | lego | Easy to embed in CI, no Python/Shell deps |
| Existing Certbot infrastructure | Certbot | Migration path is seamless |
If you are also interested in verifying your TLS configuration after deployment, check out our SSL/TLS scanning tools guide.
FAQ
What is the difference between HTTP-01 and DNS-01 ACME challenges?
HTTP-01 requires placing a file at http://yourdomain/.well-known/acme-challenge/ which the CA fetches over HTTP. DNS-01 requires creating a TXT record at _acme-challenge.yourdomain.com which the CA resolves via DNS. DNS-01 is the only method that supports wildcard certificates and works for services not accessible via the public internet.
Can I use DNS-01 challenges without exposing my DNS provider API credentials?
All DNS-01 tools require API access to create TXT records. However, you can minimize risk by using scoped API tokens (e.g., Cloudflare tokens limited to DNS edit on specific zones) rather than full account keys. Tools like acme.sh and lego support reading credentials from environment variables, Docker secrets, or HashiCorp Vault rather than plaintext config files.
How long does DNS propagation take for ACME challenges?
Most CAs poll for the DNS TXT record every 2-5 seconds and typically complete validation within 30-60 seconds. However, some DNS providers have higher propagation delays. If validation fails, all tools support configurable wait periods — acme.sh uses --dnssleep, Certbot uses --dns-cloudflare-propagation-seconds, and lego uses --dns-timeout.
Do these tools support multiple CAs beyond Let’s Encrypt?
Yes. Certbot, acme.sh, and lego all support Let’s Encrypt, ZeroSSL, Google Trust Services, and BuyPass. Dehydrated supports Let’s Encrypt and ZeroSSL with custom CA configuration. acme.sh allows switching CAs with --server zerossl or --server buypass. lego uses --server flag to specify any ACME v2 endpoint.
Can I automate DNS-01 challenges if my DNS provider is not natively supported?
Yes. All four tools support custom DNS hooks. For acme.sh, create a dns_myprovider.sh script following their API format. For Certbot, write a Python plugin or use the --manual-auth-hook flag. For lego, implement the DNS provider interface in Go or use the --dns flag with a custom executable. For Dehydrated, the hook script receives deploy_challenge and clean_challenge calls.
How do I renew certificates before they expire?
All tools handle renewal automatically when configured correctly. Certbot runs twice daily via systemd timer and only renews within 30 days of expiry. acme.sh checks certificates daily via cron and renews automatically. lego requires a systemd timer or cron entry to trigger renewal. Dehydrated needs a daily cron job with the --cron flag. You can always force a renewal manually for testing with the respective --force or --dry-run flags.