PHAROS

Fast because nothing’s in the way.

Pharos isn’t quick because of clever tricks. It’s quick because of what the hot path leaves out: no heap allocations, no dynamic dispatch, no copies of your message.

  1. Allocs / query0

    Nothing hits the heap

    On the hot path, each listener writes answers into a buffer it already owns. Plugins hand back their result instead of writing to a shared response writer, so nothing gets boxed or copied along the way.

  2. Dispatchstatic

    A plugin chain built for your build

    plugin.cfg generates the chain at compile time. Every hop to the next plugin is a direct call the compiler can inline, not a boxed future.

  3. Parsingzero-copy

    Messages read in place

    Records are read through borrowed views over the receive buffer, the zero-copy idea behind NLnet Labs’ domain crate, done in safe Rust.

  4. TCP1,024 × 16

    Calm under load

    Each listener holds up to 1,024 TCP connections with 16 queries in flight on each. Anything beyond that waits in the backlog instead of eating your memory.

Pharos vs CoreDNS.

Same box, same Corefile, same query list, ten seconds of dnsperf each. We’ll post the results as soon as the release runs are in.

  • Pharos
  • CoreDNS 1.15
Throughputqueries/s, higher is better
Pharosnot measured yet
CoreDNS 1.15not measured yet
Latency p50ms, lower is better
Pharosnot measured yet
CoreDNS 1.15not measured yet
Latency p99ms, lower is better
Pharosnot measured yet
CoreDNS 1.15not measured yet
Peak memoryMiB, lower is better
Pharosnot measured yet
CoreDNS 1.15not measured yet

Don’t take our word for it.

One command runs the micro-benchmarks, then pits Pharos against CoreDNS 1.15 with dnsperf. Save a baseline and it’ll fail the run when throughput drops, or p99 or memory grows, by more than 5%. Handy in CI.

How the benchmark works

# from a checkout of the repository$ cargo xtask bench$ cargo xtask bench --save-baseline main$ cargo xtask bench --baseline main

Try it on your Corefile.

Ask Pharos what it thinks of your config, then let it serve. It reads /etc/pharos/pharos.conf, so one mount is all it takes.

# see what changes for your Corefile, if anything$ docker run --rm \-v "$PWD/Corefile:/etc/pharos/pharos.conf:ro" \ghcr.io/itsmeyaw/pharos:1.0.0 check # serve it on port 53$ docker run --rm -p 53:53/udp -p 53:53/tcp \-v "$PWD/Corefile:/etc/pharos/pharos.conf:ro" \ghcr.io/itsmeyaw/pharos:1.0.0