nxdns
A self-hosted DNS sinkhole for a household LAN, written in Zig 0.16. One static musl binary, SQLite for state, a Raspberry Pi 5 as the reference target. It answers your network's DNS, blocks what you tell it to, and shows you what asked for what.
Features
- Blocklist filtering: subscribe to hosts/domain lists, plus your own allow
and block rules with wildcard support (
*.example.com) - Per-client policy groups: different filtering for the kids' tablet and your workstation
- Local DNS records and conditional forwarding for internal zones
- Encrypted upstreams: DNS-over-HTTPS and DNS-over-TLS with failover
- Built-in DoH and DoT server endpoints, with certificate hot-reload
- Bounded in-memory DNS cache with TTL-respecting expiry
- Query log with retention limits, live-streamed over SSE
- Web UI (embedded in the binary) and a REST API with a served OpenAPI spec
- Prometheus-style
/metrics, per-client rate limiting, disk-full self-protection
Quickstart (docker compose)
Build the binary and image, seed a minimal configuration, start it:
(cd web && npm ci && npm run build)
zig build cross -Dweb-dist=web/dist -Doptimize=ReleaseSafe
cd deploy/docker
mkdir -p etc-nxdns
cat > etc-nxdns/config.zon <<'EOF'
.{
.groups = .{ .{ .name = "default" } },
.upstreams = .{ .{ .url = "https://cloudflare-dns.com/dns-query" } },
.web = .{ .password = "choose-a-real-password" },
}
EOF
docker compose up -d
DNS is on port 53, the web UI on http://localhost:8080. The config file
seeds the database on first boot only; from then on the database is the
truth and changes go through the UI, the API, or nxdns export /
nxdns import. Full install instructions, including the systemd path and
the Pi 5 recipe, are in
docs/how-to/install-with-systemd.md and
docs/how-to/install-with-docker.md.
Building from source
Requires Zig 0.16.0 and Node.js 24 (for
the web UI). C dependencies (SQLite, mbedTLS) are vendored and built by
zig build.
(cd web && npm ci && npm run build) # web UI -> web/dist
zig build -Dweb-dist=web/dist # native binary -> zig-out/bin/nxdns
zig build cross -Dweb-dist=web/dist -Doptimize=ReleaseSafe
# static x86_64 + aarch64 musl binaries
zig build test --summary all # unit tests
Documentation
Start at docs/README.md, which splits the documentation into a tutorial, how-to guides, reference and explanation.
- docs/tutorial/first-run.md — build it, resolve a name, block a domain, on a scratch directory
- docs/how-to/install-with-systemd.md — a real install, including the Raspberry Pi 5
- docs/reference/configuration.md — every configuration field
- docs/reference/api.md — REST API, auth and SSE
- docs/reference/cli.md — subcommands, flags and exit codes
- docs/explanation/architecture.md — module map and design
- PLAN.md and specs/ — scope, design decisions and per-milestone contracts