Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Configuration

dns-rs reads one TOML file, named by the DNS_RS_CONFIG environment variable. Without it, the defaults below apply. Every section and key is optional, and an unknown key is an error. The commented reference is dns-rs.example.toml.

Durations use the humantime format (2s, 500ms, 24h).

Settings

KeyDefaultMeaning
server.listen0.0.0.0:53Address of the UDP and TCP listeners
http.listen0.0.0.0:8080Address of the HTTP endpoint for probes and metrics (see Metrics and probes)
http.api_token_filenoneFile holding the API token, for example a mounted Secret; DNS_RS_API_TOKEN wins over it. Without a token the API is disabled (see Authentication)
upstream.server1.1.1.1:53Resolver queries are forwarded to; the port defaults to 53
upstream.timeout2sUpstream timeout; must be greater than zero
cache.size10000Maximum number of cached responses
blocking.modenullnull, nxdomain, nodata or refused (see Blocklists)
blocking.refresh24hHow often blocklists are downloaded again; at least 1m
blocking.listsnoneBlocklist URLs, or { url = "...", wildcard = true } tables
blocking.denynoneDomains to block: example.com or *.example.com
blocking.allownoneSame syntax; always wins over deny, regexes and lists
blocking.deny_regexnoneRegexes of names to block
blocking.allow_regexnoneRegexes of names to allow
local.ttl300TTL of local answers, in seconds
local.recordsnone{ name = "nas.home.arpa", ip = "192.168.1.10" } tables answered by dns-rs itself (see Local records)
forwardnone[[forward]] tables with domain and server (see Conditional forwarding)

Local records

local.records lists names dns-rs answers itself, as Pi-hole’s local DNS does (ADR 0016):

[local]
ttl = 300
records = [
  { name = "nas.home.arpa", ip = "192.168.1.10" },
  { name = "nas.home.arpa", ip = "fd00::10" },
]
  • The address family gives the type: A for IPv4, AAAA for IPv6. A name may have several records; ANY returns all of them.
  • Names match exactly and without regard to case, and the answer keeps the name as the client wrote it. Subdomains of a local name are not local.
  • Local records are answered before blocking, so a list never blocks them. The answer is authoritative (AA) and uses neither the upstream nor the cache.
  • For a local name, other types (MX, TXT…) and a family with no record get an empty NOERROR, never NXDOMAIN.
  • These queries have the source local in the query log and the metrics.
  • Not supported yet: PTR (reverse lookups) and CNAME.

Conditional forwarding

A [[forward]] table sends a domain, and every name under it, to its own server instead of upstream.server, as Pi-hole’s conditional forwarding does (ADR 0017):

[[forward]]
domain = "home.arpa"
server = "192.168.1.1"   # port 53 unless given
  • Matching is label by label and case-insensitive, and a trailing dot is ignored: nas.home.arpa is forwarded, myhome.arpa is not. When domains nest, the longest one wins.
  • A domain may have a single label (lan). The root is rejected: use upstream.server for that.
  • Order: local records, then blocking, then the cache, then the forward server or the upstream. Blocking still applies to forwarded names.
  • The server is queried like the upstream (UDP with TCP fallback, same upstream.timeout). Answers are cached.
  • If the server fails, the client gets SERVFAIL. The query is not retried on the upstream, so private names do not leak.
  • A domain that is not a valid name, the root, or a domain given twice (ignoring case and trailing dot) is a configuration error.
  • Changing the rules empties the cache, so an NXDOMAIN cached before a rule existed does not hide it.
  • These queries have the source forwarded in the query log and the metrics. A failing server is reported as failed.

Environment variables

VariableOverrides
DNS_RS_CONFIGPath of the TOML file
DNS_RS_LISTENserver.listen
DNS_RS_HTTP_LISTENhttp.listen
DNS_RS_API_TOKENAPI token, at least 16 characters; wins over http.api_token_file. Not a TOML key: keep the token out of the file
DNS_RS_API_TOKEN_FILEhttp.api_token_file. The Helm chart uses it because it refuses [http] in the reloaded configuration
DNS_RS_UPSTREAMupstream.server
DNS_RS_UPSTREAM_TIMEOUT_MSupstream.timeout, in milliseconds
DNS_RS_CACHE_SIZEcache.size
RUST_LOGLog filter, default info (see Logging)

Overrides are applied after the file is read, at startup and on every reload.

Validation

dns-rs refuses to start on an invalid configuration. The error is one line naming the problem, with its position for TOML syntax errors, for example invalid config file: line 4, column 1: unknown field ....

Beyond syntax, these are rejected:

  • upstream.timeout of zero.
  • blocking.refresh under one minute.
  • A local.records name that is not a domain of at least two labels (the error names local.records), or an address that does not parse.
  • A deny or allow entry that is not a domain or *.domain.
  • A regex that does not compile, or a regex list whose patterns are too large together (each list is compiled as one set). The error names the key.
  • A list source that is not file:///absolute/path, https:// or http://, or an http(s):// URL that does not parse (for example https://exa mple.org).

Reload without restart

The file is read every 10 seconds, compared by content, and immediately on SIGHUP. Changes to [blocking], [local] and [[forward]] apply at once. Changes to server.listen, http.listen, http.api_token_file, upstream.server, upstream.timeout and cache.size need a restart: dns-rs logs configuration changes need a restart to apply once, listing the settings, and keeps the running values. An invalid or missing file is rejected (logged once as configuration rejected, keeping the previous one) and the previous configuration keeps working.

The API token file is read again at the same moments, so a rotated token applies without a restart (see Authentication).

Blocklists are downloaded again every blocking.refresh. On a configuration reload only lists that are new are downloaded; lists that were removed stop blocking. An invalid [local] rejects the whole file, so the previous records stay in force. See ADR 0010.