Configuration
Warden is a single-binary DNS filter configured by one plain-text TOML file. This page covers where Warden finds that file, the smallest config worth running, what changes apply without a restart, how to validate before you reload, and where the full field reference lives.
For installing the binary first, see the install guide. For every field and value, see the TOML reference.
Where Warden reads config
Warden resolves the config path in this order. The first existing file wins — there is no merging across paths.
- The
--configflag on the command line. /etc/purge-warden/config.toml(the standard system path)./var/lib/purge-warden/config.toml(older installs, kept for backward compatibility)../config.toml(the working directory, handy in development).
Pass an explicit path when you want to be sure:
warden --config /etc/purge-warden/config.toml start
Ask the running daemon which file it loaded:
warden status
Splitting one big file into many
For more than ~20 devices, split the config across a directory. The master file declares what to pull in:
schema_version = 1
[server]
listen = "0.0.0.0:53"
default_profile = "default"
allow_from = ["192.168.1.0/24", "127.0.0.0/8"]
includes = [
"devices.d/*.toml",
"profiles.d/*.toml",
"subnets.d/*.toml",
"groups.d/*.toml",
"blocklists.d/*.toml",
]A minimal config
The smallest config that protects a home LAN with the three default purge.cc lists:
schema_version = 1
[server]
listen = "0.0.0.0:53"
default_profile = "default"
allow_from = ["192.168.1.0/24", "127.0.0.0/8"]
[[blocklists]]
id = "privacy-ads"
url = "https://lists.purge.cc/privacy/ads.txt"
format = "domains"
[[blocklists]]
id = "privacy-tracking"
url = "https://lists.purge.cc/privacy/tracking.txt"
format = "domains"
[[blocklists]]
id = "security-malicious"
url = "https://lists.purge.cc/security/malicious.txt"
format = "domains"
[profiles.default]
display_name = "Home default"
blocklists = ["privacy-ads", "privacy-tracking", "security-malicious"]
block_response = "zero"warden config lint
sudo systemctl start purge-warden
Smoke test from any machine on the LAN:
dig @<warden-ip> google.com # normal answer
dig @<warden-ip> doubleclick.net # 0.0.0.0 — blocked
0.0.0.0, you must set allow_from. Warden refuses to start as an open resolver.Hot reload
Most config changes apply without restarting the daemon. After editing the file:
warden reload
Or send a signal directly:
sudo kill -SIGHUP $(pidof warden)
If you change config through a CLI mutation (warden device add, warden profile blocklists, warden blocklist add…), the reload happens automatically — nothing further to run.
Reloads cleanly, no downtime:
- adding or removing blocklists, profiles, devices, groups, subnets, schedules
- changing
default_profile,block_response, TTLs - editing
allow_from, upstream servers, upstream mode - toggling tracking, query log, local DNS records
Requires a full daemon restart:
- the
listenaddress or port (the socket has to be rebound) - the user or group the daemon runs as
schema_versionupgrades
If the new config fails validation, the reload is rejected and the previous config keeps serving traffic. The reason lands in the journal:
journalctl -u purge-warden -n 30
Validating the file
Always lint before reloading:
warden config lint
Exit codes:
- 0 — clean, safe to reload.
- 1 — errors, the daemon would reject this file.
The linter catches the mistakes that quietly break a setup:
- Unknown references. A device pointing at a missing profile id, a profile listing a blocklist that isn’t declared, a schedule targeting a group that doesn’t exist.
- Duplicate ids. Two
[[devices]]or[[blocklists]]sharing anid. - Malformed CIDRs.
192.168.1.0/33, missing prefix length, IPv4 written like IPv6. - Group priority collisions. A device that ends up in two groups with the same priority and different profiles — Warden can’t pick.
- Insecure secrets. A
secrets.tomlreadable by anyone other than the owner. - Open-resolver footgun.
listen = "0.0.0.0:53"with an emptyallow_from.
Preview what would change between the file on disk and the running daemon before you reload:
warden config diff --live
warden config validate still works as an alias of lint. New scripts should prefer lint.Common patterns
Most setups combine a handful of entities. Each is documented in full in the TOML reference — here’s the shape:
- One device, one profile. Pin a laptop or phone by IP or MAC, point it at a profile.
- Group several devices. Put the kids’ tablets into a group; change one line to retune the whole group.
- VLAN-wide defaults. Use
[[subnets]]to assign a profile to a CIDR; longest-prefix wins, so a tighter range overrides a broader one. - Time-based rules. Use
[[schedules]]to swap a group’s profile between hours, e.g. “kids-night from 21:00 to 07:00”. - Per-device exceptions. Add an
allow_rulesordeny_rulesentry on the device for a one-off domain that doesn’t deserve a whole profile.
When something doesn’t behave — wrong profile applied, blocklist not loading, the default catching a device you thought was mapped — start with troubleshooting.