Self-Hosting Knot Resolver with Docker Compose

What Is Knot Resolver?

Knot Resolver (kresd) is a recursive DNS resolver developed by CZ.NIC, the Czech domain registry. It’s the resolver behind Cloudflare’s 1.1.1.1 DNS service (Cloudflare uses a modified version) and the Turris routers. Unlike Unbound, which focuses on being a standalone recursive resolver, Knot Resolver emphasizes performance, modern protocol support, and extensibility through Lua modules.

Version 6 introduced YAML-based configuration, replacing the older Lua config format. It resolves queries recursively (walking the DNS hierarchy from root servers), validates DNSSEC by default, and can serve DNS-over-TLS and DNS-over-HTTPS natively.

Prerequisites

  • A Linux server (Ubuntu 22.04+ recommended)
  • Docker and Docker Compose installed (guide)
  • 256 MB of free RAM
  • Port 53 available (disable systemd-resolved if needed)
  • Understanding of DNS concepts (DNS Explained)

Docker Compose Configuration

Create a directory for Knot Resolver:

mkdir -p /opt/knot-resolver && cd /opt/knot-resolver

Create a docker-compose.yml:

services:
  knot-resolver:
    image: cznic/knot-resolver:v6.2.0
    container_name: knot-resolver
    ports:
      - "53:53/tcp"
      - "53:53/udp"
      - "853:853/tcp"    # DNS-over-TLS (optional)
      - "443:443/tcp"    # DNS-over-HTTPS (optional)
    volumes:
      - ./config.yaml:/etc/knot-resolver/config.yaml:ro
      - knot-cache:/var/cache/knot-resolver
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "kdig", "@127.0.0.1", "example.com"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 10s

volumes:
  knot-cache:

Configuration File

Create /opt/knot-resolver/config.yaml:

# Knot Resolver v6 configuration
workers: 2

logging:
  level: info

network:
  listen:
    - interface: 0.0.0.0@53       # Plain DNS (UDP + TCP)
    - interface: 0.0.0.0@853      # DNS-over-TLS
      kind: dot

# DNSSEC validation is enabled by default
# No additional configuration needed

# Cache configuration
cache:
  size-max: 256m                   # Maximum cache size in memory

# Optional: forwarding instead of recursive resolution
# forward:
#   - subtree: "."
#     servers:
#       - address: 1.1.1.1
#       - address: 9.9.9.9

Start the stack:

docker compose up -d

Initial Setup

Test that Knot Resolver is working:

# Basic resolution
dig @127.0.0.1 example.com

# DNSSEC validation test
dig @127.0.0.1 sigfail.verteiltesysteme.net  # Should return SERVFAIL
dig @127.0.0.1 sigok.verteiltesysteme.net    # Should return A record

Point Your Network at Knot Resolver

Update your router’s DHCP settings to distribute your server’s IP as the primary DNS server, or configure individual devices.

Configuration

YAML Configuration Reference

OptionTypeDefaultPurpose
workersintegerautoNumber of resolver worker threads
logging.levelstringinfoLog verbosity: debug, info, warning, error, critical
network.listenlistlo@53Bind addresses and protocols
cache.size-maxstringautoMaximum memory for DNS cache
forwardlistUpstream forwarders (disables recursive mode)

Recursive Mode (Default)

By default, Knot Resolver operates as a full recursive resolver — it walks the DNS hierarchy from root servers down to authoritative nameservers. DNSSEC validation is enabled automatically. No upstream forwarders needed.

Forwarding Mode

To forward queries to an upstream DNS provider instead of resolving recursively:

forward:
  - subtree: "."
    servers:
      - address: 1.1.1.1
      - address: 9.9.9.9

Split DNS (Domain-Specific Forwarding)

Route specific domains to different resolvers:

forward:
  # Internal domains → corporate DNS
  - subtree: "corp.example.com"
    servers:
      - address: 10.0.0.53

  # Home LAN → router
  - subtree: "home.lan"
    servers:
      - address: 192.168.1.1

  # Everything else → recursive resolution (no forward block for ".")

See Split DNS Setup for detailed patterns.

Performance Tuning

SettingRecommendationWhy
workersCPU count - 1Each worker handles queries independently
cache.size-max50-75% of available RAM for DNSLarger cache = fewer recursive lookups

For a server with 4 GB RAM dedicated to DNS:

workers: 3
cache:
  size-max: 2g

Advanced Configuration

DNS-over-TLS Server

Serve encrypted DNS to clients that support DoT (Android, iOS, many DNS clients):

network:
  listen:
    - interface: 0.0.0.0@53
    - interface: 0.0.0.0@853
      kind: dot

# TLS configuration
network:
  tls:
    cert-file: /certs/fullchain.pem
    key-file: /certs/privkey.pem

Mount your certificates:

volumes:
  - ./config.yaml:/etc/knot-resolver/config.yaml:ro
  - /etc/letsencrypt/live/dns.yourdomain.com:/certs:ro
  - knot-cache:/var/cache/knot-resolver

DNS-over-HTTPS Server

Serve DoH on port 443:

network:
  listen:
    - interface: 0.0.0.0@53
    - interface: 0.0.0.0@443
      kind: doh2

Requires the same TLS certificate configuration as DoT.

Management API

Enable the kresctl management API for monitoring and runtime control:

management:
  interface: 127.0.0.1@5000

Query resolver status:

docker exec knot-resolver kresctl metrics
docker exec knot-resolver kresctl cache/stats

Aggressive NSEC (RFC 8198)

Knot Resolver implements aggressive NSEC caching by default — it uses cached NSEC records to synthesize negative responses without querying authoritative servers. This reduces query load and speeds up “domain does not exist” answers.

Reverse Proxy

Knot Resolver serves DNS on port 53 (and optionally DoT on 853, DoH on 443). A reverse proxy isn’t applicable for DNS traffic. For remote access, use Tailscale or WireGuard. See Remote Access for options.

If serving DoH, the HTTPS endpoint can optionally be placed behind a reverse proxy — but Knot Resolver handles TLS termination natively.

Backup

Knot Resolver’s persistent state is minimal:

DataLocationBackup
Configurationconfig.yaml (host file)File copy
DNS cacheknot-cache volumeNot needed — rebuilds automatically
TLS certificatesHost filesBack up with your cert management
cp /opt/knot-resolver/config.yaml /opt/backups/knot-resolver-config-$(date +%F).yaml

See Backup Strategy for general guidance.

Troubleshooting

Container Exits With Config Error

Symptom: Container restarts repeatedly, logs show YAML parse error

Fix: Validate your YAML syntax. Common issues:

  • Indentation must use spaces, not tabs
  • Listen interfaces need the @port suffix
  • kind values are case-sensitive: dot, doh2, not DoT, DoH
docker compose logs knot-resolver

Port 53 Already in Use

Symptom: bind: address already in use

Fix: Disable systemd-resolved:

sudo systemctl disable --now systemd-resolved
sudo rm /etc/resolv.conf
echo "nameserver 1.1.1.1" | sudo tee /etc/resolv.conf

DNSSEC Validation Failures for Valid Domains

Symptom: Some legitimate domains return SERVFAIL

Fix: Check the system clock — DNSSEC validation depends on accurate time. Ensure NTP is running:

timedatectl status

If the clock is correct, the domain may have genuinely broken DNSSEC records. Test with:

dig @127.0.0.1 example.com +dnssec +cd  # cd flag disables DNSSEC validation

Slow Resolution at Startup

Symptom: First queries take 1-2 seconds after restart

Fix: Normal — the cache is empty after restart. Knot Resolver needs to build its cache by performing full recursive lookups. Performance improves within minutes as the cache fills.

Resource Requirements

ResourceValue
RAM~50 MB base + configured cache size
CPULow to moderate — scales with worker count
Disk~90 MB container + cache (if disk-backed)
NetworkOutbound to root/authoritative DNS servers

Verdict

Knot Resolver is the performance-focused alternative to Unbound for recursive DNS. Its YAML configuration (v6+) is clean and readable, DNSSEC validation works out of the box, and native DoT/DoH serving means you can offer encrypted DNS without additional software. The fact that Cloudflare chose it as the engine behind 1.1.1.1 speaks to its performance at scale.

For most homelabs, Unbound is the simpler choice with more community documentation and Docker image options. Choose Knot Resolver if you want native DoT/DoH serving, need aggressive NSEC caching, or prefer YAML configuration over Unbound’s custom config format.

FAQ

Is Knot Resolver the same as Knot DNS?

No. Knot DNS is an authoritative DNS server (like PowerDNS). Knot Resolver is a recursive resolver (like Unbound). Both are developed by CZ.NIC, but they’re separate projects for different purposes.

Can Knot Resolver block ads?

Not natively. Knot Resolver is a resolver, not a DNS sinkhole. For ad blocking, use Pi-hole or AdGuard Home with Knot Resolver as the upstream recursive resolver.

Why choose Knot Resolver over Unbound?

Knot Resolver has native DoT/DoH/DoQ serving, aggressive NSEC caching (RFC 8198), and YAML configuration. Unbound has more community Docker images, simpler defaults, and wider homelab adoption. Both perform well for home use.

Comments