Tag: dns

  • Replacing Pi-hole with Technitium DNS, on two servers

    For a long time I ran Pi-hole as my network’s ad and malware sink. It’s a great project, but I’ve since replaced it with Technitium DNS Server, running on two machines so that one going down never takes the whole house offline.

    What a DNS sinkhole does

    Every time a device opens a website or an app phones home, it first asks a DNS server to turn a name like ads.example.com into an IP address. A DNS sinkhole checks each name against blocklists of known ad, tracking and malware domains. If the name is on a list, it simply doesn’t give a real answer, so the ad, tracker or malicious download never loads. This works for every device on the network, including smart TVs and phones where you can’t install an ad blocker.

    Why I moved from Pi-hole to Technitium

    Pi-hole does blocking brilliantly. Technitium does blocking too, but it’s also a complete DNS server, which suits a growing home lab:

    • Encrypted upstream DNS built in: queries leave my network over DNS-over-HTTPS, with no extra resolver containers to bolt on.
    • Wildcard blocklists: blocking a domain blocks all its subdomains, so modern lists stay smaller and more effective.
    • Proper local zones: I can give my own machines and services real names on the home network.
    • Apps: optional add-ons like a searchable query log stored in SQLite.
    • A clean web interface with a dashboard of queries, blocked requests and top clients.

    Why two servers?

    When all your DNS goes through one box, that box becomes a single point of failure. Reboot it for updates, or have it crash, and every device in the house “loses the internet”, even though the connection is fine. So I run Technitium on two separate machines, and the router hands out both as DNS 1 and DNS 2.

    Diagram: every device is given two DNS servers, Technitium 1 and Technitium 2; both check blocklists for ads, trackers and malware, giving no answer for blocked domains and forwarding allowed queries upstream to Cloudflare and Google over DNS-over-HTTPS.
    Two Technitium servers share the load and cover for each other.

    Setting it up

    1. Free up port 53 (Ubuntu)

    Ubuntu’s built-in systemd-resolved already listens on port 53, which a DNS server needs. Turn off its stub listener:

    sudo sed -i 's/^#\?DNSStubListener=.*/DNSStubListener=no/' /etc/systemd/resolved.conf
    sudo systemctl restart systemd-resolved

    Once Technitium is running, point the server at itself (127.0.0.1) and at your second DNS server, so it keeps working even if the local one is down.

    2. The compose file

    Save as /opt/stacks/technitium/compose.yaml, and put the admin password in a .env file rather than in the compose file:

    services:
      technitium:
        container_name: technitium
        image: technitium/dns-server:latest
        network_mode: host
        environment:
          TZ: 'Europe/Dublin'
          DNS_SERVER_DOMAIN: 'dns-server'
          DNS_SERVER_ADMIN_PASSWORD: ${DNS_ADMIN_PASSWORD}
          DNS_SERVER_LOG_USING_LOCAL_TIME: 'true'
          DNS_SERVER_WEB_SERVICE_HTTP_PORT: '5380'
          DNS_SERVER_WEB_SERVICE_LOCAL_ADDRESSES: '0.0.0.0'
          DNS_SERVER_ENABLE_BLOCKING: 'true'
        volumes:
          - ./etc-technitium:/etc/dns
          - ./logs:/var/log/technitium
        cap_add:
          - NET_BIND_SERVICE
        restart: unless-stopped
    echo "DNS_ADMIN_PASSWORD=pick-a-strong-password" > .env
    chmod 600 .env
    docker compose up -d

    network_mode: host lets Technitium answer DNS on port 53 directly, and see real client IPs in its logs. The password variable only sets the admin password on first start. After that, change it in the web UI.

    3. Encrypted upstream DNS

    Open http://<server-ip>:5380 and log in. Under Settings → Proxy & Forwarders, use the quick-select to add Cloudflare and Google with the DNS-over-HTTPS protocol. I use both:

    https://1.1.1.1/dns-query
    https://8.8.8.8/dns-query

    4. Add blocklists

    Under Settings → Blocking, make sure blocking is enabled and add your blocklist URLs. These are the ones I use:

    • HaGeZi Multi and HaGeZi Pro++: well-maintained lists covering ads, tracking, malware, phishing and scams, in wildcard format (github.com/hagezi/dns-blocklists).
    • StevenBlack unified hosts with the fake-news and gambling extensions (github.com/StevenBlack/hosts). There’s also a variant that adds adult content, which is handy on a family network.

    Technitium downloads and refreshes the lists automatically. Start with one or two lists and add more gradually. Aggressive lists like Pro++ occasionally block something you need, which the Allowed tab fixes in seconds.

    5. Build the second server

    On the second machine, repeat steps 1 and 2. Then use Settings → Backup Settings on the first server and Restore Settings on the second to copy the forwarders, blocklists and allow-list across.

    Keep the two in sync. Devices don’t strictly use “DNS 1 first, DNS 2 as backup”. They often spread queries across both. If the servers have different blocklists, ads will mysteriously appear on some page loads and not others. Whenever you change a list on one, make the same change on the other.

    6. Point your network at them

    In your router’s DHCP settings, set the primary DNS to server 1 and the secondary to server 2. Devices pick up the change the next time they renew their lease, or straight away if you reconnect them.

    7. Test it

    # a normal domain should resolve on both servers
    dig +short @<server-1-ip> example.com
    dig +short @<server-2-ip> example.com
    
    # an ad domain should come back empty (blocked)
    dig +short @<server-1-ip> doubleclick.net

    Then the real test: stop Technitium on one server and keep browsing. If everything still loads, your redundancy works.

    Was it worth switching?

    Yes. Ads and known-bad domains are blocked for every device in the house, DNS queries leave the network encrypted, and rebooting a server for updates no longer means everyone shouting that the Wi-Fi is broken.

    New to Docker? Start with Getting started with Docker and Portainer.