Skip to main content

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 CNAME at the apex is invalid in octoDNS and needs lenient, even though AdGuard is happy to store one.
  • Rewrites whose answer is the special value A or AAAA are 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

octodns_adguard-0.1.2.tar.gz (12.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

octodns_adguard-0.1.2-py3-none-any.whl (8.6 kB view details)

Uploaded Python 3

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

Hashes for octodns_adguard-0.1.2.tar.gz
Algorithm Hash digest
SHA256 1c44db7c8779cec2c88aa23e2337ae2363f7b9122cbdb2c5dfa07e9487772396
MD5 bd7e8fb586b0cc975a5c0ef28dec7bb2
BLAKE2b-256 10d46c713cb78a734f0fb4a97e90d5f3e2df15a3845f2acc2cfda91f80c0a213

See more details on using hashes here.

File details

Details for the file octodns_adguard-0.1.2-py3-none-any.whl.

File metadata

File hashes

Hashes for octodns_adguard-0.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 76fa64d91d1550fecd35bd782ba8b55d36a1ef92ee3fa6d10c5cf239a102126a
MD5 388149a991db92c2a03828e993fae0d1
BLAKE2b-256 1f00e91759365a6419da7a82913d9d3c6fd1a2c4374a5e55576e0c5841c52842

See more details on using hashes here.

Release history Release notifications | RSS feed

0.2.0

2 files

This release

0.1.2 This release

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page