Knot Resolver is a high-performance, caching DNS resolver developed by CZ.NIC (the same team behind Knot DNS). It features a modular architecture that allows for extensive customization through Lua scripting (v5) or declarative YAML configuration (v6).
⚠️ Important Version Note: Version 6.1.0+ is the first officially stable release of version 6 and is now preferred over version 5. Version 6 introduces YAML/JSON configuration (replacing Lua), a manager component, and improved production readiness. See Configuration for migration details.
Knot Resolver operates as a caching recursive resolver:
Clients → Knot Resolver → Root/TLD/Authoritative Servers
(Caching, Validation, Filtering)
┌─────────────────────────────────────┐
│ Knot Resolver Core │
├─────────────────────────────────────┤
│ Modules: │
│ • Cache │
│ • DNSSEC Validation │
│ • HTTP/2 (DoH) │
│ • TLS (DoT) │
│ • Lua Scripting │
│ • Monitoring │
│ • RPZ │
└─────────────────────────────────────┘
Version Note: Knot Resolver 6.x uses YAML/JSON configuration format, while version 5.x uses Lua scripting. The examples below show both formats where applicable. For detailed configuration guidance, see Configuration Guide.
Knot Resolver 6.x uses /etc/knot-resolver/config.yaml:
# Listen on standard DNS port
server:
interfaces:
- "0.0.0.0:53"
- ":::53"
# Enable DNSSEC validation
dnssec:
enable: true
# Forward queries to upstream resolvers
forward:
- address: "1.0.0.1"
- address: "8.8.8.8"
# Cache configuration
cache:
max_size: 104857600 # 100 MB
Knot Resolver 5.x uses /etc/knot-resolver/kresd.conf:
-- Listen on standard DNS port
interfaces { '0.0.0.0@53', '::@53' }
-- Enable DNSSEC validation
modules = { 'dnssec' }
-- Forward queries to upstream resolvers
forward('1.0.0.1')
forward('8.8.8.8')
-- Enable caching
cache.size = 100 * 1024 * 1024 -- 100 MB
v6 (YAML):
tls:
enable: true
listen:
- address: "0.0.0.0:853"
cert: "/etc/knot-resolver/tls.crt"
key: "/etc/knot-resolver/tls.key"
forward:
- address: "1.0.0.1"
tls_auth_name: "cloudflare-dns.com"
v5 (Lua):
-- Load TLS module
modules = { 'tls' }
-- Configure TLS listener
tls('0.0.0.0:853', '/etc/knot-resolver/tls.crt', '/etc/knot-resolver/tls.key')
-- Upstream TLS
forward('1.0.0.1', { tls_auth_name = 'cloudflare-dns.com' })
v6 (YAML):
http:
enable: true
listen:
- address: "0.0.0.0:443"
cert: "/etc/knot-resolver/tls.crt"
key: "/etc/knot-resolver/tls.key"
path: "/dns-query"
v5 (Lua):
-- Load HTTP/2 module
modules = { 'http2' }
-- Configure DoH listener
http2('0.0.0.0:443', '/etc/knot-resolver/tls.crt', '/etc/knot-resolver/tls.key')
-- DoH path
http2.path = '/dns-query'
-- Custom query handling
add_action(function(state)
-- Block queries for known malware domains
if state.qname:endswith('malware-domain.com') then
state:drop()
end
end)
-- Custom response modification
add_answer_filter(function(state)
-- Add custom header or modify response
-- Custom logic here
end)
-- Load RPZ module
modules = { 'rpz' }
-- Configure RPZ zone
rpz {
file = '/etc/knot-resolver/rpz.txt',
action = policy.DROP
}
-- Cache size
cache.size = 500 * 1024 * 1024 -- 500 MB
-- Cache TTL settings
cache.min_ttl = 60
cache.max_ttl = 86400
-- Negative cache
cache.negative_ttl = 3600
-- Enable monitoring module
modules = { 'monitor' }
-- Prometheus metrics
prometheus('0.0.0.0:9153')
# Add CZ.NIC repository
curl -fsSL https://repo.nic.cz/keys/nic.gpg | sudo tee /etc/apt/trusted.gpg.d/nic.gpg
echo "deb https://repo.nic.cz/debian $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/nic.list
# Install Knot Resolver
sudo apt update
sudo apt install knot-resolver
# Add CZ.NIC repository
sudo dnf config-manager --add-repo https://repo.nic.cz/rhel/knot-resolver.repo
# Install Knot Resolver
sudo dnf install knot-resolver
⚠️ Production Note: Knot Resolver Docker images are production-capable since version 6.x with the introduction of kres-manager. However, the developers note limited production experience with Docker deployments. Performance penalties may occur in Kubernetes/SDN environments. See Docker Deployment Guide for detailed guidance.
Version 6.x (Recommended):
version: '3.8'
services:
knot-resolver:
image: docker.io/cznic/knot-resolver:6.1.0
container_name: knot-resolver
restart: unless-stopped
ports:
- "53:53/udp"
- "53:53/tcp"
- "853:853/tcp" # DoT
- "443:443/tcp" # DoH
volumes:
- ./config.yaml:/etc/knot-resolver/config.yaml:ro
- ./cache:/var/cache/knot-resolver
- ./certs:/etc/knot-resolver/certs:ro
cap_add:
- NET_BIND_SERVICE
networks:
- dns-net
networks:
dns-net:
driver: bridge
Version 5.x (Legacy):
version: '3.8'
services:
knot-resolver:
image: docker.io/cznic/knot-resolver:5.7.6
container_name: knot-resolver
restart: unless-stopped
ports:
- "53:53/udp"
- "53:53/tcp"
volumes:
- ./kresd.conf:/etc/knot-resolver/kresd.conf:ro
- ./cache:/var/cache/knot-resolver
cap_add:
- NET_BIND_SERVICE
Note: For production use, mount persistent volumes for
/etc/knot-resolver(configuration) and/var/cache/knot-resolver(cache). See Docker Deployment Guide for complete examples.
Build System: Version 6.x uses Meson build system. Version 5.x uses traditional Make.
Version 6.x (Meson):
# Install dependencies
sudo apt install build-essential meson ninja-build \
libgnutls28-dev libluajit-5.1-dev libuv1-dev \
libnghttp2-dev pkg-config python3-pip
# Clone repository
git clone https://github.com/CZ-NIC/knot-resolver.git
cd knot-resolver
# Build
meson setup build
cd build
ninja
# Install
sudo ninja install
Version 5.x (Make):
# Install dependencies
sudo apt install build-essential cmake libgnutls28-dev liblua5.3-dev \
libnghttp2-dev libuv1-dev pkg-config
# Clone repository
git clone https://github.com/CZ-NIC/knot-resolver.git
cd knot-resolver
# Build
mkdir build && cd build
cmake ..
make
# Install
sudo make install
Note: For detailed build instructions and dependencies, see the official documentation.
# Start/stop/restart (systemd)
sudo systemctl start knot-resolver
sudo systemctl stop knot-resolver
sudo systemctl restart knot-resolver
# Check status
sudo systemctl status knot-resolver
# View logs
sudo journalctl -u knot-resolver -f
# Management CLI (kresctl)
kresctl status # Show resolver status
kresctl reload # Reload configuration
kresctl validate # Validate configuration file
kresctl schema # Output JSON schema
kresctl migrate # Migrate configuration to newer version
# Cache management
kresctl cache clear # Clear DNS cache
kresctl cache stats # Show cache statistics
# Prometheus metrics
curl http://localhost:8453/metrics
# Start/stop/restart
sudo systemctl start knot-resolver
sudo systemctl stop knot-resolver
sudo systemctl restart knot-resolver
# Check status
sudo systemctl status knot-resolver
# View logs
sudo journalctl -u knot-resolver -f
# Connect to console (kresctl)
sudo kresctl
# Console commands
stats() # Show statistics
cache.stats() # Show cache status
cache.clear() # Clear cache
modules.list() # Show loaded modules
config.reload() # Reload configuration
resolve('example.com') # Test resolution
Access metrics at http://localhost:8453/metrics (default port)
Configuration (YAML):
monitoring:
metrics: true
port: 8453
Key Metrics:
resolver_request_total - Total DNS requestsresolver_answer_total - Total answered queriesresolver_answer_cached - Queries answered from cacheresolver_answer_noerror - NOERROR answersresolver_answer_nxdomain - NXDOMAIN answersresolver_latency_sum_ms - Sum of all latencies in msresolver_dnssec_validated - DNSSEC validated responsesAccess metrics at http://localhost:9153/metrics
Configuration (Lua):
modules = { 'stats', 'http' }
http.prometheus.namespace = 'resolver_'
Key Metrics:
knot_resolver_queries_total - Total queriesknot_resolver_cache_hits - Cache hitsknot_resolver_dnssec_validated - DNSSEC validated responsesknot_resolver_response_time - Query response timev6 (YAML):
modules:
- stats
v5 (Lua):
modules = { 'stats' }
-- View in console
stats()
stats.list() -- List all metrics
stats.frequent() -- Most frequent queries
stats.upstreams() -- Recently contacted authoritative servers
modules = {
graphite = {
prefix = hostname() .. worker.id,
host = '127.0.0.1',
port = 2003,
interval = 5 * sec,
tcp = false
}
}
Tip: InfluxDB + Grafana provides richer visualization options. See Monitoring Guide for complete details.
v6 (YAML):
server:
interfaces:
- "0.0.0.0:53"
- ":::53"
cache:
max_size: 2147483648 # 2 GB
dnssec:
enable: true
rate_limiting:
enable: true
limit: 100 # queries per second per IP
forward:
- address: "1.0.0.1"
- address: "8.8.8.8"
- address: "9.9.9.9"
v5 (Lua):
-- High-performance caching
cache.size = 2 * 1024 * 1024 * 1024 -- 2 GB
-- DNSSEC validation
modules = { 'dnssec' }
-- Rate limiting
rate_limit = 100 -- queries per second per IP
-- Upstream forwarders
forward('1.0.0.1')
forward('8.8.8.8')
forward('9.9.9.9')
v6 (YAML):
cache:
max_size: 524288000 # 500 MB
dnssec:
enable: true
forward:
- address: "192.168.1.10" # Internal DNS
- address: "192.168.1.11" # Backup
policy:
rpz:
- file: "/etc/knot-resolver/malware.rpz"
action: "drop"
v5 (Lua):
-- Moderate cache size
cache.size = 500 * 1024 * 1024 -- 500 MB
-- Internal forwarders
forward('192.168.1.10') -- Internal DNS
forward('192.168.1.11') -- Backup
-- DNSSEC validation
modules = { 'dnssec' }
-- Block malware domains
modules = { 'rpz' }
rpz { file = '/etc/knot-resolver/malware.rpz' }
v6 (YAML):
server:
interfaces:
- "127.0.0.1:53" # Local only
dnssec:
enable: true
privacy:
qname_minimization: true
logging:
level: "error" # Minimal logging
tls:
enable: true
listen:
- address: "0.0.0.0:853"
cert: "/etc/knot-resolver/tls.crt"
key: "/etc/knot-resolver/tls.key"
http:
enable: true
listen:
- address: "0.0.0.0:443"
cert: "/etc/knot-resolver/tls.crt"
key: "/etc/knot-resolver/tls.key"
v5 (Lua):
-- QNAME minimization
modules = { 'privacy' }
-- DNSSEC validation
modules = { 'dnssec' }
-- Minimal logging
log_level = 'error'
-- DoT/DoH only
interfaces { '127.0.0.1@53' } -- Local only
tls('0.0.0.0:853', 'tls.crt', 'tls.key')
http2('0.0.0.0:443', 'tls.crt', 'tls.key')
v6 (YAML):
logging:
level: "debug" # Detailed logging for analysis
modules:
- stats
- policy # Custom policy scripts
v5 (Lua):
-- Load experimental modules
modules = { 'experimental' }
-- Custom Lua scripting
dofile('/etc/knot-resolver/custom.lua')
-- Detailed logging for analysis
log_level = 'debug'
v6 (YAML):
forward:
- domain: "internal.example.com"
address: "192.168.1.10"
- address: "1.0.0.1" # External queries
- address: "8.8.8.8"
v5 (Lua):
-- Forward internal zones to Knot DNS
forward('internal.example.com', '192.168.1.10')
-- External queries to upstream
forward('1.0.0.1')
forward('8.8.8.8')
v6 (YAML):
forward:
- domain: "powerdns.example.com"
address: "192.168.1.20"
- address: "1.0.0.1"
v5 (Lua):
-- Forward PowerDNS zones
forward('powerdns.example.com', '192.168.1.20')
-- Other queries to public resolvers
forward('1.0.0.1')
v6 (YAML):
forward:
- address: "192.168.1.10" # Primary (BIND)
- address: "8.8.8.8" # Backup
v5 (Lua):
-- Forward to BIND recursive resolver
forward('192.168.1.10')
-- Use as backup
forward('8.8.8.8')
v6 (YAML):
policy:
access_control:
- network: "10.0.0.0/8"
action: "allow"
- network: "192.168.0.0/16"
action: "allow"
rate_limiting:
enable: true
limit: 50 # per IP
dnssec:
enable: true # Always enable
v5 (Lua):
-- Restrict access
access_control('10.0.0.0/8')
access_control('192.168.0.0/16')
-- Rate limiting
rate_limit = 50 -- per IP
-- DNSSEC validation (always enable)
modules = { 'dnssec' }
v6 (YAML):
tls:
ciphers: "ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384"
min_protocol: "TLSv1.2"
v5 (Lua):
-- Strong TLS settings
tls_config {
ciphers = 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384',
min_protocol = 'TLSv1.2'
}
v6 (YAML):
server:
workers: 4 # Worker threads
cache:
max_size: 1073741824 # 1 GB
max_ttl: 604800 # 1 week
network:
tcp_max_clients: 1000
udp_max_clients: 10000
v5 (Lua):
-- Increase worker threads
workers = 4
-- Tune cache
cache.size = 1024 * 1024 * 1024 -- 1 GB
cache.max_ttl = 604800 -- 1 week
-- Network tuning
tcp.max_clients = 1000
udp.max_clients = 10000
1. DNSSEC validation failures:
v6:
kresctl dnssec check # Check trust anchors
kresctl stats # View validation status
v5:
-- Check trust anchors
dnssec.check()
-- View validation status
stats()
2. High memory usage:
v6:
cache:
max_size: 268435456 # 256 MB
kresctl cache clear # Clear cache
v5:
-- Reduce cache size
cache.size = 256 * 1024 * 1024
-- Clear cache
cache.clear()
3. Slow queries:
v6:
kresctl stats # Check statistics
kresctl stats upstreams # Check upstream latency
v5:
-- Check upstream latency
stats()
stats.upstreams()
-- Add more forwarders
forward('1.0.0.1')
forward('8.8.8.8')
4. Configuration validation errors (v6):
kresctl validate # Validate configuration
# Review error messages for specific issues
v6 (kresctl):
kresctl status # Show resolver status
kresctl stats # View detailed statistics
kresctl cache stats # Check cache
kresctl cache clear # Clear cache
kresctl reload # Reload configuration
kresctl validate # Validate configuration
v5 (console):
# Connect to console
sudo kresctl
# Commands
stats() # View detailed statistics
cache.stats() # Check cache
modules.list() # View loaded modules
resolve('example.com') # Test resolution
# View logs (both versions)
sudo journalctl -u knot-resolver -f
# Debug level logging
sudo journalctl -u knot-resolver -f --priority=debug
| Feature | Knot Resolver v6 | Knot Resolver v5 | Unbound | BIND | PowerDNS Recursor |
|---|---|---|---|---|---|
| Performance | Very High | Very High | Very High | High | Very High |
| DNSSEC | ✅ Excellent | ✅ Excellent | ✅ Excellent | ✅ Excellent | ✅ Excellent |
| Configuration | ✅ YAML/JSON | ✅ Lua | ⚠️ Text conf | ⚠️ Text conf | ✅ Lua |
| DoT/DoH | ✅ Native | ✅ Native | ✅ Native | ⚠️ Limited | ⚠️ Via dnsdist |
| Modularity | ✅ High | ✅ High | ⚠️ Medium | ⚠️ Medium | ⚠️ Medium |
| Memory Usage | Low | Low | Low | Medium | Low |
| Manager API | ✅ HTTP API | ❌ None | ❌ None | ⚠️ Limited | ✅ API |
| Production Ready (Docker) | ✅ Yes | ⚠️ Limited | ✅ Yes | ✅ Yes | ✅ Yes |
Questions? Find all contact information on our contact page.