Coming from AdGuard Home

AdGuard Home's filters, custom rules, persistent clients and upstream settings, and where each one lives in kudurru.

AdGuard Home keeps everything in AdGuardHome.yaml; kudurru splits it into settings (upstreams, cache, blocking defaults, query log) and objects (lists, client groups, policies, zones) that use the same YAML in Docker and in Kubernetes. kudurru reads the adblock rule syntax with its own parser (AdGuard Home's code is GPL and none of it is used), so most filter lists work unchanged.

There is no AdGuard Home importer yet (kudurru import adguard is planned, see the migration overview), so you write the objects once by hand.

Concepts

AdGuard HomekudurruNotes
DNS blocklistsBlockList objects with mode: block and url sourcesRefreshed every spec.refreshInterval; kudurru lists refresh <name> fetches now
DNS allowlistsBlockList objects with mode: allow, referenced from a Policy's allowListRefsAllow wins across every list that applies
Custom filtering rulesan inline source (inline: lines) with format: adblock, or kudurru lists allow / deny for single domainsSee the rule support below
Persistent clients (IP, CIDR, ClientID)entries of a ClientGroup: ip, cidr, clientID, hostname, nameGlobMAC entries are accepted but match only once the v0.2 client inventory supplies addresses
Per-client settings (filters, upstreams)a Policy for that group: blockListRefs, allowListRefs, upstreamGroup, blockMode, queryLogThe highest priority wins per setting when several policies match
Client tagsnoneUse client groups
Schedules (pause per client)Policy.spec.schedule: weekdays, start and end time, time zoneThe policy applies only inside its window
Upstream DNS serversupstreams.groupsNamed groups; a Policy or a forward zone picks one
[/example.org/]upstream linesupstreams.conditional, which accepts the same [/a/b/] form as keysLongest suffix wins
Upstream mode: load balancing, parallelstrategy: rtt_weighted, parallel_best (the default), strict (in order), random
Bootstrap DNS serversupstreams.bootstrap
Private reverse DNS serversupstreams.conditional entries for the in-addr.arpa. and ip6.arpa. suffixes
EDNS Client Subnetupstreams.ecs.mode: strip (default), forward, add
Rate limitratelimit.*, queries per minute per prefixUDP only
Blocking mode (default, NXDOMAIN, REFUSED, custom IP, null IP)blockMode (Policy), response (BlockList) or blocking.defaultResponse: zeroIP (default), nxdomain, refused, customIP
Blocked response TTLblocking.blockTTL or Policy.spec.blockTTL
Cache size, optimistic cachingcache.maxEntries, cache.prefetch.*, cache.serveStale.*Serve-stale follows RFC 8767
DNS rewritesrecords in a primary DNSZone of your own (kudurru records add)Per-policy answer rewrites (Policy.spec.rewrites) are planned for v0.2
Blocked services, safe searchthe kudurru-owned catalogue of decision D60Not every build has it yet: check your version's changelog. Without it, list a service's domains in a BlockList
Parental control, safe browsing web servicesnoneUse blocklists
Query log and statisticsthe dashboard; querylog.privacy and querylog.retention.*Per node, with rollups for long-term views
Encryption settings (DoT, DoH, DoQ)listeners.dot, doh, doq, doh3 with a certificate, or dnsdist in frontSee encrypted transports
DHCP servernoneKeep DHCP on your router or a DHCP server
DNSSECnot yetValidation is planned for v0.2

Rule support

RuleExamplev0.1
Domain and subdomains||example.org^yes (||x is read as ||x^)
Exception@@||example.org^yes, within its own list
Hosts lines0.0.0.0 example.orgyes; lines pointing at other addresses are rewrites and counted as unsupported
Plain domains, *.example.orgexample.orgyes; *.x also matches x
Regular expressions/^ad[0-9]+\./yes, up to 1,000 per list
Modifiers$important, $badfilter, $dnstype, $dnsrewrite, $client, $ctag, $denyallowcounted as unsupported and ignored; planned for v0.2

A list's status.invalid and the dashboard show how many rules were skipped, so a browser-oriented list is safe to load.

An AdGuard Home setup as kudurru objects

apiVersion: kudurru.io/v1alpha1
kind: BlockList
metadata: {name: adguard-dns-filter, namespace: dns}
spec:
  mode: block
  sources:
    - url: https://adguardteam.github.io/HostlistsRegistry/assets/filter_1.txt
      format: adblock
    - name: custom-rules
      format: adblock
      inline:
        - "||tracker.example^"
        - "@@||cdn.example^"
---
apiVersion: kudurru.io/v1alpha1
kind: ClientGroup
metadata: {name: living-room-tv, namespace: dns}
spec:
  clients:
    - {ip: 192.168.1.40, name: TV}
---
apiVersion: kudurru.io/v1alpha1
kind: Policy
metadata: {name: living-room-tv, namespace: dns}
spec:
  clientGroupRefs: [living-room-tv]
  blockListRefs: [adguard-dns-filter]
  upstreamGroup: default

The upstream configuration goes into the settings of kudurru.yaml:

upstreams:
  groups:
    default:
      servers: ["tls://1.1.1.1", "tls://9.9.9.9"]
      strategy: parallel_best
    lan: {servers: ["192.168.1.1:53"]}
  conditional:
    "[/lan/168.192.in-addr.arpa/]": lan
  bootstrap: ["1.1.1.1:53", "9.9.9.9:53"]

Then switch over as with Pi-hole: run both, compare with kudurru query <name> --client <address>, and change the DHCP DNS option.

Source: site/content/migrate/adguard-home.md