EN
  • English
  • Deutsch
  • Polski
  • Italiano
  • Español

Build from source

Under review — wording may still change.

Compile Warden from source and install it on any Linux distribution.

Most people should install the package instead — it’s faster and upgrades cleanly. If your distribution has one, use the Debian / Ubuntu or Fedora guide. Build from source when there’s no package for your distribution (Arch, RHEL, openSUSE, …), when you want to turn on optional features, or when you need to target an older glibc than the prebuilt package supports.

Nothing here touches your network until the last step: you compile the binary, install it alongside a hardened systemd unit, free up port 53, then start Warden and confirm it’s blocking.

Requirements

  • A Linux distribution with systemd
  • Architecture: x86_64 (aarch64 / ARM is cross-compile only — see the end)
  • Root / sudo

Because you build on your own machine, the binary links whatever glibc that machine already has — so there’s no minimum-version hurdle the way there is with the prebuilt package. The build tools themselves are installed in the next step.

1. Install build tools

You need a C compiler, make, pkg-config, git, and a Rust toolchain (1.94 or newer). Warden’s TLS stack is pure Rust (rustls + ring) — there is no OpenSSL dependency to install.

bash
# Fedora / RHEL
sudo dnf install gcc make pkg-config git

# Debian / Ubuntu
sudo apt install build-essential make pkg-config git

Install Rust with rustup (the toolchain manager) if you don’t already have it:

bash
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
source ~/.cargo/env

You’ll also want dig for the final check — it’s bind-utils on Fedora (usually pre-installed) and dnsutils on Debian / Ubuntu.

2. Get the source

bash
git clone {{TODO: clone URL}}
cd warden

Run every command below from this directory — the build and install steps use paths relative to the repository root.

3. Build

bash
cargo build --release

This produces the warden binary at target/release/warden — the daemon and the CLI in one executable.

Warning
The release profile is tuned for a small, fast binary (fat LTO, a single codegen unit), which makes the build itself slow and memory-hungry — expect several minutes, and the final link-time optimization needs a fair amount of free RAM. On a small box, close other work first.

Optional features are off by default. Turn one on at build time if you need it, for example cargo build --release --features dnssec (also doq and cluster).

4. Install

Install the binary, scaffold the config, then drop in the systemd unit:

bash
sudo install -m 0755 target/release/warden /usr/local/bin/warden
sudo warden init --yes
sudo install -m 0644 systemd/purge-warden.service /etc/systemd/system/
sudo systemctl daemon-reload

What each line does:

  • installs the warden binary to /usr/local/bin
  • warden init --yes creates the purge-warden system user, a working default config at /var/lib/purge-warden/config.toml, and the data directories — with --yes it accepts the defaults non-interactively; drop --yes to pick your blocklists and allowed clients at a prompt
  • installs the hardened unit, which runs Warden as the unprivileged purge-warden user and grants it only CAP_NET_BIND_SERVICE so it can bind port 53 — no setcap step needed
Warning
Like the packages, this installs but does not start the service. A DNS server going live unprompted is disruptive, so you activate it in the next two steps.

Prefer a shortcut? sudo make install places the binary, unit, and an example config in one step; you still run sudo warden init --yes afterwards to create the user and live config, then sudo systemctl daemon-reload.

5. Free port 53 (if needed)

Check what holds port 53 first with sudo ss -ulpn 'sport = :53'. If nothing is listening there, skip this step. Otherwise, if this machine runs systemd-resolved on port 53 (common on desktop Linux), hand port 53 to Warden by disabling only the stub listener:

bash
printf '[Resolve]\nDNSStubListener=no\n' | \
  sudo tee /etc/systemd/resolved.conf.d/purge-warden-no-stub.conf
sudo systemctl restart systemd-resolved
Info
Warden never edits your resolver configuration automatically — you run this step yourself, so nothing about your DNS changes without your say-so.

6. Enable + start

bash
sudo systemctl enable --now purge-warden

7. Verify

verify
$ dig @127.0.0.1 doubleclick.net # → 0.0.0.0 (blocked) $ dig @127.0.0.1 google.com # → real address (allowed)

On first start Warden fetches its blocklists, so allow a few moments before doubleclick.net resolves to 0.0.0.0.

Point your router’s or devices’ DNS at this machine’s IP to protect the whole network.

Upgrading

Pull the latest source, rebuild, and replace the binary:

bash
git pull
cargo build --release
sudo install -m 0755 target/release/warden /usr/local/bin/warden
sudo systemctl restart purge-warden

Your config in /var/lib/purge-warden/config.toml is preserved across upgrades. If a release ships a changed systemd unit, reinstall it as in step 4 and re-run sudo systemctl daemon-reload before restarting.

Removing

There’s no package manager to undo a source install, so remove the pieces by hand:

bash
sudo systemctl disable --now purge-warden
sudo rm /usr/local/bin/warden /etc/systemd/system/purge-warden.service
sudo systemctl daemon-reload

That stops and removes the program but leaves your config and data in /var/lib/purge-warden. For a clean slate, also delete that directory and the system user:

bash
sudo rm -rf /var/lib/purge-warden
sudo userdel purge-warden

Building for ARM (aarch64)

ARM builds are cross-compiled and best-effort — not yet packaged or tested on real hardware. If you want to try, BUILDING.md in the repository documents the cross build --target aarch64-unknown-linux-musl route. Note that the shipped systemd unit’s syscall filter kills a static musl binary — it was rejected on x86_64 for exactly this reason — so an ARM build needs the unit’s hardening relaxed.

Older distributions (glibc < 2.39)

Building on the target machine sidesteps this entirely — cargo links whatever glibc is already installed, so a binary built on Ubuntu 22.04 runs on Ubuntu 22.04. The only hard floor is Rust 1.94, which rustup provides on any distro. You only need a build container or cargo-zigbuild tricks in BUILDING.md when cross-building on a newer host for an older target.