DNS Sinkhole Benchmark

Overview

Comparative benchmark of 5 DNS sinkhole implementations running as FROM scratch static binaries on StageX. Each server is tested against 4 upstream providers: Quad9, Cloudflare, Google, and OpenDNS. Upstream protocol varies by server (DoH for AdGuard Home / Blocky / Ferrous, plain DNS for Pi-hole, DoT for NUMA). DNSSEC validation is enabled on all servers except Blocky (upstream limitation). dnsperf runs on the client machine (laptop); the servers run on a remote VPS — the two are on different networks with no LAN adjacency, connected over the public internet.

Generated: 2026-07-01T21:33:06Z

Read before ranking: this is a real-world WAN benchmark, not a lab-isolated CPU microbenchmark. The harness and containers are reproducible; absolute QPS and latency include public-internet path, VPS scheduling, upstream resolver policy, and transient network conditions. Treat close results as ties, not universal truth.

Each metric below has two modes, toggled by the filter pills in the nav bar:

⚠ Transport protocol confound. Each server uses its native upstream protocol in best-of-breed mode: DoH (AdGuard Home, Blocky, Ferrous), DoT (NUMA), plain DNS (Pi-hole). Differences in QPS/latency between servers partly reflect protocol overhead, not just server efficiency. The "common-denominator" filter (mode=1) forces all servers to plain UDP 53 to isolate server-side performance. See the Methodology section for details.
📌 Example: AdGuard Home in best-of-breed mode. In mode=0, AdGuard Home terminates DoH (DNS-over-HTTPS), which adds significant TLS handshake and HTTP/2 overhead. This inflates latency (e.g., ~212 ms vs ~94 ms for plain DNS on cache-hit) and memory (~287 MB vs ~45 MB) compared to its own mode=1 numbers. This is not a server-efficiency problem — it is the cost of HTTPS transport. Switch to "Common-denominator" mode to see server-level performance without the protocol confound.

How to read this page

Comparison Table

Scenario: Cache hit

All queries resolve from server cache — measures peak throughput after cache and connection warmup.

Per-Second QPS

Per-second QPS chart data is available in the table below.

Average Latency (ms)

Average latency chart data is available in the table below.

Lost Queries (%)

Lost queries chart data is available in the table below.

Base Memory (MB)

Base memory chart data is available in the table below.

Per-Second QPS

Per-second QPS chart data is available in the table below.

Average Latency (ms)

Average latency chart data is available in the table below.

Lost Queries (%)

Lost queries chart data is available in the table below.

Base Memory (MB)

Base memory chart data is available in the table below.

Scenario: Cache miss

Every query requires upstream resolution — measures cold-cache behavior plus upstream resolver/network behavior. In common-denominator mode some servers report near-zero QPS when plain-DNS upstream forwarding fails or times out.

Per-Second QPS

Per-second QPS chart data is available in the table below.

Average Latency (ms)

Average latency chart data is available in the table below.

Lost Queries (%)

Lost queries chart data is available in the table below.

Base Memory (MB)

Base memory chart data is available in the table below.

Per-Second QPS

Per-second QPS chart data is available in the table below.

Average Latency (ms)

Average latency chart data is available in the table below.

Lost Queries (%)

Lost queries chart data is available in the table below.

Base Memory (MB)

Base memory chart data is available in the table below.

Scenario: Mixed

90% cached + 10% unique domains — simulates a mixed real-world workload.

Per-Second QPS

Per-second QPS chart data is available in the table below.

Average Latency (ms)

Average latency chart data is available in the table below.

Lost Queries (%)

Lost queries chart data is available in the table below.

Base Memory (MB)

Base memory chart data is available in the table below.

Per-Second QPS

Per-second QPS chart data is available in the table below.

Average Latency (ms)

Average latency chart data is available in the table below.

Lost Queries (%)

Lost queries chart data is available in the table below.

Base Memory (MB)

Base memory chart data is available in the table below.

Scenario: Blocked

Domains on the sinkhole blocklist — measures blocklist behavior. Ferrous DNS is not apples-to-apples here because config-file filter-list blocking is not equivalent to the other servers.

Per-Second QPS

Per-second QPS chart data is available in the table below.

Average Latency (ms)

Average latency chart data is available in the table below.

Lost Queries (%)

Lost queries chart data is available in the table below.

Base Memory (MB)

Base memory chart data is available in the table below.

Per-Second QPS

Per-second QPS chart data is available in the table below.

Average Latency (ms)

Average latency chart data is available in the table below.

Lost Queries (%)

Lost queries chart data is available in the table below.

Base Memory (MB)

Base memory chart data is available in the table below.

Per-Server Details

Methodology

Note: Blocky does not perform DNSSEC validation (upstream limitation). All other servers have DNSSEC validation enabled. Results reflect testing against 4 upstream providers: Quad9, Cloudflare, Google, and OpenDNS.

Transport Protocol Confound

Each server ships with its preferred upstream protocol baked into the configuration — there is no single "best" protocol that all servers support uniformly:

Best-of-breed mode tests each server with its native protocol — this is the real-world deployment scenario, but comparisons between servers partly reflect protocol overhead, not just server efficiency. Common-denominator mode forces all servers to plain UDP 53 where possible, reducing the transport confound but not making the implementations architecturally identical. Pi-hole is plain DNS in both modes because FTL v6 has no native DoH/DoT upstream support.

Dataset completeness

The expected matrix for observed cells is 480 rows. The published CSV has 478 rows. Aggregates use available rows; missing rows are not imputed.

Fairness matrix

FeaturePi-holeBlockyAdGuard HomeFerrousNuma
Best-mode upstreamPlain UDPDoHDoHDoHDoT
Common modePlain UDPPlain UDPPlain UDPPlain UDPPlain UDP
DNSSECLocal enabledUpstream onlyEnabledEnabledEnabled
Cache enabledYesYesYesYesYes
Rate limitRaised highNot configuredDisabledDisabledNot configured
Blocked scenarioNULL responseNXDOMAINFilter configNot equivalentFile blocklist

Warmup

Before each scenario, a 5-second dnsperf warmup pass is run to stabilize connections and fill the TCP congestion window. For cache-hit and mixed scenarios, the server cache is also pre-warmed by resolving each cached domain once via dig before the warmup pass. The measurement window (-l 30) begins only after both warmup phases complete. Each scenario therefore runs for ≈37–42s wall-clock; only the final 30 s of steady-state data are reported.

Test Parameters

Servers Tested

ServerVersionLanguageBasePortDNSSEC
Ferrous DNSv0.8.8RustStageX pallet-rust15353Yes
Numa DNSv0.21.0RustStageX pallet-rust15454Yes
Blockyv0.29.0GoStageX pallet-go15555No
AdGuard Homev0.107.77GoStageX pallet-go15656Yes
Pi-hole FTLv6.6.2CStageX + source-built deps15757Yes

Known limitations

Reproducibility

All builds are 100% StageX, FROM scratch, fully static binaries with pinned source tarballs and SHA256 verification. See the project README for rebuild instructions, schema, and caveats.

Support

GitHub Sponsors Ko-fi Liberapay Thanks.dev

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.