Skip to content

Configuration reference

Ward reads one YAML file, validates every field at startup, and refuses to run on a bad value. The error names the field and the fix:

config validation error: field "upstreams[0].server_name": must not be empty — set to the TLS SNI name of the upstream, e.g. "dns.quad9.net"

Unknown keys are ignored, so check spelling: a misspelled blocklists runs Ward with no blocklist.

The first match wins:

  1. --config <path>
  2. $XDG_CONFIG_HOME/protocol-ward/ward.yaml (~/.config/protocol-ward/ward.yaml when XDG_CONFIG_HOME is unset)
  3. /etc/protocolward/ward.yaml

If you pass --config, Ward uses that path and does not fall back to the others.

listen: "127.0.0.1:5354"
log_level: "info"
upstreams:
- address: "9.9.9.9:853"
server_name: "dns.quad9.net"
- address: "1.1.1.1:853"
server_name: "one.one.one.one"
blocklists:
- id: "trackers"
path: "./blocklists/trackers.txt"
allowlists:
- id: "my-overrides"
path: "./allowlists/overrides.txt"
decoys:
- id: "home-decoys"
path: "./decoys.txt"
block_response:
mode: "address"
a: "0.0.0.0"
aaaa: "::"
timeouts:
dial: "3s"
query: "2s"
shutdown: "5s"
model:
builtin: lexical

Required. A string of the form host:port, with the port between 1 and 65535. There is no default; the bind address is always your choice. Use 127.0.0.1:5354 to try Ward locally (5353 belongs to mDNS on macOS) and :53 to serve a network, which needs privileges to bind.

One of debug, info, warn, error. Default info.

Required, at least one. DNS-over-TLS resolvers that Ward forwards to.

Key Type Notes
address host:port Required. DoT is usually port 853.
server_name string Required. The name on the upstream’s TLS certificate (SNI).
ca_bundle path Optional. A PEM file of CA certificates that pins trust for this upstream. Omit it to use system roots. Ward reads and checks the file at startup.

Optional. Omit the key to run Ward as a plain forwarder. If the key is present it must have at least one entry.

Key Type Notes
id string Required and unique. Appears in every block log line.
path path Required. A local file of hosts-file lines (0.0.0.0, 127.0.0.1 or AdAway 0 prefix) or AdGuard ||host^ rules. Plain one-name-per-line files are not accepted; skipped lines are counted at load. Relative paths resolve against the directory you run ward from.

Optional, with the same shape and rules as blocklists. An allowlist hit beats a blocklist hit.

Optional, with the same shape and rules as blocklists. Files hold hosts-file lines or plain hostnames, and each entry matches that exact name only, not names beneath it. Decoys are checked before everything else, are never forwarded, and are never included in ward config export. See Decoys.

Optional. Shapes the answer to a blocked query.

Key Default Notes
mode address address or nxdomain.
a 0.0.0.0 IPv4 returned for A queries in address mode. Must be omitted in nxdomain mode.
aaaa :: IPv6 returned for AAAA queries in address mode. Must be a real IPv6 address, not IPv4-mapped. Must be omitted in nxdomain mode.

In address mode, query types other than A and AAAA get NXDOMAIN. In nxdomain mode every type does.

Durations in Go syntax (500ms, 3s). Every value must be greater than zero.

Key Default Meaning
dial 3s TCP connect plus TLS handshake to an upstream.
query 2s End-to-end budget for one forwarded query.
shutdown 5s How long Ward drains in-flight queries on SIGTERM.

Optional. Omit it to run without a behavioral detector. When model is present, set exactly one of builtin or command.

Key Type Notes
builtin string lexical runs Ward’s built-in hostname detector in-process. See Behavioral detector.
command list of strings argv for an external classifier that Ward runs as a separate sibling process and talks to over newline-delimited JSON on stdio.
env list of KEY=value Environment for command only; setting it together with builtin is a validation error. Omit it to inherit Ward’s environment; [] starts the classifier with an empty one.

Verdicts from either kind are recorded as flags and are never enforced in the beta.

  • Dashboard address. The dashboard serves on 127.0.0.1:18987. Override it with ward serve --dash-listen-addr. Only loopback addresses are accepted, because the dashboard shows your DNS activity.
Command What it does
ward serve Run the resolver and dashboard.
ward doctor Validate the config, check that the bind address is free, and check TCP reachability of each upstream.
ward config export Print the validated config as YAML, with decoys left out. The model stanza is not exported either, so re-add it if you restore from an export. ca_bundle paths are printed as written.
ward update verify <dir> Verify a signed update bundle on disk. No network access.
ward eval Score a labelled hostname suite against the configured classifier and report accuracy.
ward version Print the version.

Every command that reads config accepts --config <path>.