Skip to content

Repository files navigation

DNS Sinkhole Benchmark

DNS Sinkhole Benchmark banner

GitHub Podman Python Bash Chart.js

GitHub Sponsors Ko-fi Liberapay Thanks.dev

Reproducible, fully static, FROM-scratch DNS sinkhole benchmark harness, with one published real-world WAN result set.

Repository: github.com/ConYel/dns-sinkhole-bench

Published site: conyel.github.io/dns-sinkhole-bench

Five DNS sinkhole implementations, each built into a fully static binary inside a reproducible StageX container — no runtime dependencies, no package manager, no shell, no bullshit.

The published benchmark tests Pi-hole, Blocky, AdGuard Home, Ferrous DNS, and Numa DNS across 4 workload scenarios, 4 upstream providers, and 2 benchmark modes. The generated result site includes violin QPS plots and bar charts for latency, lost queries, and memory.

Read this before interpreting results

This repository is not claiming a universal DNS ranking.

It is:

  • a reproducible container build setup;
  • a reproducible benchmark harness;
  • a published real-world WAN benchmark run;
  • a static site generator for inspecting the raw result set.

It is not:

  • a lab-isolated CPU microbenchmark;
  • a same-host localhost benchmark;
  • a long-duration stability test;
  • a full security review;
  • a tail-latency benchmark;
  • a claim that your network will produce the same absolute numbers.

The topology is intentionally real-world:

client running dnsperf → public internet → VPS running sinkhole container → public internet → upstream resolver

That is where many people actually run this shitware1. It also means WAN routing, upstream resolver behavior, and transient internet conditions are part of the measured system.

Current headline caveats

  • Only coarse geography is disclosed: EMEA client and EMEA VPS. Exact provider, IPs, regions, and timestamps are intentionally not published.
  • Each server/provider/scenario/mode normally has 3 replicates. The merged CSV has 478 rows out of an expected 480; one common-denominator cell is incomplete and disclosed in METHODOLOGY.md and results/schema.md.
  • Best-of-breed mode intentionally uses each server's native upstream transport: DoH for AdGuard Home, Blocky, and Ferrous; DoT for Numa; plain UDP DNS for Pi-hole FTL.
  • Common-denominator mode forces plain UDP DNS upstream where possible, but Pi-hole is plain DNS in both modes because FTL v6 has no native DoH/DoT upstream support.
  • Blocky relies on upstream DNSSEC validation by design.
  • Ferrous DNS config-file blocking is not equivalent to the others; its blocked-mode result must be read with that caveat.
  • dnsperf text output does not provide p50/p95/p99 latency, so latency is average latency only.
  • Memory is a benchmark-time memory reading, not a full heap/RSS profile.

If you want stricter statistics, more replicates, randomized run order, repeated day/night runs, packet-loss baselines, or a different latency tool: please reproduce it and send a PR. The harness is here for exactly that. Or pay me to do it :P

Implementations

Image Size Language Base
Pi-hole FTL v6.6.2 8.8 MB C StageX + source-built deps
Blocky v0.29.0 28.3 MB Go StageX pallet-go
Ferrous DNS v0.8.8 25.3 MB Rust StageX pallet-rust
AdGuard Home v0.107.77 24.5 MB Go StageX pallet-go (DNS only)
Numa DNS v0.21.0 10.1 MB Rust StageX pallet-rust

Results and artifacts

Build

make bench-build

Verify reproducible container builds:

make verify-all

Reproduce benchmark run

cp vps-config.example.sh vps-config.sh
$EDITOR vps-config.sh
./scripts/run-benchmark-modes.sh both vps-config.sh

Choose a single mode if needed:

./scripts/run-benchmark-modes.sh best vps-config.sh
./scripts/run-benchmark-modes.sh plain vps-config.sh
./scripts/run-benchmark-modes.sh merge

Generate the static result site from the current merged CSV:

make bench-site

Testing

make bench-test   # smoke-test dnsperf output parsing
make bench-lint   # shellcheck all scripts

make bench-lint requires ShellCheck.

Publishing with GitHub Pages

This repo is already laid out for GitHub Pages from docs/:

  1. Push the repository to GitHub.
  2. Repository Settings → Pages.
  3. Source: deploy from a branch.
  4. Branch: master.
  5. Folder: /docs.

The public URL will be:

https://conyel.github.io/dns-sinkhole-bench/

Known criticisms, answered upfront

Why not disclose the VPS provider? Because exact infrastructure identity is not needed to reproduce the harness and creates avoidable privacy/abuse surface. Coarse EMEA geography and system specs are enough context for interpreting the published run.

Why not run for days? Because this benchmark intentionally avoids hammering public DNS resolvers for long periods. If you need longitudinal measurements, run them responsibly on your own infrastructure and document the policy.

Why only 3 replicates? Because the full matrix is already large: 5 servers × 4 providers × 4 scenarios × 2 modes × 3 replicates. More replicates are welcome, but the current dataset is disclosed honestly rather than over-claimed.

Why WAN instead of localhost? Because the goal is real-world deployment behavior, not only pure server-loop throughput.

Why include best-of-breed if transports differ? Because deployments differ. The common-denominator mode exists to reduce this confound; the best-of-breed mode shows what the software actually does when configured naturally.

Support

GitHub Sponsors Ko-fi Liberapay Thanks.dev

Rant

This benchmark was made in France. For anyone who thinks it is not a real country, or that it is third world:

La Trahison des Images, 1929 — René Magritte

"Ceci n'est pas une pipe" %>% gsub("une", "un", .)

you know where to put it.


Acknowledgements

Reproducible build foundation — This project stands on other people's work. I am grateful to the StageX maintainers for the reproducible build toolchain and pallet images that made the static scratch-container builds practical: 🛡️ Lance R. Vick, 🪙 Anton Livaja, 🔑 Ryan Heywood, 🌐 Danny Grove, 👛 Matthew Brooks, 💻 Jakub Panek, and 🧩 Zoë Finja Emilia Kron.

DNS projects under test — Respect to the maintainers of the DNS projects benchmarked here. This benchmark exists because their projects exist: Pi-hole FTL, Blocky, AdGuard Home, Ferrous DNS, and Numa DNS.

Benchmark and publishing stack — Appreciation also goes to the maintainers of the tools used to run and publish the benchmark: DNS-OARC dnsperf, BIND/dig, Podman, ShellCheck, GNU Make, Python, Bash, Chart.js, and chartjs-chart-boxplot.

Workflow companions — Finally, thanks to ZeroClaw and opencode for helping drive the coding workflow, and to ThePrimeagen (GitHub), whose videos were running in the background while this project was built.

Licensing

Repository-owned code, scripts, configuration templates, documentation, and build harness files are licensed under AGPL-3.0-or-later, or under a separate commercial license for users who do not want AGPL terms; see LICENSE and LICENSE-COMMERCIAL.

Benchmark data and generated reports are licensed under Creative Commons Attribution-NonCommercial 4.0 International (CC BY-NC 4.0); see results/LICENSE. Attribution is required, and commercial/product use is not allowed without separate written permission.

Third-party software remains under its own upstream licenses; see THIRD_PARTY_LICENSES.md.

Footnotes

  1. "Yeah, well, you know, that's just, like, your opinion, man." Also: "Everything is a copy of a copy of a copy." More plainly: humans write software, humans make mistakes, and every non-trivial system is broken somewhere.

About

Reproducible WAN benchmark of 5 DNS sinkholes (Pi-hole, Blocky, AGH, Ferrous, Numa). FROM scratch StageX containers, real results with violin plots. Discovered Quad9 DoT rate-limiting, AGH rate-limit bug, Pi-hole REFUSED flood.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Sponsor this project

Packages

Contributors

Languages