Comprehensive guides to help you install, configure, and get the most out of EtchDNS.
Download the latest release from the releases page.
cargo build --release
The executable will be available at
target/release/etchdns.
By default, EtchDNS is built without WebAssembly hooks support
to keep the binary size smaller. If you want to enable the hooks
functionality, build with the hooks feature flag:
cargo build --release --features hooks
Note that enabling the hooks feature includes a WebAssembly runtime which significantly increases the binary size. Only enable this feature if you plan to use WebAssembly extensions.
EtchDNS uses a TOML configuration file to control all aspects of
its behavior. A complete example with documentation can be found
in the included etchdns.toml file.
# Basic server configuration
listen_addresses = ["127.0.0.1:53", "[::1]:53"]
log_level = "warn"
max_udp_packet_size = 1232
max_tcp_packet_size = 65535
# Upstream DNS servers
upstream_servers = [
"8.8.8.8:53",
"1.1.1.1:53",
"9.9.9.9:53"
]
server_timeout = 2.0 # seconds
To run EtchDNS with your configuration file:
etchdns -c /path/to/etchdns.toml
Create a text file with domains that should be allowed:
# Company domains
example.com
example.org
# Third-party services
github.com
google.com
Then reference this file in your configuration:
allowed_zones_file = "allowed_zones.txt"
Create a text file with domains that should return NXDOMAIN:
# Advertising domains
ads.example.com
analytics.example.com
# Known malicious domains
malware.example.net
Then reference this file in your configuration:
nx_zones_file = "nx_zones.txt"
EtchDNS supports multiple load balancing strategies to distribute queries across upstream DNS servers:
# Load balancing configuration
load_balancing_strategy = "fastest" # Options: "random", "fastest", "p2"
probe_interval = 60.0 # seconds
EtchDNS implements an efficient DNS caching system using the SIEVE algorithm which provides optimal memory usage and fast lookups:
# Cache configuration
cache_size = 10000 # Number of entries
cache_ttl_cap = 86400 # Maximum 1 day cache time, even if TTL is longer
cache_in_memory_only = false # Set to true to disable mmap-based caching
negative_cache_ttl = 60 # Seconds for negative responses
min_cache_ttl = 1 # Minimum seconds to cache responses
ttl_multiply_factor = 1.0 # Optionally adjust all TTLs (e.g. 0.5 = half)
serve_stale_grace_time = 86400 # Seconds (1 day) to serve stale entries during failures
serve_stale_ttl = 30 # TTL for stale entries when served
prefetch_popular_queries = false # Set to true to preemptively refresh popular entries
prefetch_threshold = 10 # Prefetch entries seen at least this many times
EtchDNS provides flexible TTL (Time To Live) management to balance freshness and performance:
The "serve stale" feature allows EtchDNS to return expired DNS cache entries when upstream resolution fails or times out. This improves reliability during upstream server outages.
EtchDNS can proactively refresh popular cache entries before they expire, ensuring seamless responses for frequently accessed domains:
EtchDNS provides an HTTP API for cache management via the control interface:
GET /control/cache: View cache statisticsDELETE /control/cache: Clear the entire cacheDELETE /control/cache/zone/<example.com>: Clear all entries for a specific zoneDELETE /control/cache/name/<www.example.com>: Clear a specific cached name# IP validation configuration
enable_strict_ip_validation = false # Enable advanced IP validation features
block_private_ips = false # Block 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16
block_loopback_ips = false # Block 127.0.0.0/8, ::1
min_client_port = 1024 # Block connections from privileged ports
blocked_ip_ranges = [ # Custom CIDR blocks to reject
# "203.0.113.0/24" # Example: Block TEST-NET-3 range
]
IP validation provides protection against spoofed addresses and requests from potentially malicious sources. When enabled, each client connection is validated before processing DNS requests. The system includes:
Connections from blocked IPs are silently dropped without any response, increasing security by not revealing the server's existence to potentially malicious clients. The recently improved implementation provides more efficient connection filtering with minimal impact on performance.
# Rate limiting configuration
max_udp_clients = 1000
max_tcp_clients = 100
max_doh_connections = 100
rate_limit_udp = 100 # queries per second per client
rate_limit_tcp = 20 # queries per second per client
rate_limit_doh = 10 # queries per second per client
rate_limit_table_size = 50000
# Privilege dropping
user = "nobody"
group = "nogroup"
chroot = "/var/empty"
EtchDNS can log DNS queries to a file for monitoring, troubleshooting, and security analysis. Query logging is configurable and supports automatic log rotation.
# Query logging configuration
query_log_file = "queries.log" # Path to log file (empty to disable logging)
query_log_include_timestamp = true # Include timestamp in log entries
query_log_include_client_addr = true # Include client IP address
query_log_include_query_type = true # Include DNS query type (e.g., A, AAAA, MX)
query_log_include_query_class = false # Include DNS query class
EtchDNS supports automatic log rotation to manage log file sizes and implement log retention policies. Log files can be rotated based on size, time intervals, or both. Rotated log files can optionally be compressed.
# Log rotation configuration
query_log_rotation_size = 10485760 # Rotate when log reaches 10MB (0 to disable)
query_log_rotation_interval = "daily" # Options: hourly, daily, weekly, monthly, never
query_log_rotation_count = 7 # Keep 7 rotated log files (older ones are deleted)
query_log_compression = true # Compress rotated logs with gzip
The log rotation system works as follows:
original_name.timestampNote: For production environments, it's recommended to enable log rotation to prevent logs from consuming too much disk space. A common configuration is daily rotation with 7-30 days of retention, depending on your compliance and troubleshooting needs.
# Control API setup
control_listen_addresses = ["127.0.0.1:8080"]
control_path = "/control"
Available endpoints:
GET /control/status: Server statusGET /control/cache: Get cache status information
DELETE /control/cache: Clear entire cacheDELETE /control/cache/zone/<example.com>:
Clear entries for a specific zone
DELETE /control/cache/name/<example.com>:
Clear a specific entry
EtchDNS can be extended through WebAssembly modules, allowing you to implement custom DNS processing logic in any language that compiles to WebAssembly.
# WebAssembly hooks configuration
hooks_wasm_file = "hooks.wasm"
hooks_wasm_wasi = false # Set to true if your plugin needs WASI support
WebAssembly (Wasm) allows you to extend EtchDNS's functionality without modifying the core code. You can write custom logic to filter queries, modify responses, implement custom logging, or any other processing you need.
EtchDNS currently supports the following hook points:
Zig is a modern, high-level programming language that compiles efficiently to WebAssembly and is well-suited for
creating EtchDNS extensions. EtchDNS includes a complete Zig-based example plugin in the
webassembly-plugins/zig/ directory.
To build a custom extension with Zig:
webassembly-plugins/zig/src/main.zigcd webassembly-plugins/zig
zig build
# The output will be in zig-out/lib/hooks.wasm
The Zig example illustrates how to implement a simple plugin that filters domains based on certain criteria:
// Sample from webassembly-plugins/zig/src/main.zig
export fn hook_client_query_received(
qname_ptr: [*]const u8,
qname_len: usize,
qtype: u16,
qclass: u16,
) i32 {
// Access DNS query fields
const qname = qname_ptr[0..qname_len];
// Example filtering logic
if (std.mem.indexOf(u8, qname, "ads") != null) {
// Block queries containing "ads"
return -1;
}
// Allow all other queries
return 0;
}
EtchDNS exports the following interfaces to WebAssembly modules:
log_debug(message_ptr: *const u8, message_len: usize): Log a debug messagelog_info(message_ptr: *const u8, message_len: usize): Log an info messagelog_warn(message_ptr: *const u8, message_len: usize): Log a warning messagelog_error(message_ptr: *const u8, message_len: usize): Log an error messageget_configuration_value(key_ptr: *const u8, key_len: usize, value_ptr: *u8, value_max_len: usize): Get a configuration valueset_response_ttl(ttl: u32): Modify the TTL of a DNS responsehook_client_query_received(qname_ptr: *const u8, qname_len: usize, qtype: u16, qclass: u16) -> i32:
Return 0 to allow the query, -1 to block it, or a positive value for a custom response code.To use a WebAssembly extension:
cargo build --release --features hooks.wasm file in an accessible locationetchdns.toml:# WebAssembly hooks configuration
hooks_wasm_file = "/path/to/your/hooks.wasm"
hooks_wasm_wasi = false # Set to true if your plugin needs WASI support
For more details on building WebAssembly extensions, see the
WebAssembly Extension Guide
and the examples in the webassembly-plugins/ directory.
For optimal performance, consider these configuration guidelines:
cache_size based on available memory
max_udp_clients and
max_tcp_clients for your environment
fastest for
highest performance, p2 for a good balance
serve_stale_grace_time for improved reliability
The following tutorials provide step-by-step instructions for common EtchDNS deployment scenarios.
This tutorial demonstrates how to set up EtchDNS as a secondary authoritative DNS server.
etchdns.toml with the following content:# Secondary DNS server configuration
listen_addresses = ["0.0.0.0:53"] # Listen on all interfaces, port 53
log_level = "info" # Adjust as needed
# Authoritative mode enabled
authoritative_dns = true
# Upstream servers (your primary DNS servers)
upstream_servers = [
"primary-dns-1.example.com:53",
"primary-dns-2.example.com:53"
]
# Load balancing - fastest is recommended for secondary DNS
load_balancing_strategy = "fastest"
probe_interval = 30.0 # Check primary servers every 30 seconds
# Cache configuration
cache_size = 10000
min_cache_ttl = 60 # Minimum 60 seconds cache time
serve_stale_grace_time = 86400 # 1 day
serve_stale_ttl = 30
# Security settings
max_udp_clients = 1000
max_tcp_clients = 100
rate_limit_udp = 100 # Queries per second per client
rate_limit_tcp = 20 # Queries per second per client
sudo etchdns -c /path/to/etchdns.toml
Note: Running on port 53 requires root privileges. For production, consider using privilege dropping:
# Add to your configuration file
user = "nobody"
group = "nogroup"
dig @your-etchdns-server example.com
This tutorial demonstrates how to set up EtchDNS as a local DNS cache for your network.
etchdns.toml with the following content:# Local DNS cache configuration
listen_addresses = ["192.168.1.100:53"] # Replace with your server's IP
log_level = "info" # Adjust as needed
# Caching mode (default)
authoritative_dns = false
# Public DNS resolvers (choose reliable providers)
upstream_servers = [
"8.8.8.8:53", # Google DNS
"1.1.1.1:53", # Cloudflare DNS
"9.9.9.9:53" # Quad9 DNS
]
# Load balancing strategy
load_balancing_strategy = "p2" # Good balance of performance and reliability
probe_interval = 60.0 # Check servers every minute
# Caching configuration for optimal performance
cache_size = 50000 # Adjust based on available RAM
min_cache_ttl = 30 # Minimum 30 seconds cache time
negative_cache_ttl = 60 # 1 minute for negative responses
serve_stale_grace_time = 86400 # Serve stale entries for up to 1 day during outages
serve_stale_ttl = 30 # TTL for stale entries
# Security settings
max_udp_clients = 1000
rate_limit_udp = 100 # Queries per second per client
sudo etchdns -c /path/to/etchdns.toml
dig @192.168.1.100 example.com
Note: For better security in a local network, consider enabling domain filtering:
# Add to your configuration file
nx_zones_file = "nx_zones.txt" # Blocklist for malicious domains
This tutorial demonstrates how to set up EtchDNS with a custom WebAssembly extension.
cargo build --release --features hooks
webassembly-plugins/zig/cd webassembly-plugins/zig
zig build
cp zig-out/lib/hooks.wasm ../../hooks.wasm
# WebAssembly hooks configuration
hooks_wasm_file = "hooks.wasm"
hooks_wasm_wasi = false # Set to true if your plugin needs WASI
./target/release/etchdns -c /path/to/etchdns.toml
Tip: WebAssembly extensions give you incredible flexibility. You can use them to implement custom filtering rules, modify DNS responses, add custom logging, or implement business logic specific to your needs.
These ready-to-use configuration examples address common use cases and can be adapted to your needs.
# High-performance DNS proxy configuration
listen_addresses = ["0.0.0.0:53"]
log_level = "warn" # Minimal logging for performance
# Upstream DNS (choose servers with good latency)
upstream_servers = [
"8.8.8.8:53",
"1.1.1.1:53",
"9.9.9.9:53"
]
# Fastest server selection for optimal performance
load_balancing_strategy = "fastest"
probe_interval = 30.0
# Aggressive caching
cache_size = 100000 # Large cache
min_cache_ttl = 30 # Min 30s cache even for short TTLs
serve_stale_grace_time = 86400 # Keep expired entries for 1 day
serve_stale_ttl = 30 # Use 30s TTL for stale entries
prefetch_popular_queries = true # Keep popular entries fresh
# High connection limits for busy servers
max_udp_clients = 10000
max_tcp_clients = 1000
max_concurrent_queries = 10000
# Monitor with metrics
metrics_listen_address = "127.0.0.1:9100"
# DNS firewall configuration
listen_addresses = ["0.0.0.0:53", "[::]:53"]
log_level = "info"
# Upstream servers
upstream_servers = [
"1.1.1.2:53", # Cloudflare Malware Blocking DNS
"9.9.9.9:53" # Quad9 Secure DNS
]
# Domain filtering
nx_zones_file = "nx_zones.txt" # Blocklist
allowed_zones_file = "allowed_zones.txt" # Optional whitelist
# IP validation
enable_strict_ip_validation = true
block_private_ips = true
block_loopback_ips = true
min_client_port = 1024
blocked_ip_ranges = [
"203.0.113.0/24", # Example: Block TEST-NET-3 range
"198.51.100.0/24" # Example: Block TEST-NET-2 range
]
# Strict rate limiting
rate_limit_udp = 50 # Queries per second per client
rate_limit_tcp = 10 # Queries per second per client
# Query logging for security analysis
query_log_file = "/var/log/etchdns/queries.log"
query_log_include_timestamp = true
query_log_include_client_addr = true
query_log_include_query_type = true
# Log rotation for security logs
query_log_rotation_size = 52428800 # 50MB
query_log_rotation_interval = "daily"
query_log_rotation_count = 30 # Keep 30 days of logs
query_log_compression = true # Save space with compression
# Privilege dropping for security
user = "nobody"
group = "nogroup"
chroot = "/var/empty"
# DoH server with WebAssembly extensions
listen_addresses = ["127.0.0.1:53"] # Standard DNS port
log_level = "info"
# DoH endpoint
doh_listen_addresses = ["0.0.0.0:443"] # Standard HTTPS port
doh_certificate_file = "/path/to/cert.pem"
doh_key_file = "/path/to/key.pem"
doh_path = "/dns-query" # Standard DoH path
# Upstream servers
upstream_servers = [
"8.8.8.8:53",
"1.1.1.1:53"
]
# Cache configuration
cache_size = 50000
min_cache_ttl = 30
serve_stale_grace_time = 86400
# WebAssembly hooks
hooks_wasm_file = "hooks.wasm"
hooks_wasm_wasi = false
# Rate limits for DoH
max_doh_connections = 1000
rate_limit_doh = 20 # Queries per second per client
# Remote control API
control_listen_addresses = ["127.0.0.1:8080"]
control_path = "/control"