Configuration Reference
import { Aside } from ‘@astrojs/starlight/components’;
homeDNS reads a single YAML file passed via --config. Copy config.example.yaml from the repo root as your starting point.
server
Section titled “server”server: name: homeDNS # Display name — shown in web admin header data_dir: ./data # Base path for persistent storage (SQLite DB, etc.)listeners
Section titled “listeners”Configures DNS wire-protocol endpoints. Each listener is independently toggled.
listeners: do53: enabled: true bind: ":53" # Standard DNS; use ":5353" in dev to avoid privilege issues
dot: enabled: false bind: ":853" # DNS over TLS — requires tls.cert_file + tls.key_file
doh: enabled: false bind: ":8443" # DNS over HTTPS path: /dns-query # RFC 8484 pathShared TLS material used by the DoT and DoH listeners.
tls: cert_file: ".certs/server.crt" key_file: ".certs/server.key"See Quick Start for generating a self-signed dev cert.
recursion
Section titled “recursion”Controls the iterative recursive resolver built into dnsd.
recursion: enabled: true max_queries: 50 # Max upstream queries per resolution query_timeout: 3s # Per-query timeout max_cname_hops: 8 # CNAME chain limit (prevents infinite loops) dnssec_validate: false # Enable DNSSEC validation (stub — full validation is v2)forwarding
Section titled “forwarding”Global defaults and health-probe settings for the forwarding engine.
forwarding: default_strategy: failover # fallback strategy when no per-forwarder strategy is set
health: slow_above: 20ms # Upstream classified as "slow" above this RTT failing_after: 3 # Consecutive probe failures before "failing" status probe_interval: 30s # Time between latency probes probe_timeout: 2s # Individual probe timeout sample_window: 3 # Rolling window size for RTT averagesHealth states: healthy (avg < slow_above, no errors), slow (avg ≥ slow_above), failing (last failing_after probes all errored). Failing upstreams are skipped by all strategies.
forwarders
Section titled “forwarders”Optional static upstream list. When forwarders.servers is populated, the runtime uses this YAML list. When empty, the runtime falls back to API-stored conditional forwarders created via the web UI or dnsctl.
forwarders: contact_method: sequential # How to pick from the server list (see below) servers: - id: cloudflare-dot protocol: dot # do53 | dot | doh | doq | doh3 address: "1.1.1.1:853" dnssec: true # Forward DNSSEC DO-bit timeout: 2s skip_tls_verify: false # Do not skip in production
- id: cloudflare-doh protocol: doh address: "https://1.1.1.1/dns-query" dnssec: true timeout: 3scontact_method
Section titled “contact_method”| Value | Behaviour |
|---|---|
sequential | Try servers in order; fall back on failure |
parallel_first | Race all servers; first valid response wins |
lowest_latency | Prefer server with lowest observed probe latency |
filter
Section titled “filter”DNS blocking engine (Pi-hole / AdGuard Home style).
filter: enabled: true default_refresh: 24h # Interval between automatic list re-downloads
action: nxdomain # Block action: nxdomain | nodata | sinkhole | refused | custom_cname
sinkhole_ipv4: 0.0.0.0 # Returned for A queries when action is "sinkhole" sinkhole_ipv6: "::" # Returned for AAAA queries when action is "sinkhole" sinkhole_ttl: 60 # TTL of sinkhole responsesList sources (URLs, formats) are managed via the Filter page in the web admin or dnsctl filter.
CIDR-based access control for DNS queries.
acl: allow_query: # Who may send any DNS query - 0.0.0.0/0 - ::/0
allow_recurse: # Who may trigger recursive resolution - 127.0.0.0/8 - ::1/128 - 10.0.0.0/8 - 192.168.0.0/16 - 172.16.0.0/12A separate deny list (acl.deny_query) can be defined to explicitly reject sources before the allow check.
rate_limit
Section titled “rate_limit”Token-bucket per-source-IP rate limiting on the DNS data plane.
rate_limit: enabled: true qps: 100 # Sustained queries per second per source IP burst: 200 # Burst allowance above QPS window: 1s # Token refill windowstorage
Section titled “storage”Persistence backend for zones, forwarders, filter sources, DDNS policies, and users.
storage: driver: memory # memory | sqlite path: "" # Path to SQLite DB file when driver is "sqlite"memory— all config is ephemeral; resets on restart. Good for testing.sqlite— data survives restarts. Uses WAL mode; safe to copy the.dbfile for backups while the daemon is running.
management
Section titled “management”The HTTP server that hosts the REST API and embedded web admin SPA.
management: enabled: true bind: 127.0.0.1:8080 # Address to listen on — loopback by default allow_cidrs: # Only these source IPs may reach the API - 127.0.0.0/8 - ::1/128 debug: false # When true, raw error details surface in the web UIGET /api/v1/version exposes the debug flag so the web UI can adjust its error display at bootstrap time.
logging
Section titled “logging”logging: level: info # debug | info | warn | error format: text # text (human-readable) | json (structured, recommended for prod)Hot reload
Section titled “Hot reload”homeDNS watches the config file for changes by default. Writing a new config in place triggers a reload without dropping active DNS connections. SIGHUP support is version-dependent — see docs/RUNBOOK.md for current behaviour.