A caching DNS proxy with advanced security features
You'll find the full documentation on the EtchDNS website.
Key Features • Quickstart • Installation • Configuration • Use Cases • Advanced Features • Performance Tuning • Security • Development • License
EtchDNS is a caching DNS proxy.
It sits between your clients and upstream DNS servers to make DNS more secure and reliable. It caches responses, spreads queries across servers, and protects your DNS traffic.
Where is this useful?
- Deploying zero-setup, maintenance-free secondary DNS servers
- Improving DNS performance and security for an organization
- Protecting a network you manage from DNS-based attacks
- Taking DNS traffic off primary servers as a service provider
- Getting more control over DNS resolution when you care about privacy
- Building your own DNS processing with WebAssembly
- Running a secondary authoritative DNS server with any provider, without zone transfers
- Putting a public or local DNS cache in front of a resolver
- Sitting between a DNSCrypt server proxy and a resolver
- Download or clone the repository.
- Edit a copy of the
etchdns.tomlconfiguration file. - Run EtchDNS:
etchdns -c /path/to/etchdns.toml-
Caching: Uses the SIEVE algorithm to make the best use of memory
-
Query aggregation: Combines duplicate queries that are still waiting for a response, so upstream servers do less work
-
Load balancing: Spreads queries across servers using your choice of strategy:
- fastest
- p2
- random
-
EDNS-Client-Subnet: Improves CDN and location-based DNS responses
-
Protocols:
- UDP/TCP (standard DNS)
- Basic DoH (DNS-over-HTTP)
-
Planned protocols for better security and privacy:
- DNSCrypt
- PQDNSCrypt
- Anonymized DNSCrypt
-
Domain filtering: Allow or block domains with allowed/NX zones
-
IP validation: Blocks suspicious IP ranges, checks client ports, and prevents address spoofing
-
Rate limiting: Gives you fine-grained control for each protocol:
- UDP
- TCP
- DoH
-
Transaction ID masking: Protects against cache poisoning attacks
-
Privilege dropping: Runs with minimal system access after startup, with settings for:
- user
- group
- chroot
-
Request validation: Checks DNS packets thoroughly
- Automatic failover: Detects server outages immediately and keeps routing queries without interruption
- Serve stale: Keeps serving expired cache entries when upstream servers fail
- Health monitoring: Checks upstream servers regularly, at intervals you set
- Latency guarantees: Keeps response times consistent even when upstream servers slow down
- Connection limits: Gives you separate limits for each protocol and for queries still waiting for a response
- Prometheus metrics: Gives you a full view of server activity through a Prometheus endpoint
- Remote control API: Lets you check server status and manage the cache over HTTP
- Query logging: Lets you choose which query details to log
- Log rotation: Rotates logs by size or time, with compression support
-
Plugins: Write custom plugins in any language that compiles to WebAssembly, including:
- C/C++
- Zig
- AssemblyScript
-
Custom filters: Write filtering rules that go beyond static blocklists
-
Response changes: Change DNS responses based on your own business rules
-
Stateful processing: Keep state across DNS queries to enforce complex policies
# Secondary DNS server mode
authoritative_dns = trueThis makes EtchDNS a secondary authoritative DNS server for your zones. It handles client requests, takes load off your primary servers, and keeps service running while protecting against common attacks.
You can use it with any DNS provider. No zone transfers needed.
# Cache mode
authoritative_dns = falsePoint your devices at EtchDNS as their DNS resolver. It caches responses and spreads queries across multiple upstream servers to improve performance, reliability, and security. You can run it as a cache for your local network or as a public DNS service.
# Domain blocklist configuration
nx_zones_file = "nx_zones.txt"
# IP address validation and filtering
enable_strict_ip_validation = true
block_private_ips = true
block_loopback_ips = true
blocked_ip_ranges = ["203.0.113.0/24", "198.51.100.0/24"]
min_client_port = 1024This puts a protective layer in front of your network.
Add domains to nx_zones.txt to return NXDOMAIN responses for them, blocking:
- Malicious domains
- Ads
- Unwanted content
You can also use IP validation to block connections from suspicious or problematic IP ranges.
EtchDNS can sit between a DNSCrypt server proxy, such as encrypted-dns-server, and your resolver to reduce load and improve reliability.
# WebAssembly hooks
hooks_wasm_file = "hooks.wasm"
hooks_wasm_wasi = false # Set to true if your plugin needs WASI supportThese settings let you use WebAssembly for custom DNS processing:
- Custom filtering rules
- Monitoring
- Changes to DNS queries and responses
Download the latest release from the releases page.
- Make sure you have Rust and Cargo installed.
- Clone this repository.
- Build the release version:
cargo build --releaseYou'll find the executable at target/release/etchdns.
EtchDNS builds without WebAssembly hooks by default to keep the binary smaller.
To enable them, use the hooks feature flag:
cargo build --release --features hooksThis adds a WebAssembly runtime and makes the binary significantly larger. Only enable it if you plan to use WebAssembly extensions.
You control all of EtchDNS's behavior through a TOML configuration file.
The included etchdns.toml has a complete, documented example.
Here's what you can configure:
-
Basic server settings:
- Listen addresses
- Log level
- Packet size limits
-
Upstream DNS servers: Servers to forward queries to
-
Load balancing: Strategy and probe interval
-
Rate limiting: Settings for each protocol
-
Caching: Cache size and TTL settings
-
Domain filtering: Allowed and blocked zones
-
IP validation: Filtering and security options for client source IP addresses
-
EDNS-client-subnet: Whether it's enabled and which prefix lengths to use
-
Security: Settings for dropping privileges
Put the domains you want to allow in a text file:
# Company domains
example.com
example.org
# Third-party services
github.com
google.com
Put the domains that should return NXDOMAIN in a text file:
# Advertising domains
ads.example.com
analytics.example.com
# Known malicious domains
malware.example.net
EtchDNS supports EDNS-client-subnet (ECS), as defined in RFC 7871. When enabled, ECS shares client IP information with upstream servers to improve CDN and location-based DNS responses.
# EDNS-Client-Subnet configuration
enable_ecs = true
ecs_prefix_v4 = 24 # Send first 24 bits of IPv4 address (hide last 8 bits)
ecs_prefix_v6 = 56 # Send first 56 bits of IPv6 address (hide last 72 bits)These settings enable ECS, so upstream queries include client IP information and DNS providers can tailor responses to the client's location. The prefix lengths set how much of the client's IP address you share with upstream servers. That's the tradeoff between performance and privacy.
# Control API setup
control_listen_addresses = ["127.0.0.1:8080"]
control_path = "/control"These settings enable the HTTP API for remote management. You can use these endpoints:
-
GET /control/status: Get full server status, including:- Uptime
- Connection stats
- Health information
-
GET /control/cache: Get cache status, including:- Size
- Hit/miss rates
- Entry counts
-
DELETE /control/cache: Clear the entire cache -
DELETE /control/cache/zone/<example.com>: Clear all entries for a specific zone -
DELETE /control/cache/name/<example.com>: Clear a specific entry by name
Note: WebAssembly extensions work, but they're still under active development. Expect API changes and more features in future releases.
You can write your own DNS processing logic as a WebAssembly module. Any language that compiles to WebAssembly works, including:
- C/C++
- Zig
- AssemblyScript
- Go/TinyGo
- Many others, through Extism
Important: You must build EtchDNS with the
hooksfeature flag to use WebAssembly extensions:cargo build --release --features hooksThis adds the Extism WebAssembly runtime and makes the binary significantly larger.
- Language choice: Write extensions in the language you prefer
- Sandboxing: Extensions run in a secure sandbox with minimal overhead
- Hot reloading: Update extensions without restarting EtchDNS
- Stateful processing: Keep state across DNS queries to enforce complex policies
- Extism: Uses the Extism plugin system, with optional WASI support
The current implementation supports this hook:
-
hook_client_query_received: Called when a client query is received, before checking the cache- Return code 0: Continue normal processing
- Return code -1: Return a minimal response with the REFUSED response code (rcode)
Hooks receive query information as structured JSON. You can process that data in any language that compiles to WebAssembly.
You'll find an example WebAssembly plugin in webassembly-plugins.
It shows how to write a simple plugin that changes how DNS queries are processed.
Set the path to your compiled WASM file in the configuration:
# WebAssembly hooks
hooks_wasm_file = "hooks.wasm"
hooks_wasm_wasi = false # Set to true if your plugin needs WASI supportSee the WebAssembly Extension Guide in Development for details on building your own extensions.
To get the best performance, start with these settings:
-
Cache size: Increase
cache_sizeto match the memory you have available -
Client limits: Adjust these for your environment:
max_udp_clientsmax_tcp_clients
-
Load balancing: Choose:
fastestfor the highest performancep2for a good balance
-
Serve stale: Enable
serve_stale_grace_timeto improve reliability -
Rate limiting: Set limits that prevent DoS while letting legitimate traffic through
-
In-flight queries: Adjust
max_inflight_queriesto make query aggregation more efficient -
TTL settings: Adjust the TTL settings to get the most out of the cache
-
Probe interval: Set
probe_intervalto balance load balancer accuracy against network overhead
You can use these features to protect both clients and upstream servers:
- Run with minimal privileges: Use the privilege dropping feature
- Domain filtering: Choose which queries are processed
- IP validation: Block connections from suspicious or problematic source addresses
- Rate limiting: Prevent abuse by limiting queries per client
- Transaction ID masking: Protect against cache poisoning attacks
IP validation lets you choose which client IP addresses can use your server:
# Enable strict IP validation
enable_strict_ip_validation = true
# Block private IP address ranges (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16)
block_private_ips = true
# Block loopback IP address ranges (127.0.0.0/8, ::1)
block_loopback_ips = true
# Minimum port to allow from clients (ports below this will be rejected)
min_client_port = 1024
# List of blocked IP ranges
blocked_ip_ranges = ["203.0.113.0/24", "198.51.100.0/24"]These settings help protect against:
- IP spoofing
- Abuse from internal networks
- Connections from known problematic IP ranges
Client port validation also blocks connections from privileged ports, which are commonly used in spoofing attacks.
cargo testThis runs the standard unit tests.
EtchDNS has a full set of fuzzing tests for its DNS parsers.
-
Install cargo-fuzz:
cargo install cargo-fuzz
-
Run a specific fuzz target:
cargo fuzz run validate_dns_packet
See fuzz/README.md for more details on the available targets.
To build your own WebAssembly extension:
-
Make sure you've compiled EtchDNS with hooks support:
cargo build --release --features hooks
-
Add the WebAssembly target to your Rust toolchain:
rustup target add wasm32-unknown-unknown
-
Build the example Rust plugin or your own extension:
cd webassembly-plugins/rust cargo build --target wasm32-unknown-unknown --release -
You'll find the compiled WebAssembly module at
webassembly-plugins/rust/target/wasm32-unknown-unknown/release/hooks_plugin.wasm -
Copy the WebAssembly module to your EtchDNS directory and update the configuration:
cp target/wasm32-unknown-unknown/release/hooks_plugin.wasm /path/to/etchdns/hooks.wasm
Or you can build the Zig example plugin:
-
Go to the Zig plugin directory:
cd webassembly-plugins/zig -
Build the Zig plugin:
zig build
-
You'll find the compiled WebAssembly module in
zig-out/bin/
For other languages, check their WebAssembly build guides. Your WASM module must export functions that match EtchDNS's hook interface.
If your WebAssembly plugin needs system resources, you can enable WASI support. Those resources include:
- The file system
- Environment variables
# Enable WASI for WebAssembly hooks
hooks_wasm_wasi = trueThis lets your plugin use WASI system calls, but it increases the security risk. Only enable it if your plugin specifically needs those capabilities.
Note: If your EtchDNS binary was built without the
hooksfeature, WebAssembly hooks won't work. Any hook-related configuration will be ignored.
EtchDNS is licensed under the MIT License.
-
Protocols: Future versions may support:
- DNSCrypt
- Anonymized DNS
This may involve porting functionality from encrypted-dns-server.
Note: DoH support is currently limited to traditional DoH, not Oblivious DoH (ODoH). If you need a mature DoH server that's been tested in real use, consider doh-server instead.
