octodns-adguard
An octoDNS provider that syncs a YAML source of truth to the DNS rewrites of an AdGuard Home instance.
octoDNS owns the rewrites of the zones you declare, and only those. Rewrites belonging to any other zone are never read, never changed and never deleted, so the AdGuard web UI stays usable in parallel for everything octoDNS does not manage.
Support Information
Records
| Type | Notes |
|---|---|
A |
Multi-valued; each value becomes one AdGuard rewrite |
AAAA |
Same |
CNAME |
Single-valued, as AdGuard stores one answer per rewrite |
Records are identified by the (name, type) pair, so an A and an AAAA on the same
name coexist without being confused for one another. Apex ('') and wildcard (*) names
are supported.
Anything else AdGuard can hold is out of scope: rewrites are not a zone file, and there is
no way to express an NS, MX or TXT record through them.
Dynamic
Dynamic records are not supported. A rewrite is a single unconditional answer, with no weighting, no geo or subnet targeting and no health checking to build a pool on, so there is nothing for octoDNS to map a dynamic record onto.
Limitations
list_zones()is not implemented. AdGuard has no notion of a zone — the rewrite list is flat — so dynamic zone configuration is unavailable. List your zones explicitly.- A
CNAMEat the apex is invalid in octoDNS and needslenient, even though AdGuard is happy to store one. - Rewrites whose answer is the special value
AorAAAAare blocking exceptions, not records. They are skipped on read and left untouched. - A rewrite disabled from the web UI is still reported as present, since AdGuard identifies
an entry by its
(domain, answer)pair alone and its add endpoint does not deduplicate. Its disabled state is preserved across updates.
Installation
Requires Python 3.11 or newer and octoDNS 1.5 or newer.
Command line
uv add octodns-adguard
pip install octodns-adguard works the same way, and a requirements.txt entry is just
octodns-adguard.
Pinning
Pinning a version is recommended to avoid unplanned upgrades:
uv add "octodns-adguard==0.1.2"
From Git
To run ahead of a release, install the branch instead — pin a SHA, since main moves:
uv add "git+https://gitlab.com/davinkevin.fr/projects/octodns-adguard.git@<sha>"
You do not have to install anything if you only want to run a sync: see running from the container image.
Configuration
providers:
adguard:
class: octodns_adguard.AdGuardProvider
url: env/ADGUARD_URL
username: env/ADGUARD_USERNAME
password: env/ADGUARD_PASSWORD
| Option | Required | Default | Description |
|---|---|---|---|
url |
yes | Base URL of the instance, e.g. http://10.0.0.2:3000 |
|
username |
yes | Admin user; AdGuard has no service account | |
password |
yes | Admin password, sent as HTTP basic auth | |
default_ttl |
no | 3600 |
See below |
timeout |
no | 5 |
Per-request timeout, in seconds |
About default_ttl
AdGuard stores no TTL, but octoDNS requires one on every record. default_ttl is the
synthetic value reported by populate. You do not have to match it in your YAML: a change
that only moves the TTL is filtered out of the plan, because it is not something this
provider could ever apply.
Bootstrapping from a live instance
Point octodns-dump at the provider to turn what AdGuard already holds into YAML, rather
than transcribing it by hand:
octodns-dump --config-file config/production.yaml \
--output-dir config/ \
example.com. adguard
Review the generated config/example.com.yaml, wire it in as the source for that zone,
then check that octoDNS agrees with reality before handing it the keys:
octodns-sync --config-file config/production.yaml # should report no change
Running from the container image
Every version tag publishes an image built on the upstream octoDNS one, with this provider already installed. Running a sync is then an image reference and a mounted config, nothing else:
docker run --rm -v "$PWD/config:/config" \
registry.gitlab.com/davinkevin.fr/projects/octodns-adguard:v0.1.0 \
octodns-sync --config-file /config/production.yaml --doit
octodns-dump is called the same way. What stays outside the image is your config.yaml
and your zone YAML: they belong to your repository, mounted or cloned by whatever runs the
sync.
There is no latest tag, deliberately: an image reference should always name a version, so
that a cluster never moves without a commit to blame. The image is built for linux/amd64,
and the project being public it pulls without credentials — a Kubernetes CronJob needs no
imagePullSecret.
The image runs as uid 65534 (nobody), so it is accepted by a pod that sets
runAsNonRoot: true. A sync needs nothing writable, but octodns-dump --output-dir does:
the directory you mount for it has to be writable by that uid. In exchange, the files it
writes come out owned by you rather than by root, which on a Linux host is what you want.
Development
Toolchain, tests, build scans and the release process are described in
CONTRIBUTING.md. In short: mise install && uv sync, then uv run pytest.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file octodns_adguard-0.1.2.tar.gz.
File metadata
- Download URL: octodns_adguard-0.1.2.tar.gz
- Upload date:
- Size: 12.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1c44db7c8779cec2c88aa23e2337ae2363f7b9122cbdb2c5dfa07e9487772396
|
|
| MD5 |
bd7e8fb586b0cc975a5c0ef28dec7bb2
|
|
| BLAKE2b-256 |
10d46c713cb78a734f0fb4a97e90d5f3e2df15a3845f2acc2cfda91f80c0a213
|
File details
Details for the file octodns_adguard-0.1.2-py3-none-any.whl.
File metadata
- Download URL: octodns_adguard-0.1.2-py3-none-any.whl
- Upload date:
- Size: 8.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
76fa64d91d1550fecd35bd782ba8b55d36a1ef92ee3fa6d10c5cf239a102126a
|
|
| MD5 |
388149a991db92c2a03828e993fae0d1
|
|
| BLAKE2b-256 |
1f00e91759365a6419da7a82913d9d3c6fd1a2c4374a5e55576e0c5841c52842
|