FREE & OPEN-SOURCE RECURSIVE DNS SERVER · v1.1.1

A Privacy-First Recursive Resolver You
Actually Own.

Install a self-hosted iterative recursive DNS server written in Rust with a single command. Full DNSSEC validation including post-quantum ML-DSA-44, five transports (UDP/TCP, DoT, DoQ, DoH, DoH3), geo-aware multi-node GSLB steering with peer-mesh health, bounded W-TinyLFU caching, RFC 8767 stale serving, and SSRF-resistant resolution. No forwarders. No upstream logging.

Recommended install
via crates.io
cargo install unified-dns
100% DNSSEC ValidatedML-DSA-44 Post-QuantumGSLB Multi-Node SteeringMIT Licensed
Live Resolver Output

Watch It Validate DNSSEC and Steer Clients in Real Time

A standard dig +dnssec query returning the AD flag, plus the internal validation and GSLB decision log.

unified-dns , live query trace
● AD: validated

Client Query

$ dig @127.0.0.1 cloudflare.com A +dnssec

; <<>> DiG 9.18.24 <<>> @127.0.0.1 cloudflare.com A +dnssec
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 41827
;; flags: qr rd ra ad; QUERY: 1, ANSWER: 2, AUTHORITY: 0,
;;        ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags: do; udp: 1232
;; QUESTION SECTION:
;cloudflare.com.                IN      A

;; ANSWER SECTION:
cloudflare.com.         300     IN      A       104.16.132.229
cloudflare.com.         300     IN      A       104.16.133.229

;; Query time: 12 msec
;; SERVER: 127.0.0.1#53(127.0.0.1) (UDP)
;; WHEN: Mon Sep 15 09:14:02 UTC 2026
;; MSG SIZE  rcvd: 73

Internal Validation & GSLB Log

[DNSSEC] secure      .                DNSKEY verified (KSK-2017)
[DNSSEC] secure      com.             DS -> DNSKEY chain OK
[DNSSEC] secure      cloudflare.com.  RRSIG verified (alg 13)
[DNS]    resolved    cloudflare.com.  A  12ms  cache=MISS
[CACHE]  inserted    cloudflare.com.  A  ttl=300s  ad=1
[GEO]    decision    client_region=DE selected_node=europe-1
                    returned_nodes=europe-1,usa-1,asia-1
                    returned_count=3  score=0.97  rtt_ms=0.14
[RRL]    allow       subnet 203.0.113.0/24  qps=1
Installation

Up and Running in Under 5 Minutes

Three installation paths: cargo from crates.io, pre-built Linux binaries from GitHub Releases, or a full source build. All three produce the same unified-dns binary.

bash , download & verify
# 1. Pick your architecture (x86_64 shown; replace with aarch64 for ARM)
ARCH=x86_64
VERSION=v1.1.1
BASE="unified-dns-linux-${ARCH}.tar.gz"

# 2. Download the release archive and its checksum from GitHub
curl -fsSLO "https://github.com/AZBrandCanada/Unified-DNS/releases/download/${VERSION}/${BASE}"
curl -fsSLO "https://github.com/AZBrandCanada/Unified-DNS/releases/download/${VERSION}/${BASE}.sha256"

# 3. Verify the SHA-256 checksum before extracting
sha256sum -c "${BASE}.sha256"

# 4. Extract and install
tar -xzf "${BASE}"
sudo install -m 0755 unified-dns /usr/local/bin/unified-dns

# 5. (Optional) Grant privileged port binding without root
sudo setcap 'cap_net_bind_service=+ep' /usr/local/bin/unified-dns

# 6. Run it
unified-dns

Need a production deployment walkthrough? Contact our engineering team.

Option 1 — Install from crates.io (recommended)

If you have a Rust toolchain installed, unified-dns is published on crates.io. A single command installs the binary to ~/.cargo/bin/unified-dns:

shell
$ cargo install unified-dns
    Updating crates.io index
  Downloaded unified-dns v1.1.1
   Compiling unified-dns v1.1.1
    Finished release [optimized] target(s)
  Installing ~/.cargo/bin/unified-dns

$ unified-dns --help
Unified Recursive DNS Server
A multi-protocol iterative recursive resolver with DNSSEC
validation and geo-aware GSLB steering.
⚠

GeoIP database not included

The cargo install path does not bundle the MaxMind GeoLite2 database, because the licence requires each user to download it separately. This only affects the GSLB feature — the resolver runs fine as a normal recursive resolver without it. See the GeoIP setup section below.

Optional: GeoLite2 database (required for GSLB only)

The Geo-Aware GSLB feature needs a MaxMind GeoLite2-City database to map client IPs to countries and coordinates. This database is not included in the cargo crate, and not included in the pre-built release binaries. It must be downloaded separately, and this only matters if you plan to run the GSLB layer.

  1. Create a free MaxMind account at maxmind.com/en/geolite2/signup.
  2. Generate a licence key under Account → Manage Licence Keys.
  3. Download the GeoLite2-City database in mmdb format (the GZIP archive, not the CSV format).
  4. Extract the archive and place the mmdb file next to the resolver binary.
shell
# After downloading GeoLite2-City_YYYYMMDD.tar.gz from MaxMind:

tar -xzf GeoLite2-City_*.tar.gz
cp GeoLite2-City_*/GeoLite2-City.mmdb /opt/unified-dns/

# Then set the path in your systemd unit or shell environment:
#   Environment="GEOIP_DATABASE=/opt/unified-dns/GeoLite2-City.mmdb"

# Or drop it in the working directory and rely on the default name:
#   /opt/unified-dns/GeoLite2-City.mmdb   ← picked up automatically

If the database is missing when GSLB is enabled, the resolver still starts. Client location is reported as ?? in the decision log, and node scoring falls back to health and any measured latency. You can also point GEOIP_DATABASE at any GeoLite2-City compatible mmdb file.

Optional: systemd service unit

Save as /etc/systemd/system/unified-dns.service to run the resolver on boot with an unprivileged user. The example below shows a single-node setup with cache, root zone, and GSLB steering enabled. A full multi-node deployment adds the peer mesh and node list variables shown in the comments.

/etc/systemd/system/unified-dns.service
[Unit]
Description=Unified Recursive DNS Server
After=network.target
Wants=network-online.target

[Service]
Type=simple
User=unified-dns
Group=unified-dns
WorkingDirectory=/var/lib/unified-dns
ExecStart=/usr/local/bin/unified-dns

# ─── Listener ports ─────────────────────────────────────────────
Environment="HOST=0.0.0.0"
Environment="DNS_PORT=53"
Environment="DOT_PORT=853"
Environment="DOQ_PORT=853"
Environment="DOH_PORT=443"
Environment="DOH3_PORT=443"
Environment="DOH_NO_TLS=0"

# ─── DNSSEC enforcement ─────────────────────────────────────────
Environment="DNSSEC_ENFORCE=1"

# ─── Rate limits and stale serving ──────────────────────────────
Environment="MAX_STALE_SECS=300"
Environment="RATE_LIMIT_BURST=300"
Environment="RATE_LIMIT_PER_SEC=60"

# ─── TLS ────────────────────────────────────────────────────────
Environment="CERT_PATH=/etc/letsencrypt/live/dns.example.com/fullchain.pem"
Environment="KEY_PATH=/etc/letsencrypt/live/dns.example.com/privkey.pem"

# ─── Cache (relative paths resolve against WorkingDirectory) ────
Environment="CACHE_FILE=cache.json"
Environment="CACHE_MAX_ENTRIES=500000"
Environment="CACHE_PREFETCH=1"
Environment="CACHE_PREFETCH_THRESHOLD_PCT=15"
Environment="CACHE_PREFETCH_MIN_HITS=5"

# ─── Root zone (relative paths resolve against WorkingDirectory) ─
#Environment="ROOT_ZONE_FILE=root.zone"
#Environment="ROOT_ZONE_REFRESH_HOURS=168"

# ─── Pre-warming (optional) ─────────────────────────────────────
#Environment="WARM_LIMIT=20000"
#Environment="WARM_CONCURRENCY=6"

# ─── Geo-aware GSLB ─────────────────────────────────────────────
Environment="GEO_ROUTING_ENABLED=1"
Environment="GEO_AUTHORITATIVE_NAMES=dns.example.com"
Environment="GEOIP_DATABASE=GeoLite2-City.mmdb"
Environment="GEO_ROUTING_TTL=10"
Environment="GEO_HEALTH_INTERVAL=10"
Environment="GEO_IP_FAILOVER_IP=3"

# ─── Peer mesh (all nodes share the same secret and node list) ──
#Environment="GEO_SELF_NODE=europe-1"
#Environment="GEO_PEER_SECRET=<64-char hex from: openssl rand -hex 32>"
#Environment="GEO_PEER_HEARTBEAT_INTERVAL=3"

# ─── Node list (identical on every node) ────────────────────────
#Environment="NODE1_NAME=asia-1"
#Environment="NODE1_IPV4=203.0.113.10"
#Environment="NODE1_DOH_URL=https://dns1.example.com:3053"
#Environment="NODE1_LOCATION=asia"
#Environment="NODE1_LAT=1.3521"
#Environment="NODE1_LON=103.8198"
#Environment="NODE1_ENABLED=true"

#Environment="NODE2_NAME=europe-1"
#Environment="NODE2_IPV4=198.51.100.20"
#Environment="NODE2_DOH_URL=https://dns2.example.com:3053"
#Environment="NODE2_LOCATION=europe"
#Environment="NODE2_LAT=52.5200"
#Environment="NODE2_LON=13.4050"
#Environment="NODE2_ENABLED=true"

#Environment="NODE3_NAME=usa-1"
#Environment="NODE3_IPV4=192.0.2.30"
#Environment="NODE3_DOH_URL=https://dns3.example.com:3053"
#Environment="NODE3_LOCATION=north_america"
#Environment="NODE3_LAT=40.7128"
#Environment="NODE3_LON=-74.0060"
#Environment="NODE3_ENABLED=true"

# ─── Logging ────────────────────────────────────────────────────
Environment="RUST_LOG=info,unified_dns=info"

Restart=always
RestartSec=3
LimitNOFILE=65535

# Hardening (optional but recommended)
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/var/lib/unified-dns

[Install]
WantedBy=multi-user.target

For privileged ports (53, 853, 443) without running as root, grant the capability once:

sudo setcap 'cap_net_bind_service=+ep' /usr/local/bin/unified-dns

If you installed via cargo install, the binary lives at ~/.cargo/bin/unified-dns. Copy it to /usr/local/bin/ so the systemd unit can find it:

sudo install -m 0755 ~/.cargo/bin/unified-dns /usr/local/bin/unified-dns
Alternative · Pre-Built Binaries

Download Linux Binaries

If you don't have a Rust toolchain, static Linux binaries for x86_64 and ARM64 are available from GitHub Releases. Each archive ships with a matching SHA-256 checksum file.

Linux x86_64

unified-dns-linux-x86_64.tar.gz

2.55 MB
SHA-256
2c808cd5f1fa878027fb5956ff3e91f15826b6014efb0f1e8d46c1875f20a1cc

Linux ARM64 (aarch64)

unified-dns-linux-aarch64.tar.gz

2.42 MB
SHA-256
a220e86e6c6f989a5da08c7e06c08739b77b9555ef0f95455dcfaede03894dba
⚠

Release binaries do not include the GeoLite2 database

The GeoIP database is downloaded separately under the MaxMind licence. See the GeoIP setup section above. This only matters if you enable the GSLB feature.

Core Engine Capabilities

Everything a Serious Resolver Needs

Every subsystem is designed around RFC compliance, not shortcuts.

Full DNSSEC Validation

RSA (SHA-256/512), ECDSA P-256/P-384, Ed25519, and post-quantum ML-DSA-44 (Algorithm 18). NSEC/NSEC3 negative proofs with an RFC 9276 iteration cap. Expired or unauthorized RRSIGs are rejected.

Five Transports, One Pipeline

Plain DNS over UDP/TCP, RFC 7858 DNS-over-TLS, RFC 9250 DNS-over-QUIC, RFC 8484 DNS-over-HTTPS with GET and POST, and RFC 9114 DNS-over-HTTP/3. All five share the same cache and DNSSEC validator. Reverse-proxy mode included.

Geo-Aware GSLB Steering

Acts as a small authoritative nameserver for a configured set of names. Scores backend nodes by geographic distance, measured health, and last-observed latency. Per-request decisions, EDNS Client Subnet support, never cached.

Authenticated Peer Mesh

Multiple nodes exchange HMAC-SHA256 signed heartbeats on a short interval. A failed node is excluded from every peer's GSLB responses within seconds. Source-IP allowlist, signature verification, and 60-second replay window.

Bounded Cache & Single-Flight

W-TinyLFU admission and eviction keep memory flat under traffic skew. Concurrent identical misses coalesce into a single upstream resolution via a per-key single-flight gate. Configurable capacity.

Background Prefetch

Hot records nearing expiration are refreshed in the background by a loop that only considers records above a configurable hit count. Failed refreshes back off exponentially and never invalidate the still-valid entry.

RFC 8767 Stale Serving

When an upstream is unreachable, recently expired records are served with a 30-second positive TTL and AD=0 while a background task revalidates them. Configurable stale window via MAX_STALE_SECS.

SSRF & Poisoning Defenses

Strict bailiwick checks on referrals, out-of-bailiwick glue rejection, RFC 1918 / loopback / link-local / CGNAT / cloud-metadata filtering on upstreams, and full DNSSEC validation before any cache insert.

Iterative, Not Forwarding

Resolves directly from the IANA root servers down through TLDs to authoritative nameservers. In-process root zone lifecycle with atomic refresh. No Google, Cloudflare, or Quad9 in the path.

Free to Self-Host. Paid When You Need a Hand.

The resolver itself is MIT-licensed and free forever. Optional support and managed hosting exist for teams that want them.

FREE FOREVER

Self-Hosted

$0 / forever

Full source code under the MIT license. Run it on your own hardware with no restrictions.

  • One-command install via cargo
  • Full iterative resolver source code
  • DNSSEC validation (incl. ML-DSA-44)
  • DoH + DoT + DoQ + DoH3 + UDP/TCP
  • Geo-aware GSLB with peer mesh
  • Bounded cache + single-flight + prefetch
Install v1.1.1

Managed Hosting

$20 / month

We run the resolver on hardened infrastructure in Canadian and EU regions. You get a private DoH/DoT endpoint and no servers to patch.

  • Private DoH + DoT + DoQ endpoint
  • Multi-region GSLB with peer mesh
  • Automatic updates & CVE patching
  • Daily config backups
  • Zero query logging (enforced)
Request Managed Hosting

Enterprise Support

$500 / month

For ISPs, hosting providers, and enterprises running the resolver at scale.

  • Dedicated Slack / Matrix channel
  • < 1 hr P1 response SLA
  • Custom feature development hours
  • GSLB deployment review & hardening audit
  • Commercial support contract
Talk to Engineering

Recursive DNS Server FAQ

Common questions about DNSSEC, transports, GSLB, self-hosting, and licensing.

Need Help Deploying?

Tell us about your environment — VPS, bare metal, or a multi-region GSLB deployment — and our engineers will help you plan a hardened self-hosted rollout.

Security Verification
Loading…