Measured, not claimed
Performance
Numbers from the project's own benchmark harness (make bench-full), on loopback, with the raw results one click away.
AMD Ryzen 5 5500U with Radeon Graphics (12 threads, x86_64, Linux 6.8.0) · 4 DNS workers · 2026-10-04 · commit c67ccba · raw results (JSON)
| Corpus | Peak qps | Load | Offered qps | p50 | p99 | Loss |
|---|---|---|---|---|---|---|
cache-hot | 172,580 | 25% | 43,144 | 34 µs | 303 µs | 0.00% |
| 50% | 86,289 | 37 µs | 1.6 ms | 0.00% | ||
| 75% | 129,434 | 47 µs | 2.9 ms | 0.00% | ||
blocked | 154,381 | 25% | 38,595 | 35 µs | 575 µs | 0.00% |
| 50% | 77,190 | 36 µs | 975 µs | 0.00% | ||
| 75% | 115,785 | 37 µs | 1.7 ms | 0.00% | ||
miss-heavy | — | fixed rate | 2,000 | 103 µs | 251 µs | 0.00% |
Server: ready in 76 ms, filter ready in 5.5 s, 89 MiB idle and 354 MiB peak RSS with 5 blocklists loaded. 3 runs of 30 s per corpus; each number is the median across runs. Load generator: dnsperf 2.14.0, 2 threads, 8 clients.
By transport
Intel(R) Core(TM) i5-1035G4 CPU @ 1.10GHz (8 threads, x86_64) · 2026-10-06 · raw results (JSON)
| Transport | Peak qps | p50 at 50% | p99 at 50% | Loss |
|---|---|---|---|---|
| UDP | 199,476 | 75 µs | 879 µs | 0.00% |
| TCP | 229,386 | 359 µs | 2.7 ms | 0.00% |
| DoT | 91,903 | 235 µs | 3.0 ms | 0.00% |
| DoH (HTTP/2) | 147,803 | 771 µs | 2.6 ms | 0.00% |
The same cache-hit corpus over each transport, from one node with a throwaway certificate. UDP, TCP, and DoT from dnsperf; DoH from h2load (RFC 8484 GET). The load generator runs on the same machine, so compare the rows with each other rather than with other hosts.
How to read this: peak is the most queries per second the server answered with no loss while dnsperf pushed as hard as it could on the same machine; latency at peak is mostly queueing in the load generator. The at 25 / 50 / 75% load rows replay a fixed rate below that peak: that's what devices on a home network experience. cache-hot asks 10,000 names repeatedly (answers from the cache), blocked asks names on the blocklists (answered by the filter, about 2.7 million names loaded), and miss-heavy sends unique names at a fixed 2,000 per second, each one resolved through a local stub upstream. Harness details: bench/README.md.
Against the v1.0 targets
The release gate for 1.0 sets targets for speed and footprint. These were measured on the shipped build, with the method for each one in docs/v1-gate.md.
| What | Target | Measured |
|---|---|---|
| Time to answer from the cache or the blocklists, at half the maximum load sent from another machine | p99 ≤ 250 µs | 99.999 % of answers sent within 250 µs (p99 within 50 µs), with no queries lost |
| Memory at idle, no lists | ≤ 20 MiB | 13.7 MiB |
| Memory with a million blocked names, 100,000 cached answers, and telemetry | ≤ 64 MiB | 60 MiB |
| Raspberry Pi 4: new rules answering after a 2-million-name list changes | ≤ 8 s | 4.7 s (the rest of the compile finishes in the background) |
| Start to serving, with the filter on disk | ≤ 500 ms | 52 ms with a million names |
| Compressed container image | ≤ 15 MiB | 10.6 to 11.1 MiB per architecture |
Still open before 1.0: throughput on a Raspberry Pi 4 that isn't serving a home network, and how fast a change reaches every node, measured over more changes.
Screenshots of the UI moved to the Tour; what TelltaleDNS measures and alerts on is on Monitoring.