Settings reference

Every key of kudurru.yaml with its type, default, scope and environment variable.

Every settings key of kudurru.yaml, generated from the metadata table in internal/config/schema.go, the same table kudurru config schema prints and GET /api/v1/settings/_schema serves.

Where a value comes from. Each layer overrides the one before it:

  1. the built-in default (the Default column);
  2. the config file (--config, KUDURRU_CONFIG, default /etc/kudurru/kudurru.yaml);
  3. the settings file (settingsFile or KUDURRU_SETTINGS_FILE) for cluster-scope keys, which is where changes made in the UI, with kudurru settings set or with PATCH /api/v1/settings are written; it is watched and hot keys apply at once;
  4. the environment: KUDURRU_<SECTION>__<KEY>, with __ for every dot of the path (KUDURRU_LOG__LEVEL=debug), or KUDURRU_<SECTION>__<KEY>_FILE naming a file that holds the value. string[] values are comma-separated; list and map keys can only be set in a file. A key set from the environment is read-only in the UI, and a variable that names no key stops the server at start.

Scope. node keys belong to one host. cluster keys are shared by every node of a deployment.

Allowed values. The bold label is the name the UI shows. The Allowed line lists what a key accepts: a closed set, a range, a format (ip, cidr for a CIDR or a single address, hostport, url, domain, upstream for a server in dnsproxy syntax), the object a value names (ref) and the members of list and map values. The config file, the environment and the API reject anything else with an error naming the key.

Flags. host-only keys can only be set in the file or the environment on the host, never through the API or the UI (they name listeners, files and privileged behaviour). restart keys take effect after a restart (in Kubernetes the chart rolls the pods); every other key is applied while the server runs. write-only keys are never returned by the API.

node

KeyTypeDefaultScopeDescription
node.namestring(empty)nodeNode name. Name of this server in the dashboard, the query log and the block page, e.g. dns1. Empty uses the hostname. Every node of a cluster needs its own name; it identifies the node to the others.
host-only, restart
Environment: KUDURRU_NODE__NAME
node.lameduckduration2snodeLameduck drain window. How long the server keeps answering after it is told to stop, so devices and load balancers move to another server first, e.g. 2s; 0 stops at once. During this window readiness (/healthz/ready) answers 503 and the health name SERVFAIL, then the listeners close. The Helm chart also runs kudurru lameduck in preStop.
host-only, restart
Environment: KUDURRU_NODE__LAMEDUCK
node.dataDirstring/var/lib/kudurrunodeData directory. Directory where kudurru keeps its data, e.g. /var/lib/kudurru; it must be writable and should survive restarts and be backed up. It holds the object store (state.db), the query log (querylog.db), the zone snapshot (local.db), downloaded lists and keys.
host-only, restart
Environment: KUDURRU_NODE__DATADIR

listeners

KeyTypeDefaultScopeDescription
listeners.dnslist[{address: "0.0.0.0:53", protocols: [udp, tcp]}]nodeDNS listeners. Addresses where devices send plain DNS queries. Port 53 is what devices expect; the default 0.0.0.0:53 answers on every address over UDP and TCP. Bind one address, e.g. 192.168.1.10:53, when another service on the machine already uses port 53.
Allowed: items: {address (required): string (format hostport); protocols: list of string (one of udp | tcp) (default ["udp","tcp"])}.
host-only, restart
Environment: file only.
listeners.dotlist[]nodeDNS-over-TLS listeners. Encrypted DNS-over-TLS (DoT), usually on port 853, for devices that support it such as Android's Private DNS. Each entry needs an address, e.g. 0.0.0.0:853, and a certificate and key valid for the name devices use. Empty serves none. RFC 7858, ALPN dot.
Allowed: items: {address (required): string (format hostport); tls (required): {certFile (required): string; keyFile (required): string}}.
host-only, restart
Environment: file only.
listeners.dohlist[]nodeDNS-over-HTTPS listeners. Encrypted DNS-over-HTTPS (DoH), usually on port 443, for browsers and devices that support it. Each entry needs an address, e.g. 0.0.0.0:443, a certificate and key, and optionally a path (default /dns-query). Empty serves none. RFC 8484, HTTP/2 and HTTP/1.1.
Allowed: items: {address (required): string (format hostport); path: string (default /dns-query); tls (required): {certFile (required): string; keyFile (required): string}}.
host-only, restart
Environment: file only.
listeners.doqlist[]nodeDNS-over-QUIC listeners. Encrypted DNS-over-QUIC (DoQ), usually on UDP port 853, for clients that support it. Each entry needs an address, e.g. 0.0.0.0:853, and a certificate and key. Empty serves none. RFC 9250, ALPN doq.
Allowed: items: {address (required): string (format hostport); tls (required): {certFile (required): string; keyFile (required): string}}.
host-only, restart
Environment: file only.
listeners.doh3list[]nodeDNS-over-HTTP/3 listeners. Encrypted DNS-over-HTTPS over HTTP/3 (QUIC), usually on UDP port 443, for browsers that support it. Each entry needs an address, e.g. 0.0.0.0:443, a certificate and key, and optionally a path (default /dns-query). Empty serves none. ALPN h3.
Allowed: items: {address (required): string (format hostport); path: string (default /dns-query); tls (required): {certFile (required): string; keyFile (required): string}}.
host-only, restart
Environment: file only.
listeners.api.addressstring0.0.0.0:8080nodeAPI and UI address. Address and port of the dashboard and the HTTP API, e.g. 0.0.0.0:8080 for every address or 127.0.0.1:8080 for this machine only; browse to https://<server address>:8080.
Allowed: format hostport.
host-only, restart
Environment: KUDURRU_LISTENERS__API__ADDRESS
listeners.api.tls.autostringselfsignednodeAPI automatic TLS. Whether the dashboard uses HTTPS with a certificate kudurru makes itself when no certificate file is set. selfsigned (default) encrypts the connection, though browsers warn because no public authority signed the certificate; off serves plain HTTP, for use behind a reverse proxy that handles TLS. The certificate is generated once into <node.dataDir>/keys/.
Allowed: one of selfsigned | off.
host-only, restart
Environment: KUDURRU_LISTENERS__API__TLS__AUTO
listeners.api.tls.certFilestring(empty)nodeAPI certificate file. Path of your own certificate for the dashboard (PEM, full chain), e.g. /etc/kudurru/tls/tls.crt; set it with the key file to replace the self-signed certificate. Takes precedence over listeners.api.tls.auto.
host-only, restart
Environment: KUDURRU_LISTENERS__API__TLS__CERTFILE
listeners.api.tls.keyFilestring(empty)nodeAPI key file. Path of the private key (PEM) of listeners.api.tls.certFile, e.g. /etc/kudurru/tls/tls.key.
host-only, restart
Environment: KUDURRU_LISTENERS__API__TLS__KEYFILE
listeners.api.pathPrefixstring(empty)nodeAPI path prefix. URL path the dashboard and API are served under when a reverse proxy mounts kudurru below the root of a site, e.g. /kudurru for https://home.example/kudurru/. Empty serves them at /.
host-only, restart
Environment: KUDURRU_LISTENERS__API__PATHPREFIX
listeners.api.trustedProxiesstring[][]nodeTrusted reverse proxies. Reverse proxies in front of the dashboard, as addresses or CIDRs, e.g. 192.168.1.5 or 10.0.0.0/8. Only requests from them may report the real client address and scheme in X-Forwarded-For and X-Forwarded-Proto; empty trusts no proxy.
Allowed: items: string (format cidr).
host-only, restart
Environment: KUDURRU_LISTENERS__API__TRUSTEDPROXIES
listeners.ops.addressstring0.0.0.0:8081nodeHealth listener address. Address and port of the health checks Docker, Kubernetes and load balancers poll, e.g. 0.0.0.0:8081 (/healthz/live, /healthz/ready). They need no login.
Allowed: format hostport.
host-only, restart
Environment: KUDURRU_LISTENERS__OPS__ADDRESS
listeners.metrics.addressstring0.0.0.0:9153nodeMetrics listener address. Address and port Prometheus scrapes metrics from (/metrics), e.g. 0.0.0.0:9153, or 127.0.0.1:9153 to keep them on this machine.
Allowed: format hostport.
host-only, restart
Environment: KUDURRU_LISTENERS__METRICS__ADDRESS
listeners.proxyProtocolFromstring[][]nodePROXYv2 peers. Only with a dnsdist front end: the exact addresses allowed to tell kudurru the real client address in a PROXY protocol v2 header, as CIDRs, normally one /32 per front end, e.g. 192.168.1.2/32. Leave empty when devices query kudurru directly. Use loopback only when dnsdist shares kudurru's network namespace.
Allowed: items: string (format cidr).
host-only, restart
Environment: KUDURRU_LISTENERS__PROXYPROTOCOLFROM
listeners.proxyProtocolSecretFilestring(empty)nodePROXYv2 secret file. Path of a file holding the secret shared with the dnsdist front end, at least 32 bytes, e.g. made with openssl rand -hex 32. Required when proxyProtocolFrom names a peer that is not loopback, so another host cannot forge client addresses. Carried in PROXYv2 TLV 0xE0.
host-only, restart
Environment: KUDURRU_LISTENERS__PROXYPROTOCOLSECRETFILE
listeners.internal.addressstring0.0.0.0:8082nodeNode-to-node listener address. Address and port the other nodes of a cluster use to reach this one, e.g. 0.0.0.0:8082; a single server does not need it reachable. Always TLS with a bearer token; it serves list bodies by digest and /internal/status.
Allowed: format hostport.
host-only, restart
Environment: KUDURRU_LISTENERS__INTERNAL__ADDRESS
listeners.internal.tokenFilestring(empty)nodeInternal token file. Path of a file holding the token nodes present to each other. Empty (the usual choice) derives it from the encryption key, so every node with the same KUDURRU_ENCRYPTION_KEY agrees on it (HKDF).
host-only, restart
Environment: KUDURRU_LISTENERS__INTERNAL__TOKENFILE
listeners.internal.tls.certFilestring(empty)nodeInternal certificate file. Path of the certificate (PEM) of the node-to-node listener, for your own PKI. Empty (the usual choice) issues one from a CA derived from the encryption key, which every node with the same key trusts.
host-only, restart
Environment: KUDURRU_LISTENERS__INTERNAL__TLS__CERTFILE
listeners.internal.tls.keyFilestring(empty)nodeInternal key file. Path of the private key (PEM) of listeners.internal.tls.certFile.
host-only, restart
Environment: KUDURRU_LISTENERS__INTERNAL__TLS__KEYFILE
listeners.internal.tls.caFilestring(empty)nodeInternal CA bundle. Path of the CA bundle (PEM) trusted when this node connects to other nodes; set it together with your own certificates. Empty trusts the CA derived from the encryption key.
host-only, restart
Environment: KUDURRU_LISTENERS__INTERNAL__TLS__CAFILE
listeners.internal.tls.serverNamestring(empty)nodeInternal TLS server name. Name checked in other nodes' certificates, e.g. kudurru-internal.home.example; it must be a SAN of listeners.internal.tls.certFile. Empty uses the fixed name of the derived certificates.
Allowed: format domain.
host-only, restart
Environment: KUDURRU_LISTENERS__INTERNAL__TLS__SERVERNAME
listeners.blockPage.httpstring:80nodeBlock page HTTP address. Address and port where the block page answers browsers over plain HTTP, e.g. :80; empty turns it off. Under the blockPage block response, blocked names point at this server, so a browser that opens one lands here. Needs blockPage.enabled.
Allowed: format hostport.
host-only, restart
Environment: KUDURRU_LISTENERS__BLOCKPAGE__HTTP
listeners.blockPage.httpsstring(empty)nodeBlock page HTTPS address. Optional address and port of the block page over HTTPS, e.g. :443; empty (the default) leaves it off. No certificate can be valid for the blocked names, so browsers show a certificate warning before the page, and sites that use HSTS show no page at all (docs/deploy/blocking-page.md).
Allowed: format hostport.
host-only, restart
Environment: KUDURRU_LISTENERS__BLOCKPAGE__HTTPS
listeners.blockPage.tls.certFilestring(empty)nodeBlock page certificate file. Path of the certificate (PEM) of the HTTPS block page. Empty generates a self-signed one into <node.dataDir>/keys/blockpage.crt; a certificate of your own cannot match the blocked names either.
host-only, restart
Environment: KUDURRU_LISTENERS__BLOCKPAGE__TLS__CERTFILE
listeners.blockPage.tls.keyFilestring(empty)nodeBlock page key file. Path of the private key (PEM) of listeners.blockPage.tls.certFile.
host-only, restart
Environment: KUDURRU_LISTENERS__BLOCKPAGE__TLS__KEYFILE
listeners.socketsint0nodeSockets per DNS listener. How many sockets each UDP and TCP DNS listener opens, so several CPU cores read queries in parallel. 0 (default) opens one per CPU (GOMAXPROCS), 1 a single socket. Uses SO_REUSEPORT on Linux, each socket with its own read loop; other systems always use one.
Allowed: at least 0.
host-only, restart
Environment: KUDURRU_LISTENERS__SOCKETS

store

KeyTypeDefaultScopeDescription
store.driverstringsqlitenodeStore driver. Where kudurru keeps its objects (zones, lists, policies, users): sqlite, a database file under node.dataDir, for Docker and single servers; kube, Kubernetes custom resources, which the Helm chart sets.
Allowed: one of sqlite | kube.
host-only, restart
Environment: KUDURRU_STORE__DRIVER
store.kube.startupTimeoutduration15snodeKubernetes sync timeout. Kubernetes only: how long to wait at start for the apiserver to deliver the objects, e.g. 15s. After that the node answers from its last saved copy (local.db) until the apiserver catches up.
host-only, restart
Environment: KUDURRU_STORE__KUBE__STARTUPTIMEOUT
store.kube.snapshot.maxAgeduration24hnodeSnapshot maximum age. Kubernetes only: the oldest saved copy (local.db) the node may answer its own zones from while the apiserver is unreachable, e.g. 24h. An older copy is not trusted: the node reports not ready and its primary zones answer SERVFAIL.
host-only, restart
Environment: KUDURRU_STORE__KUBE__SNAPSHOT__MAXAGE

upstreams

KeyTypeDefaultScopeDescription
upstreams.groupsmap{default: {servers: ["1.1.1.1:53", "9.9.9.9:53"], strategy: parallel_best, timeout: 2s}}clusterUpstream groups. The DNS servers kudurru asks for names it does not answer itself, in named groups; the group named default gets every query nothing else routes. Each group lists servers (e.g. 1.1.1.1, tls://dns.quad9.net), a strategy that picks among them and a timeout per server. Conditional forwarding, forward zones and policies send queries to other groups by name.
Allowed: values: {servers (required): list of string (format upstream); strategy: string (one of parallel_best | strict | random | rtt_weighted, default parallel_best); timeout: duration (default 2s)}.
Environment: file only.
upstreams.bootstrapstring[]["1.1.1.1:53"]clusterBootstrap resolvers. Plain DNS servers used only to look up the host names of upstream servers, such as dns.quad9.net in tls://dns.quad9.net, e.g. 1.1.1.1:53 or 9.9.9.9. Never asked for client queries.
Allowed: items: string (format upstream).
Environment: KUDURRU_UPSTREAMS__BOOTSTRAP
upstreams.conditionalmap{}clusterConditional forwarding. Send queries for some domains to a chosen upstream group, e.g. home.lan to a group holding your router, so local device names resolve. Each entry maps a domain suffix to a group name; the longest matching suffix wins, and a key may list several suffixes in dnsmasq form, "[/corp.example/10.in-addr.arpa/]". These answers are never cached.
Allowed: values: string (ref upstreamGroup).
Environment: file only.
upstreams.ecs.modestringstripclusterEDNS Client Subnet. Whether upstream servers learn which network a query came from (EDNS Client Subnet). strip (default) never tells them, the private choice; forward and add can get answers from servers nearer the client, at the cost of revealing part of its address.
Allowed: one of strip | forward | add.
Environment: KUDURRU_UPSTREAMS__ECS__MODE
upstreams.ecs.ipv4Prefixint24clusterECS IPv4 prefix length. How much of an IPv4 client address EDNS Client Subnet may reveal, as a prefix length from 0 to 32: with 24 (default) only the network, e.g. 203.0.113.0/24, is sent. Used only when upstreams.ecs.mode is forward or add.
Allowed: range 0-32.
Environment: KUDURRU_UPSTREAMS__ECS__IPV4PREFIX
upstreams.ecs.ipv6Prefixint56clusterECS IPv6 prefix length. How much of an IPv6 client address EDNS Client Subnet may reveal, as a prefix length from 0 to 128: with 56 (default) only the /56 network is sent. Used only when upstreams.ecs.mode is forward or add.
Allowed: range 0-128.
Environment: KUDURRU_UPSTREAMS__ECS__IPV6PREFIX

cache

KeyTypeDefaultScopeDescription
cache.maxEntriesint200000clusterMaximum cache entries. How many answers the cache holds, e.g. 200000; when it is full the least recently used answer is dropped. More entries mean more cache hits and more memory; 0 turns the cache off.
Allowed: at least 0.
Environment: KUDURRU_CACHE__MAXENTRIES
cache.minTTLduration5sclusterMinimum TTL. Shortest time an answer stays cached, even when its record asks for less, e.g. 5s. Raising it saves upstream queries but keeps changed addresses longer; 0 honors every record's TTL.
Environment: KUDURRU_CACHE__MINTTL
cache.maxTTLduration24hclusterMaximum TTL. Longest time an answer stays cached, even when its record asks for more, e.g. 24h; lower it so changed addresses show up sooner. 0 sets no upper limit.
Environment: KUDURRU_CACHE__MAXTTL
cache.negativeTTLduration5mclusterMaximum negative TTL. Longest time a "does not exist" answer (NXDOMAIN or NODATA) stays cached, e.g. 5m; shorter lets a newly created name resolve sooner. Below it the zone's SOA minimum applies (RFC 2308).
Environment: KUDURRU_CACHE__NEGATIVETTL
cache.prefetch.thresholdint5clusterPrefetch threshold (hits). Refresh popular answers shortly before they expire, so nobody waits for the upstream: an answer asked for this many times within cache.prefetch.window counts as popular, e.g. 5. 0 turns prefetching off.
Allowed: at least 0.
Environment: KUDURRU_CACHE__PREFETCH__THRESHOLD
cache.prefetch.windowduration2hclusterPrefetch window. Time over which the hits for cache.prefetch.threshold are counted, e.g. 2h.
Environment: KUDURRU_CACHE__PREFETCH__WINDOW
cache.serveStale.ttlduration72hclusterServe stale for. When every upstream server fails, keep answering from expired cache entries for up to this long after they expired, e.g. 72h, so names you already use keep working through an outage. 0 turns it off (RFC 8767 serve-stale).
Environment: KUDURRU_CACHE__SERVESTALE__TTL
cache.serveStale.answerTTLduration30sclusterStale answer TTL. TTL given to an expired answer served this way, e.g. 30s, so clients ask again soon; after a failed refresh, queries for the entry also stop waiting for the upstream for this long. 0 means 30s (RFC 8767).
Environment: KUDURRU_CACHE__SERVESTALE__ANSWERTTL
cache.serveStale.maxWaitduration1800msclusterStale answer wait. How long a query for an expired entry waits for a fresh upstream answer before the expired one is sent, e.g. 1800ms; 0 means 1800ms (RFC 8767 client response timer).
Environment: KUDURRU_CACHE__SERVESTALE__MAXWAIT

blocking

KeyTypeDefaultScopeDescription
blocking.enabledbooltrueclusterBlocking enabled. Block ads, trackers and other listed names for the whole network; off answers every query unfiltered. To pause blocking for a while, use the temporary disable on the dashboard instead (a runtime toggle, not this key). Applies to forwarded queries, not to your own zones.
Environment: KUDURRU_BLOCKING__ENABLED
blocking.defaultResponsestringzeroIPclusterDefault block response. What a blocked query is answered with, unless the client's Policy (blockMode) or the BlockList (response) chooses otherwise. zeroIP (default) makes the connection fail at once and suits most networks; blockPage shows browsers a page that explains the block; refused is discouraged, because devices then ask another DNS server.
Allowed: one of zeroIP | nxdomain | customIP | refused | blockPage.
Environment: KUDURRU_BLOCKING__DEFAULTRESPONSE
blocking.blockTTLduration10mclusterBlock response TTL. How long devices may remember a block answer, e.g. 10m, unless the Policy sets blockTTL. Keep it short: after you allow a name, devices keep the cached block until it expires.
Environment: KUDURRU_BLOCKING__BLOCKTTL
blocking.allowDegradedbooltrueclusterServe while lists load. Answer queries and report ready while blocklists are still loading, e.g. right after a restart; queries are filtered by the lists already loaded. Off, the node reports not ready until every referenced list is loaded, so load balancers keep traffic away.
restart
Environment: KUDURRU_BLOCKING__ALLOWDEGRADED
blocking.customIP.v4string(empty)clusterCustom block IPv4. IPv4 address blocked A queries are answered with when the block response is customIP, e.g. 192.168.1.10 for a server of yours that shows a notice. Empty answers NODATA.
Allowed: format ipv4.
Environment: KUDURRU_BLOCKING__CUSTOMIP__V4
blocking.customIP.v6string(empty)clusterCustom block IPv6. IPv6 address blocked AAAA queries are answered with when the block response is customIP, e.g. fd00::10. Empty answers NODATA.
Allowed: format ipv6.
Environment: KUDURRU_BLOCKING__CUSTOMIP__V6
blocking.refreshIntervalduration24hclusterList refresh interval. How often blocklists are downloaded again when a list sets no refreshInterval of its own, e.g. 24h; at least 1m. In a cluster the leader downloads and the other nodes copy its result by digest.
restart
Environment: KUDURRU_BLOCKING__REFRESHINTERVAL
blocking.initStrategystringfastclusterList loading at start. What happens at start while blocklists load: fast (default) serves at once using the copies saved on disk; blocking waits until every list is loaded; failOnError also exits when a list cannot be loaded.
Allowed: one of fast | blocking | failOnError.
restart
Environment: KUDURRU_BLOCKING__INITSTRATEGY
blocking.listsDirstring(empty)nodeList cache directory. Directory where downloaded blocklists are saved so the next start can use them at once. Empty uses <node.dataDir>/lists. Files are named by the sha256 of the list URL.
host-only, restart
Environment: KUDURRU_BLOCKING__LISTSDIR
blocking.allowLocalListURLsboolfalsenodeAllow local list URLs. Allow blocklist URLs that point at this machine or at special addresses: loopback, link-local (169.254.0.0/16, fe80::/10, where cloud metadata services live), unspecified and multicast. Off by default so whoever can add a BlockList cannot make the node fetch internal services (SSRF). Lists on private LAN addresses such as 192.168.1.5 are always allowed.
host-only, restart
Environment: KUDURRU_BLOCKING__ALLOWLOCALLISTURLS
blocking.catalogue.urlstring(empty)clusterCatalogue URL. Leave empty to use the blocked-services and safe-search catalogue built into kudurru. A URL, e.g. https://example.com/kudurru-catalogue.json, replaces it with your own document (schema version 2, or 1, docs/deploy/blocked-services.md). The leader downloads it and the other nodes copy the leader's; a document that fails validation leaves the current catalogue in service.
Allowed: format url.
restart
Environment: KUDURRU_BLOCKING__CATALOGUE__URL
blocking.catalogue.adguardServicesURLstring(empty)clusterAdGuard services URL (GPL-3.0, opt-in). Off when empty. Opt-in: a URL of AdGuard's HostlistsRegistry blocked-services file, e.g. https://raw.githubusercontent.com/AdguardTeam/HostlistsRegistry/main/assets/services.json, adds its services (about 140) to the catalogue next to kudurru's own; kudurru's entries win when both have an ID. That data is GPL-3.0 licensed: kudurru does not ship it, your instance downloads it (the leader, on the catalogue refresh interval; the other nodes copy the leader's), and the dashboard marks those services as from AdGuard (GPL-3.0). Check that the licence suits how you use and share the data. docs/deploy/blocked-services.md.
Allowed: format url.
restart
Environment: KUDURRU_BLOCKING__CATALOGUE__ADGUARDSERVICESURL
blocking.catalogue.refreshIntervalduration24hclusterCatalogue refresh interval. How often the catalogue at blocking.catalogue.url and the AdGuard services at blocking.catalogue.adguardServicesURL are checked for a new version, e.g. 24h; at least 1m. A conditional GET, so an unchanged document is not downloaded again.
restart
Environment: KUDURRU_BLOCKING__CATALOGUE__REFRESHINTERVAL

blockPage

KeyTypeDefaultScopeDescription
blockPage.enabledboolfalseclusterBlock page enabled. Show browsers a page that explains why a site was blocked, instead of a connection error. Turning it on starts the block page listener (listeners.blockPage) and allows the blockPage block response; choose that response in blocking.defaultResponse or a Policy. Blocked A and AAAA queries then point at blockPage.ipv4 and blockPage.ipv6; other types get NODATA.
restart
Environment: KUDURRU_BLOCKPAGE__ENABLED
blockPage.ipv4string(empty)clusterBlock page IPv4 address. Address browsers are sent to for the block page, e.g. 192.168.1.10: this server's LAN address, or a VIP or load balancer in front of the block page. Blocked A queries are answered with it under the blockPage response. Empty picks the first specific address of listeners.blockPage, else of listeners.dns; when every listener binds all addresses (0.0.0.0, ::) there is none, and blockPage.enabled is refused.
Allowed: format ipv4.
Environment: KUDURRU_BLOCKPAGE__IPV4
blockPage.ipv6string(empty)clusterBlock page IPv6 address. IPv6 address browsers are sent to for the block page, e.g. fd00::10; blocked AAAA queries are answered with it under the blockPage response. When blockPage.ipv4 is empty too, one is picked from the listeners as for IPv4; without an IPv6 address, AAAA queries get NODATA and browsers use IPv4.
Allowed: format ipv6.
Environment: KUDURRU_BLOCKPAGE__IPV6
blockPage.titlestringThis site is blockedclusterPage title. Heading of the block page, e.g. This site is blocked (plain text, at most 200 characters).
Environment: KUDURRU_BLOCKPAGE__TITLE
blockPage.messagestringThe administrator of this network has blocked access to this domain.clusterPage message. Text under the heading that tells people why the site is blocked and what they can do (plain text, at most 2000 characters; HTML is shown as text).
Environment: KUDURRU_BLOCKPAGE__MESSAGE
blockPage.contactstring(empty)clusterAdministrator contact. How people reach you, shown as "Ask your administrator": an email address, e.g. admin@home.example, or a link, e.g. https://wiki.home.example/dns. Empty leaves the line out.
Environment: KUDURRU_BLOCKPAGE__CONTACT
blockPage.showDetailsbooltrueclusterShow block details. Show what blocked the name (the list, rule, blocked service, safe search or policy), looked up in the recent query log. Off shows only the domain, the client, the node and the time, which keeps your list choices private.
Environment: KUDURRU_BLOCKPAGE__SHOWDETAILS
blockPage.dashboardURLstring(empty)clusterDashboard URL. Address of the dashboard the block page links administrators to, e.g. https://192.168.1.10:8080; the link opens /queries?domain=... there. Empty builds it from the block page address and listeners.api; set it when the dashboard is reached under another name or through a reverse proxy.
Allowed: format url.
Environment: KUDURRU_BLOCKPAGE__DASHBOARDURL
blockPage.rateLimitint120clusterRequests per minute per client. Most block page requests one client address may make per minute, e.g. 120, with bursts up to the same number; beyond it the page answers 429 Too Many Requests. 0 removes the limit.
Allowed: range 0-100000.
Environment: KUDURRU_BLOCKPAGE__RATELIMIT

unblockRequests

KeyTypeDefaultScopeDescription
unblockRequests.enabledbooltrueclusterUnblock requests. Let people ask for a blocked site to be unblocked: the block page shows a Request access form, and requests appear on the dashboard's Requests page for an administrator to approve or deny. Needs blockPage.enabled.
Environment: KUDURRU_UNBLOCKREQUESTS__ENABLED
unblockRequests.rateLimitint5clusterUnblock requests per hour per client. Most unblock requests one client may send per hour, e.g. 5; IPv6 clients are counted per /64. Asking again for the same name updates the pending request instead of adding one.
Allowed: range 1-100.
Environment: KUDURRU_UNBLOCKREQUESTS__RATELIMIT
unblockRequests.maxPendingint1000clusterMost pending unblock requests. Most undecided unblock requests kept across the cluster, e.g. 1000. When reached, the block page says requests are paused until an administrator decides some.
Allowed: range 1-10000.
Environment: KUDURRU_UNBLOCKREQUESTS__MAXPENDING
unblockRequests.pendingExpiryduration336hclusterPending requests expire after. Undecided unblock requests expire after this long, e.g. 336h (14 days); at least 1h.
Environment: KUDURRU_UNBLOCKREQUESTS__PENDINGEXPIRY
unblockRequests.retentionduration720hclusterKeep decided requests for. How long decided and expired unblock requests stay on the Requests page, counted from the decision, e.g. 720h (30 days); at least 1h. A time-limited approval is kept until it ends.
Environment: KUDURRU_UNBLOCKREQUESTS__RETENTION

querylog

KeyTypeDefaultScopeDescription
querylog.enabledbooltrueclusterQuery log enabled. Record the queries each node answers, for the dashboard's query log, statistics and charts; off records nothing and those pages stay empty. Each node keeps its own log (querylog.db with rollups, the stats API).
restart
Environment: KUDURRU_QUERYLOG__ENABLED
querylog.sinkstringsqliteclusterQuery log sink. Where queries are recorded: sqlite (default) keeps them in a database the dashboard reads; slog writes one JSON log line per query and stores nothing, for an external log system.
Allowed: one of sqlite | slog.
restart
Environment: KUDURRU_QUERYLOG__SINK
querylog.privacystringfullclusterQuery log privacy. How much of each query is kept: full keeps everything; the other modes hide client addresses, query names or both, and nothing keeps only per-minute totals. Applied once when the query is recorded, so it cannot be undone for queries already logged. Only full stores the answer summary.
Allowed: one of full | anonymize_client | hide_domains | hide_domains_clients | nothing.
Environment: KUDURRU_QUERYLOG__PRIVACY
querylog.anonymizestringprefixclusterClient anonymization. How client addresses are hidden under the anonymize_client privacy mode: prefix keeps only the network (IPv4 /24, IPv6 /48), so devices in one network look alike; pseudonym replaces each address with a code that is stable for a day and cannot be reversed.
Allowed: one of prefix | pseudonym.
Environment: KUDURRU_QUERYLOG__ANONYMIZE
querylog.retention.rawduration720hclusterKeep query rows for. How long individual queries are kept, e.g. 720h (30 days); 0 keeps them until querylog.retention.maxDBBytes is reached. The totals behind the charts are kept longer: per minute for 35 days, per hour for 400 days, per day forever.
Environment: KUDURRU_QUERYLOG__RETENTION__RAW
querylog.retention.maxDBBytesbytes10GiBclusterMaximum query log size. Largest size of the query log database on each node, e.g. 10GiB; when it is exceeded the oldest queries are deleted first. 0 removes the cap.
Environment: KUDURRU_QUERYLOG__RETENTION__MAXDBBYTES
querylog.exclude.domainsstring[][]clusterDomains never logged. Names that are never logged, e.g. *.home.arpa or connectivitycheck.gstatic.com: exact names, *.suffix for a domain and everything below it, or /regex/.
Environment: KUDURRU_QUERYLOG__EXCLUDE__DOMAINS
querylog.exclude.clientsstring[][]clusterClients never logged. Clients whose queries are never logged, e.g. 192.168.1.20, 10.0.0.0/8 or a client name such as nas: IP addresses, CIDR prefixes or client names.
Environment: KUDURRU_QUERYLOG__EXCLUDE__CLIENTS
querylog.exclude.sudnbooltrueclusterSkip special-use names. Leave out names that never leave the local network, which keeps local discovery noise out of the log: localhost, .local, .home.arpa and private and link-local reverse zones (special-use names, RFC 6761).
Environment: KUDURRU_QUERYLOG__EXCLUDE__SUDN

ratelimit

KeyTypeDefaultScopeDescription
ratelimit.enabledbooltrueclusterRate limiting enabled. Limit how many queries per minute one address or network may send over UDP, which blunts floods and reflection attacks. TCP, DoT, DoH, DoQ and DoH3 are never limited, nor loopback clients and the health name.
Environment: KUDURRU_RATELIMIT__ENABLED
ratelimit.ipv4map{24: 6000, 32: 600}clusterIPv4 limits (queries per minute). Queries per minute allowed per IPv4 address or network, by prefix length: 32 is one address (default 600), 24 a /24 network (default 6000). Entries merge with the defaults key by key, and 0 turns a level off. Each prefix has a token bucket holding one minute's allowance.
Allowed: keys: int (range 0-32); values: int (at least 0).
Environment: file only.
ratelimit.ipv6map{128: 600, 56: 12000, 64: 6000}clusterIPv6 limits (queries per minute). Queries per minute allowed per IPv6 address or network, by prefix length: 128 is one address (default 600), 64 a /64 network (6000), 56 a /56 (12000). Entries merge with the defaults key by key, and 0 turns a level off.
Allowed: keys: int (range 0-128); values: int (at least 0).
Environment: file only.
ratelimit.slipint50clusterSlip (percent answered TC=1). Share of over-limit queries, in percent from 0 to 100, answered with an empty truncated reply (TC=1) instead of being dropped, e.g. 50; real clients then retry over TCP, which is not limited. 0 drops them all, 100 answers them all.
Allowed: range 0-100.
Environment: KUDURRU_RATELIMIT__SLIP
ratelimit.maxBucketsint65536clusterTracked prefixes. Most addresses and networks tracked at once, e.g. 65536 (0 means 65536); the least recently seen is forgotten first and starts again with a full allowance.
Allowed: at least 0.
Environment: KUDURRU_RATELIMIT__MAXBUCKETS
ratelimit.exemptstring[][]clusterExempt clients. Addresses or networks that are never rate limited, e.g. 192.168.1.0/24 for your LAN or the address of a busy resolver that forwards to kudurru. Loopback always is.
Allowed: items: string (format cidr).
Environment: KUDURRU_RATELIMIT__EXEMPT

Top-level keys

KeyTypeDefaultScopeDescription
objectslist[]nodeInline objects. Zones, lists, groups and policies declared in this file, in the form of Kubernetes manifests (apiVersion, kind, metadata, spec). They are re-applied on every start; edit them here, since they are managed by file:kudurru.yaml and the dashboard cannot change them.
Allowed: items: {apiVersion: string; kind: string; metadata: object; spec: object}.
host-only, restart
Environment: file only.
settingsFilestring(empty)nodeSettings file. Path of a file for the cluster-wide settings, e.g. /etc/kudurru/settings.yaml, read on top of this file and watched, so edits apply without a restart. In Docker, changes made on the Settings page are saved into it; without one they are lost on restart. KUDURRU_SETTINGS_FILE takes precedence, and the Helm chart mounts /etc/kudurru/settings/settings.yaml. Empty turns the layer off (D25).
host-only, restart
Environment: KUDURRU_SETTINGSFILE
objectsDirstring/etc/kudurru/objects.dnodeObjects directory. Directory of *.yaml files with zones, lists, groups and policies, e.g. /etc/kudurru/objects.d. It is watched: adding or editing a file applies it, and deleting a document deletes the object. The objects are managed by file:<dir>/<name>, so the dashboard cannot change them.
host-only, restart
Environment: KUDURRU_OBJECTSDIR
tsigKeyslistnodeTSIG keys. Shared keys that authenticate zone transfers and NOTIFY between kudurru and other DNS servers; only needed when you transfer zones. Each key has a name, an algorithm and a file holding the secret, and a zone lists the keys it accepts in spec.policy.transferTSIGKeyRefs (TSIG, RFC 8945).
Allowed: items: {name (required): string (format domain); algorithm (required): string (one of hmac-sha256 | hmac-sha512); secretFile (required): string}.
host-only, restart, write-only
Environment: file only.

notify

KeyTypeDefaultScopeDescription
notify.sourcestring(empty)nodeNOTIFY source address. Local IP address that NOTIFY messages to secondary servers are sent from, e.g. 192.168.1.10; empty lets the system choose. Secondaries accept NOTIFY only from an address they list as a primary.
Allowed: format ip.
host-only, restart
Environment: KUDURRU_NOTIFY__SOURCE

externalDNS

KeyTypeDefaultScopeDescription
externalDNS.webhook.enabledboolfalsenodeexternal-dns webhook enabled. Let external-dns, the Kubernetes tool, publish DNS records into kudurru through its webhook provider protocol (GET /, GET and POST /records, POST /adjustendpoints), served on externalDNS.webhook.listen.
host-only, restart
Environment: KUDURRU_EXTERNALDNS__WEBHOOK__ENABLED
externalDNS.webhook.listenstring127.0.0.1:8888nodeexternal-dns webhook address. Address and port of the external-dns webhook, e.g. 127.0.0.1:8888. It has no authentication, so the default answers only on this machine (external-dns as a sidecar or on the same host); a wider address lets every client that reaches it write records. The Helm chart renders :<port> behind its NetworkPolicy.
Allowed: format hostport.
host-only, restart
Environment: KUDURRU_EXTERNALDNS__WEBHOOK__LISTEN
externalDNS.webhook.namespacestringexternal-dnsnodeexternal-dns record namespace. Namespace the record sets from external-dns are stored in, e.g. external-dns: one DNSRecordSet per DNS name, managed by integration:external-dns.
host-only, restart
Environment: KUDURRU_EXTERNALDNS__WEBHOOK__NAMESPACE
externalDNS.webhook.zoneSelectormap{}nodeexternal-dns zone selector. Labels a primary zone must carry for external-dns to write into it, e.g. external-dns: enabled (a label selector, key: value); empty allows every primary zone.
host-only, restart
Environment: file only.

auth

KeyTypeDefaultScopeDescription
auth.localLoginstringenabledclusterLocal password login. Whether people can sign in to the dashboard with a username and password: enabled for everyone, admins-only for builtin admins, or disabled (identity providers only). kudurru admin on the server's admin socket always works, so you cannot lock yourself out. Gates POST /api/v1/auth/login.
Allowed: one of enabled | admins-only | disabled.
restart
Environment: KUDURRU_AUTH__LOCALLOGIN
auth.bootstrap.adminPasswordFilestring(empty)nodeFirst-boot admin password file. Path of a file holding the password of the first admin account, created on the first start, e.g. /run/secrets/kudurru-admin-password. The KUDURRU_ADMIN_PASSWORD and KUDURRU_ADMIN_PASSWORD_FILE environment variables take precedence; with none of them, a password is generated and printed once.
host-only, restart
Environment: KUDURRU_AUTH__BOOTSTRAP__ADMINPASSWORDFILE
auth.session.idleTimeoutduration30mclusterSession idle timeout. Sign a dashboard user out after this long without activity, e.g. 30m; at least 1m. Every request starts the time again.
restart
Environment: KUDURRU_AUTH__SESSION__IDLETIMEOUT
auth.session.lifetimeduration12hclusterSession lifetime. Longest a dashboard sign-in lasts however active it is, e.g. 12h; then the user signs in again and their roles are looked up afresh. At least auth.session.idleTimeout.
restart
Environment: KUDURRU_AUTH__SESSION__LIFETIME
auth.session.securestringautoclusterSecure session cookie. Whether browsers send the session cookie only over HTTPS (the cookie's Secure attribute). auto (default) decides per request and suits most setups; always is for a proxy that terminates TLS without telling kudurru; never is only for plain HTTP on a network you trust.
Allowed: one of auto | always | never.
restart
Environment: KUDURRU_AUTH__SESSION__SECURE
auth.tokens.maxLifetimeduration0sclusterMaximum token lifetime. Longest lifetime of a new API token, e.g. 2160h (90 days); a token created without an expiry gets this one. 0 (default) allows tokens that never expire.
restart
Environment: KUDURRU_AUTH__TOKENS__MAXLIFETIME
auth.identityProviderslist[]clusterIdentity providers. Sign-in to the dashboard through an OpenID Connect provider such as Google, Microsoft Entra ID, Keycloak, Authentik or Dex; the provider's ID tokens also work as bearer tokens for the API. Each entry needs an id, issuer, clientID, clientSecretFile and redirectURL; scopes, claimValidationRules, claimMappings and provisioning are optional.
Allowed: items: {id (required): string; displayName: string; issuer: string (format url); clientID: string; clientSecretFile: string; cliClientID: string; redirectURL: string (format url); scopes: list of string; audiences: list of string; claimValidationRules: list of object; claimMappings: object; provisioning: object; allowedDomains: list of string (format domain)}.
host-only, restart
Environment: file only.
auth.kubernetes.modestringsarclusterKubernetes authorization. How Kubernetes service-account tokens are authorized: sar asks the apiserver with a SubjectAccessReview on kudurru.io resources. impersonate is planned for v0.2.
Allowed: one of sar.
host-only, restart
Environment: KUDURRU_AUTH__KUBERNETES__MODE
auth.adminSocket.pathstring/run/kudurru/kudurru.socknodeAdmin socket path. Path of the local socket of the kudurru admin command (reset-password, create-token, list-users), e.g. /run/kudurru/kudurru.sock: the way back in when every admin password is lost. Only uid 0, the server's own user and auth.adminSocket.group may use it. Empty turns it off.
host-only, restart
Environment: KUDURRU_AUTH__ADMINSOCKET__PATH
auth.adminSocket.groupint-1nodeAdmin socket group (gid). Group id (gid) whose members may also use the admin socket, e.g. 1000; -1 (default) allows only root and the server's own user.
Allowed: at least -1.
host-only, restart
Environment: KUDURRU_AUTH__ADMINSOCKET__GROUP

rbac

KeyTypeDefaultScopeDescription
rbac.bindingslist[]clusterRole bindings. Who may do what: each binding grants a role (viewer, auditor, operator, zone-admin or admin) to users, groups or service accounts, for the whole cluster or one zone. RoleBinding objects add to these.
Allowed: items: {role (required): string (one of viewer | auditor | operator | zone-admin | admin); scope: {kind: string (one of cluster | zone | zonegroup | clientgroup, default cluster); name: string}; subjects (required): list of {kind (required): string (one of User | Group | ServiceAccount); name (required): string; namespace: string}}.
host-only, restart
Environment: file only.

log

KeyTypeDefaultScopeDescription
log.levelstringinfonodeLog level. How much the server logs: info (default) for normal operation, debug for troubleshooting, warn or error for less. Per-component levels follow a comma, e.g. info,dns=debug.
Allowed: one of debug | info | warn | error.
Environment: KUDURRU_LOG__LEVEL
log.formatstringjsonnodeLog format. Format of the log lines: json (default) for log collectors, text for reading in a terminal.
Allowed: one of json | text.
restart
Environment: KUDURRU_LOG__FORMAT

Source: site/content/reference/configuration.md