Skip to main content

octodns-adguard

PyPI Image Pipeline

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() guesses, so dynamic zone configuration works but is not exact. AdGuard has no notion of a zone — the rewrite list is flat, and nothing in a domain says where the boundary sits — so the last two labels are taken as the zone. Under a multi-label public suffix that is wrong: a rewrite on home.example.co.uk is reported as the zone co.uk.. Name those zones explicitly instead of through '*'.
  • 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.2.0"

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.2.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.

Metadata

Release files for octodns-adguard 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for octodns-adguard 0.2.0
File Size Uploaded Provenance Actions
octodns_adguard-0.2.0.tar.gz 14.8 kB Without provenance Details for octodns_adguard-0.2.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for octodns-adguard 0.2.0
File Interpreter ABI Platform Actions
octodns_adguard-0.2.0-py3-none-any.whl Python 3 none any Details for octodns_adguard-0.2.0-py3-none-any.whl

Total release size: 24.1 kB

Release files / octodns_adguard-0.2.0.tar.gz

Download URL octodns_adguard-0.2.0.tar.gz
Size 14.8 kB
Tags Source
SHA-256 checksum
How to use checksums
518ba58801a0f0b15668d6faf57483cc16d82ee6ef2dc21994ba563283c1b3b4
BLAKE2b-256 checksum
How to use checksums
aaf4368a945e0f6064bbf232f67891b914a907874eb6b8cea34f35f7125f0782
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.11

Release files / octodns_adguard-0.2.0-py3-none-any.whl

Download URL octodns_adguard-0.2.0-py3-none-any.whl
Size 9.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
8eeed313c5e96bb8c83835fe182214899302b91632c25910c5276b4910f6a188
BLAKE2b-256 checksum
How to use checksums
594113b0beadf56d116a81c0e4c1eac977be84aa965586d19be60533d3785515
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.11

Security for octodns-adguard 0.2.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files No provenance

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