bestdns
v1.2.0
Published
Find, benchmark, and apply the best DNS server for your network — a cross-platform CLI.
Maintainers
Readme
⚡ bestdns
Find the fastest, safest DNS for your network — then apply it in one step.
A cross-platform CLI that benchmarks public DNS providers, ranks them by real-world latency, and applies the winner to your OS network settings — with backups, health checks, and a polished interactive UX.
npx bestdnsTable of contents
- Why bestdns?
- Features
- Quick start
- Installation
- Usage
- Commands
bestdns optimize— full optimization flowbestdns diagnose— read-only checkupbestdns hogs— top network-hungry processesbestdns profiles— manage saved network profiles
- Provider catalog
- Health checks
- Configuration & data files
- Platform support
- How it works
- Development
- Contributing
- Releasing (maintainers)
- Roadmap
- Acknowledgments
- License
Why bestdns?
Choosing a DNS server is the cheapest, fastest network upgrade most people never make. The "best" one depends on where you are right now: a resolver that's blazing fast in Hanoi may be slow in São Paulo, and the one your ISP picks is rarely either fast or private.
bestdns measures DNS providers from your machine, ranks them honestly, and lets
you apply the winner without copy-pasting IPs into network preferences. It also tells
you whether each provider validates DNSSEC, hijacks NXDOMAIN responses, blocks ads, and
supports DNS-over-HTTPS — so "fastest" isn't the only thing you optimise for.
Features
It was a simple goal — make the internet faster.
Ran a speedtest. Checked DNS resolution times. Probed the path MTU, measured packet loss, read Wi-Fi signal strength and channel interference. Found three issues. Flushed a stale DNS cache. Restarted mDNSResponder. Swapped the resolver for the fastest one on the network. Ran the whole thing again to prove the numbers actually moved.
No AI. No cloud API. No black box. Just the network stack, a handful of shell commands, and an honest before/after comparison.
- 🛠 Optimize — full network optimization flow: baseline snapshot → walk through auto-fixable findings (DNS swap, cache flush, mDNS restart) → re-test for a side-by-side before/after.
- 🔬 Diagnose — read-only network checkup: download speed, latency, packet loss, path MTU, Wi-Fi signal, and current DNS performance, all with severity-ranked findings.
- 🐷 Hogs — list the top processes generating the most network traffic so you can spot the culprit yourself. Informational only — bestdns never kills processes.
- 🧹 Profiles — list, remove or bulk-prune saved Wi-Fi networks, macOS network locations and NetworkManager connections.
- 🔍 Benchmark — measures latency, jitter and reliability across a curated catalog of public resolvers and ranks them fastest-first.
- ⚡ Auto — benchmark and apply the fastest provider in a single command.
- 🎯 Apply — set DNS for your active network service, cross-platform, with privilege
elevation handled for you (
sudo/ UAC). - 📡 Current — show the DNS each network service is using and identify which catalog provider is behind it.
- 🩺 Health checks — DNSSEC validation, NXDOMAIN-hijacking detection, ad/tracker blocking and DNS-over-HTTPS reachability (proper RFC 8484 wire format).
- 📋 Catalog management — browse 16+ built-in providers grouped into Non-filtering, Security & Ad-blocking and Family-safe, and add your own.
- ↩️ Backup & restore — your previous DNS is saved before every change; revert to automatic (DHCP) with one command.
- 🔄 Update checker — tells you when a newer version is available (only in
interactive terminals; never pollutes
--jsonoutput). - 🖥️ Beautiful UX — an interactive menu (built on
clack) when run with no arguments, plus a
fully scriptable flag-based interface with
--jsonfor piping into other tools.
Quick start
Run it instantly, no install required:
npx bestdnsOr just benchmark and apply the fastest DNS in one go:
npx bestdns autoChanging DNS requires admin privileges. On macOS and Linux you'll be prompted for your password via
sudo; on Windows a UAC prompt appears. Use--dry-runto preview the exact command without executing it.
Installation
npx (recommended)
No install — always runs the latest version:
npx bestdnsGlobal
Install once, run anywhere:
npm install -g bestdns
bestdnsPer-project / Bun / pnpm / Yarn
bun add -d bestdns # Bun
pnpm add -D bestdns # pnpm
yarn add -D bestdns # YarnThen call it via your package manager's runner, e.g. bun run bestdns or as an npm
script.
Usage
bestdns has two faces — pick whichever fits the task.
Interactive menu — run with no arguments to get an arrow-key navigated menu:
┌ ⚡ bestdns v1.0.1
│
◆ What would you like to do?
│ ● ⚡ Auto — benchmark & apply the fastest (recommended)
│ ○ 🔍 Benchmark DNS providers
│ ○ 📡 Show current DNS
│ ○ 🎯 Apply a DNS provider
│ ○ 🩺 Health & capability check
│ ○ 📋 Manage provider list
│ ○ ↩️ Restore DNS to automatic
│ ○ 👋 ExitScriptable — every action has a matching subcommand with flags and --json:
bestdns benchmark --group security --top 5 --json | jq '.[0]'
bestdns current --json | jq '.[] | select(.active)'
bestdns apply cloudflare --dry-runCommands
| Command | Description |
| --- | --- |
| bestdns | Open the interactive menu |
| bestdns optimize | Diagnose, apply auto-fixes, re-test (before/after) |
| bestdns diagnose | Read-only network checkup (alias: doctor) |
| bestdns hogs | Top network-hungry processes (alias: top) |
| bestdns profiles | Manage saved Wi-Fi / location / NM-connection profiles |
| bestdns auto | Benchmark every provider and apply the fastest |
| bestdns benchmark | Benchmark providers and rank them by speed |
| bestdns apply <provider> | Apply a DNS provider to your network |
| bestdns current | Show the current DNS configuration |
| bestdns health [provider] | Check DNSSEC, hijacking, ad-blocking and DoH |
| bestdns list | Browse and manage the provider catalog |
| bestdns restore | Restore DNS to automatic (DHCP) |
| bestdns menu | Force-open the interactive menu (even with args) |
bestdns optimize
End-to-end network optimization in three phases:
- Baseline snapshot — runs every diagnostic check and lists findings.
- Apply auto-fixes — walks through each auto-fixable finding (swap DNS, flush DNS cache, restart mDNSResponder on macOS) with per-step confirmation.
- Re-test — takes a second snapshot and prints a before/after comparison.
-y, --yes apply every offered fix without confirming
--no-speedtest skip the download speedtest in both snapshots
--no-mtu skip the path-MTU probe
--no-retest skip the second (after) snapshotbestdns optimize # interactive flow with confirmations
bestdns optimize --yes # apply every offered fix
bestdns optimize --no-speedtest --no-mtu # quick run: skip the slow checksWhat each auto-fix actually does:
| Fix | What runs | Privilege |
| --- | --- | --- |
| Swap DNS | Internally calls bestdns auto — benchmark and apply the fastest provider to the active network service | sudo / UAC |
| Flush DNS cache | macOS: dscacheutil -flushcache && killall -HUP mDNSResponder · Linux: resolvectl flush-caches (fallback nscd -i hosts) · Windows: ipconfig /flushdns | sudo on macOS / Linux |
| Restart mDNSResponder | launchctl kickstart -k system/com.apple.mDNSResponder | sudo, macOS only |
bestdns diagnose
Read-only network checkup — useful for "is it me or the network?" before changing
anything. Runs everything optimize runs, but applies nothing.
--no-speedtest skip the download speedtest
--no-mtu skip the path-MTU probe
--json output machine-readable JSONbestdns diagnose # full report
bestdns diagnose --no-speedtest --no-mtu # quick (~3 s) check
bestdns diagnose --json | jq '.findings' # pipe findings into another tool
bestdns doctor # aliasWhat it measures:
| Check | How |
| --- | --- |
| Download speed | Streams up to 50 MB from Cloudflare's public speedtest endpoint with an 8-second deadline. |
| Latency | 4 pings each to 1.1.1.1, 8.8.8.8, github.com. |
| Packet loss | 20 pings to 1.1.1.1. |
| Path MTU | DF-bit pings at common candidate sizes (1500 → 1280). |
| Wi-Fi signal | Native tools: airport -I on macOS, iw dev on Linux, netsh wlan show interfaces on Windows. |
| Current DNS | Three quick lookups against the active resolver. |
Findings are sorted by severity (ISSUE → WARN → INFO → OK) and tagged
"auto-fix available" when optimize can resolve them.
Example output:
Download 124.8 Mbps (50.0 MB in 3.4 s)
Latency 42 ms avg across 3 target(s)
Packet loss 0.0% (20/20 to 1.1.1.1)
Path MTU 1492
Current DNS 8.8.8.8, 8.8.4.4 (30 ms avg)
┌───┬─────────┬────────────────────────────────┬─────────────────────────────────────┐
│ │ Severity│ Finding │ Detail │
├───┼─────────┼────────────────────────────────┼─────────────────────────────────────┤
│ ℹ │ INFO │ Path MTU is 1492 │ Common when on PPPoE, GRE or a VPN │
│ ℹ │ INFO │ Flush the OS DNS cache │ (auto-fix available) │
│ ℹ │ INFO │ Restart mDNSResponder │ (auto-fix available) │
│ ✔ │ OK │ Download throughput is healthy │ Measured 124.8 Mbps over 3.4 s. │
│ ✔ │ OK │ No packet loss │ 20/20 packets received from 1.1.1.1 │
│ ✔ │ OK │ Internet latency looks fine │ 42 ms average across 3 hosts. │
│ ✔ │ OK │ Current DNS is fast │ Current resolver averages 30 ms. │
└───┴─────────┴────────────────────────────────┴─────────────────────────────────────┘bestdns hogs
Lists the processes currently generating the most network traffic. Informational only — bestdns never kills, throttles or sandboxes anything; you decide.
-t, --top <n> show only the top N processes (default 10)
--json output machine-readable JSONbestdns hogs # top 10 by bytes (mac) / connections (Linux/Win)
bestdns hogs --top 3 # only the loudest 3
bestdns hogs --json | jq '.hogs[0]' # script-friendly
bestdns top # alias| OS | Mechanism | Metric |
| --- | --- | --- |
| macOS | nettop -P -L 1 -J bytes_in,bytes_out -x | Cumulative bytes received / sent per process |
| Linux | ss -tunap | Established TCP connections per process (re-run with sudo to resolve names) |
| Windows | Get-NetTCPConnection \| Group-Object OwningProcess | Established TCP connections per process |
Example output:
┌───────┬─────────────────┬─────────┬─────────┬─────────┐
│ PID │ Process │ Recv │ Sent │ Total │
├───────┼─────────────────┼─────────┼─────────┼─────────┤
│ 90801 │ Google Chrome H │ 67.6 MB │ 873 KB │ 68.5 MB │
│ 44194 │ com.aikido.endp │ 12.7 MB │ 45.5 MB │ 58.3 MB │
│ 541 │ com.crowdstrike │ 2.2 MB │ 36.4 MB │ 38.7 MB │
└───────┴─────────────────┴─────────┴─────────┴─────────┘bestdns profiles
Manage every saved network credential the OS knows about: Wi-Fi networks, macOS
network locations, NetworkManager connections. Two flavours of cleanup — a
single-item remove and an interactive multi-select prune.
bestdns profiles # list every saved profile (default: list)
bestdns profiles list --json # JSON output
bestdns profiles remove "Old Coffee Shop" # remove a single Wi-Fi / connection
bestdns profiles remove # interactive picker
bestdns profiles prune # multi-select stale ones, bulk remove| OS | Lists | Removes via |
| --- | --- | --- |
| macOS | Preferred Wi-Fi networks + network locations | networksetup -removepreferredwirelessnetwork / -deletelocation (sudo) |
| Linux | All NetworkManager connections | nmcli connection delete <name> (sudo) |
| Windows | Saved WLAN profiles | netsh wlan delete profile name="<name>" (elevated) |
Active profiles (the Wi-Fi you're currently on, the active location) are flagged
and excluded from prune so they can't be removed accidentally — use the
explicit remove <name> form if you really mean it.
bestdns benchmark
-g, --group <category> limit to a category: non-filtering | security | family | all
-r, --rounds <n> measured rounds per provider (default 5)
-t, --top <n> show only the fastest N providers
--json output machine-readable JSONbestdns benchmark # all categories, full table
bestdns benchmark --group security # only ad-blocking / security resolvers
bestdns benchmark --top 5 --json # fastest 5 in JSONOutput example:
┌─────┬───────────────────────┬─────────────────────────┬─────────┬─────────┬───────────┬─────────────┐
│ # │ Provider │ Group │ Avg │ Fastest │ Jitter │ Reliability │
├─────┼───────────────────────┼─────────────────────────┼─────────┼─────────┼───────────┼─────────────┤
│ ★ 1 │ Google Public DNS │ Non-filtering │ 34.0 ms │ 23.9 ms │ ± 12.6 ms │ 100% │
│ │ 8.8.8.8 │ │ │ │ │ │
│ 2 │ Cloudflare │ Non-filtering │ 50.2 ms │ 40.4 ms │ ± 4.9 ms │ 100% │
│ │ 1.1.1.1 │ │ │ │ │ │
│ 3 │ AdGuard DNS (Default) │ Security & Ad-blocking │ 52.7 ms │ 41.3 ms │ ± 8.0 ms │ 100% │
│ │ 94.140.14.14 │ │ │ │ │ │
└─────┴───────────────────────┴─────────────────────────┴─────────┴─────────┴───────────┴─────────────┘bestdns apply
-s, --service <name> target a specific network service
--ipv6 also configure IPv6 addresses
--dry-run print the command without running it
-y, --yes skip the confirmation promptbestdns apply cloudflare # interactive confirmation
bestdns apply quad9 --yes # no confirmation
bestdns apply adguard --ipv6 # also set IPv6
bestdns apply cloudflare --dry-run # see the exact networksetup / nmcli /
# PowerShell command without running itbestdns current
bestdns current # human-readable
bestdns current --json # machine-readablebestdns health
bestdns health cloudflare # detailed report for one provider
bestdns health --group security # capability matrix across a group
bestdns health --group all --json # full matrix in JSONbestdns list
bestdns list # show every provider (built-in + your custom)
bestdns list groups # show categories with provider counts
bestdns list add # interactively add a custom provider
bestdns list remove <id> # remove a custom providerbestdns restore
bestdns restore # revert to automatic / DHCP
bestdns restore -s "Wi-Fi" # target a specific serviceProvider catalog
The catalog follows the taxonomy from the AdGuard DNS knowledge base.
🚀 Non-filtering — fast & private
Pure resolvers with no content filtering. Use these when you want raw speed and don't need ad-blocking at the DNS layer.
| Provider | IPv4 |
| --- | --- |
| Cloudflare | 1.1.1.1, 1.0.0.1 |
| Google Public DNS | 8.8.8.8, 8.8.4.4 |
| Quad9 (Unfiltered) | 9.9.9.10, 149.112.112.10 |
| OpenDNS (Cisco) | 208.67.222.222, 208.67.220.220 |
| AdGuard DNS (Non-filtering) | 94.140.14.140, 94.140.14.141 |
🛡️ Security & Ad-blocking
Block ads, trackers, malware and phishing domains at the DNS layer.
| Provider | IPv4 |
| --- | --- |
| AdGuard DNS (Default) | 94.140.14.14, 94.140.15.15 |
| Cloudflare (Malware Blocking) | 1.1.1.2, 1.0.0.2 |
| Quad9 | 9.9.9.9, 149.112.112.112 |
| CleanBrowsing (Security) | 185.228.168.9, 185.228.169.9 |
| Mullvad (Ad-blocking) | 194.242.2.3 |
| Control D (Malware + Ads) | 76.76.2.2, 76.76.10.2 |
| DNS0.eu | 193.110.81.0, 185.253.5.0 |
👨👩👧 Family-safe
Block adult content on top of security filtering — useful for shared / kids' devices.
| Provider | IPv4 |
| --- | --- |
| AdGuard DNS (Family Protection) | 94.140.14.15, 94.140.15.16 |
| Cloudflare for Families | 1.1.1.3, 1.0.0.3 |
| CleanBrowsing (Family) | 185.228.168.168, 185.228.169.168 |
| OpenDNS FamilyShield | 208.67.222.123, 208.67.220.123 |
Adding your own
bestdns list addYou'll be prompted for an id, name, category, IPv4 addresses, optional IPv6, and an
optional DoH endpoint. Custom providers persist in
custom-providers.json and appear alongside built-ins
everywhere.
Health checks
For each provider, bestdns health reports:
| Check | What it tests | How |
| --- | --- | --- |
| Reachable | Server answered a basic query | Resolve example.com |
| DNSSEC validation | Server validates DNS signatures | Resolve dnssec-failed.org — validators return SERVFAIL, lax resolvers return an address |
| No NXDOMAIN hijacking | Server honestly says "doesn't exist" rather than redirecting to an ad page | Resolve a random unguessable subdomain |
| Ad / tracker blocking | Server blocks known ad / tracker hostnames | Resolve a handful of known trackers; count those that fail or resolve to 0.0.0.0 |
| DNS-over-HTTPS reachable | DoH endpoint responds correctly | RFC 8484 wire-format query over HTTPS |
Configuration & data files
bestdns stores its data in the standard per-OS config directory:
| OS | Location |
| --- | --- |
| macOS | ~/Library/Preferences/bestdns/ |
| Linux | $XDG_CONFIG_HOME/bestdns/ or ~/.config/bestdns/ |
| Windows | %APPDATA%\bestdns\Config\ |
Files:
config.json— app settings (last benchmark winner, preferred category)custom-providers.json— user-added DNS providersbackups.json— last 50 DNS snapshots captured before eachapply(used byrestore)
Everything is plain JSON — safe to inspect, edit, or back up.
Platform support
| OS | Read DNS | Apply DNS | Mechanism |
| --- | --- | --- | --- |
| macOS | ✓ | ✓ | networksetup |
| Linux | ✓ | ✓ | nmcli (NetworkManager) → resolvectl (systemd-resolved) → /etc/resolv.conf |
| Windows | ✓ | ✓ | PowerShell DnsClient cmdlets, elevated via UAC |
apply always:
- Backs up the current DNS configuration first.
- Asks for confirmation (unless
--yes). - Elevates only the mutation step — never the whole process.
- Shows the exact command it ran (and you can preview with
--dry-run).
How it works
- Benchmark — for each provider's primary IPv4 it creates a
node:dnsResolver pinned to that server, resolves a fixed set of test domains plus one guaranteed cache-miss random domain over multiple rounds, and computesavg + jitter + reliabilityinto a composite score (lower is better). - Health — DNSSEC, NXDOMAIN-hijacking, and ad-block tests use the same Resolver; the DoH check sends a real RFC 8484 wire-format query so it works against every provider, not just the ones with a JSON API.
- Apply — per-OS backends build the appropriate command, spawn it via
sudo/Start-Process -Verb RunAsif needed, and verify by re-reading the DNS setting.
The whole thing is bundled with Bun and shipped as a single
Node-compatible JS file, so npx bestdns works on any Node ≥ 18 with no dependencies
to install.
Development
Prerequisites: Bun ≥ 1.3, Node ≥ 18.
git clone https://github.com/hashcott/bestdns.git
cd bestdns
bun install
bun run dev # run the CLI from source
bun test # run the test suite
bun run typecheck # type-check with tsc
bun run lint # lint & format check with Biome
bun run lint:fix # auto-fix lint / formatting
bun run build # bundle to dist/bestdns.jsProject layout
src/
├── index.ts entry, update-notifier, router
├── cli.ts commander program (subcommands + flags)
├── menu.ts interactive clack menu
├── commands/ one file per top-level command
├── core/ resolver, benchmark, health, doh
├── os/ macos / linux / windows backends
├── store/ config, custom providers, backups
├── data/providers.ts built-in DNS catalog
└── ui/ theme, tables, promptsContributing
PRs and issues are very welcome. A few guidelines:
- Commit using Conventional Commits. A
Husky
commit-msghook validates this locally;semantic-releasereads the same history to decide version bumps.fix:→ patchfeat:→ minorfeat!:orBREAKING CHANGE:→ majorchore:/docs:/ci:/refactor:/test:→ no release
- Run
bun run lint && bun run typecheck && bun testbefore pushing. CI runs the same on Ubuntu, macOS and Windows. - Keep commits focused. One thing per commit; rebase / amend rather than "fixup" noise.
- Adding a provider? Edit
src/data/providers.ts, include a primary + secondary IPv4 (IPv6 / DoH / DoT if available), and add the entry to the README provider tables.
Reporting bugs
Open an issue with:
- Your OS and
bestdns --version - The command you ran and the output (
--jsonoutput is great) - For
applyissues, the output ofbestdns apply <provider> --dry-run
Releasing (maintainers)
Releases are fully automated by semantic-release:
- Push a
feat:/fix:commit tomain→ CI bumps the version, updatesCHANGELOG.md, tags the release, publishes to npm and creates a GitHub release. - No manual
npm publish, no manual version bumps inpackage.json.
One-time setup (already done on this repo):
NPM_TOKENsecret in Settings → Secrets → Actions (npm Automation or Granular token with publish rights).repository,bugs,homepageinpackage.jsonpointing at the GitHub repo.
Roadmap
Ideas welcome — open an issue if you want to take any of these on:
- [x] End-to-end network optimization flow (
optimize) — shipped in 1.1.0 - [x] Read-only diagnostic mode (
diagnose) — shipped in 1.1.0 - [x] Bandwidth-hog process listing (
hogs) — shipped in 1.2.0 - [x] Network locations / Wi-Fi profile cleanup helpers (
profiles) — shipped in 1.2.0 - [ ] Wi-Fi info on modern macOS via
wdutil/SPAirPortDataTypeJSON (airport -Iwas removed in recent versions) - [ ] DoT / DoH apply on macOS via configuration profiles
- [ ] DoH benchmarking as a first-class result column
- [ ] Per-network-profile presets ("work", "home", "travel")
- [ ] Per-domain forwarding rules
- [ ] Watch mode that re-benchmarks when the network changes
- [ ] Web UI (
bestdns serve) for a local dashboard
Acknowledgments
Built with the help of these excellent open-source projects:
- Bun — runtime, bundler, package manager and test runner
- @clack/prompts — beautiful CLI prompts
- commander — argument parsing
- cli-table3 — terminal tables
- picocolors — tiny color library
- semantic-release — automated releases
- Biome — lint + formatter
- AdGuard DNS knowledge base — taxonomy and addresses for the built-in catalog
License
MIT © bestdns contributors
