Certbot DNS DuckDNS Plugin
Certbot plugin that automates Let's Encrypt DNS-01 challenges for DuckDNS domains via the DuckDNS update API.
Repository Health
Technical Analysis
certbot-dns-duckdns is a certbot plugin that automates the DNS-01 challenge for domains hosted on DuckDNS, a free dynamic DNS service. Instead of manually creating and removing TXT records during Let’s Encrypt certificate issuance or renewal, the plugin talks directly to the DuckDNS update API to set and clear the _acme-challenge TXT record required for domain validation.
The plugin registers as a certbot authenticator entry point (dns-duckdns), accepts a DuckDNS token via CLI flag, credentials file, or environment variable, and supports delegated ACME challenges where a non-DuckDNS domain’s _acme-challenge subdomain is CNAME’d to a DuckDNS domain. It ships as a PyPI package, a Docker image, and a Snap package, and preserves any TXT value that existed before the challenge so unrelated DNS state isn’t clobbered.
What You Get
- A certbot authenticator plugin (
dns-duckdns) that plugs into certbot’s plugin system via a registered entry point. - A standalone
DuckDNSClientclass for setting and clearing TXT records through the DuckDNS update API. - Automatic detection and restoration of any TXT record value that existed before the ACME challenge began.
- Support for delegated ACME challenges, so a non-DuckDNS domain can CNAME
_acme-challengeto a DuckDNS subdomain. - Multiple credential input methods: CLI flag, INI credentials file, or environment variable.
- A prebuilt Docker image and Snap package alongside the PyPI distribution.
Common Use Cases
- Issuing and auto-renewing Let’s Encrypt certificates for services running behind a DuckDNS dynamic DNS hostname.
- Running certbot inside a Docker container or cron job on a home server / self-hosted setup that uses DuckDNS for dynamic DNS.
- Validating domains through a delegated ACME challenge when the certificate’s domain isn’t itself a DuckDNS domain but its
_acme-challengerecord is CNAME’d to one. - Automating certificate renewal for self-hosted reverse proxies (Traefik, Nginx, Caddy) fronting services on a dynamic residential or home IP.
Under The Hood
Architecture
The package is a thin two-layer design: certbot_dns_duckdns/duckdns/client.py implements a standalone DuckDNSClient that wraps the DuckDNS HTTP update API (set_txt_record/clear_txt_record, plus domain-validation regexes and typed exceptions like TXTUpdateError and NotValidDuckdnsDomainError), while certbot_dns_duckdns/cert/client.py implements the Authenticator class that certbot’s plugin framework loads via the certbot.plugins entry point declared in setup.py. Authenticator subclasses certbot’s dns_common.DNSAuthenticator and only needs to implement _perform and _cleanup, which certbot calls during the DNS-01 challenge lifecycle; these delegate to the DuckDNSClient for the actual API calls and add a delegated-domain resolution step (_get_duckdns_domain) that walks CNAME/A/AAAA records via dnspython when the target domain isn’t itself a DuckDNS domain. This separation means the DuckDNS API logic has no dependency on certbot and is independently testable.
Tech Stack
Pure Python (3.10+ per setup.py classifiers, though requirements.txt pins certbot>=1.18.0,<6.0), built on certbot’s plugin API, requests for HTTP calls to the DuckDNS update endpoint, and dnspython for resolving delegated ACME challenge records. Packaging is standard setuptools (setup.py, entry_points registration), with parallel distribution as a Docker image (Dockerfile plus docker-entrypoint.sh) and a Snap package (snap/ directory), each with its own GitHub Actions publish workflow.
Code Quality
Tests live under tests/ (duckdns_tests.py, cert_client.py) using Python’s built-in unittest, with the responses library mocking DuckDNS HTTP calls and certbot’s NamespaceConfig used to exercise the Authenticator without a live certbot install. CI runs the suite across five Python versions (3.10–3.14) via unit-tests.yml. Separate linting.yml and formatting_check.yml workflows enforce flake8, pylint, and ruff format --check on every push and PR. Error handling is explicit and typed: custom exceptions (TXTUpdateError, NotValidDuckdnsDomainError, NotValidDuckdnsTokenError) carry structured context rather than surfacing raw request/response text, and Authenticator translates all of them into certbot’s PluginError at the plugin boundary.
What Makes It Unique
Unlike a generic DNS-01 plugin, it’s purpose-built for DuckDNS’s single-TXT-record-per-domain limitation: before setting the challenge value it reads and caches whatever TXT value already existed and restores it in _cleanup, rather than just clearing the record, so other tooling relying on that TXT record isn’t disrupted mid-renewal. It also handles the case where the certificate’s domain is not itself hosted on DuckDNS but delegates its _acme-challenge subdomain there via CNAME (a common pattern for using DuckDNS purely as a low-friction ACME DNS backend for a domain registered elsewhere), resolving that delegation through direct DNS queries rather than assuming a 1:1 domain-to-DuckDNS-account mapping.