Technitium DNS Server is a self-hosted, authoritative and recursive DNS server with a full web-based admin UI. It replaces the Pi-hole + Unbound combination with a single service that handles:
- Recursive resolution — queries upstream resolvers (DoH/DoT/UDP) directly, no separate Unbound needed
- Ad/tracker blocking — built-in blocklist support similar to Pi-hole
- Local DNS zones — create A/CNAME records for internal hostnames
- DNS-over-HTTPS (DoH) / DNS-over-TLS (DoT) — encrypted DNS endpoints for clients
- Query logging & statistics — per-client query history and dashboards
Pi-hole handles blocking and Pi-hole forwards to Unbound for recursion — two processes that need to stay in sync. Technitium does both in a single container with one config UI, making it simpler to manage, back up, and update.
LAN clients (port 53)
|
[Technitium :53] <-- Docker, host-port bound
|
local zones? yes → serve from local zone DB
|
blocklist hit? yes → NXDOMAIN / 0.0.0.0
|
upstream resolvers (Cloudflare DoH, Quad9, etc.)
|
Web admin UI (:5380) → Traefik → ${TECHNITIUM_DOMAIN}
The container is on the traefik network so the web UI is proxied by Traefik. DNS itself is published directly to host port 53 (UDP + TCP) — no Traefik involvement for DNS traffic.
- Nothing else on port 53 — TrueNAS SCALE may have
systemd-resolvedusing port 53. Check withss -tulnp | grep ':53'. If it is active, disable it:systemctl stop systemd-resolved systemctl disable systemd-resolved
- Create bind-mount directories before starting the container:
mkdir -p /mnt/SSD/Containers/technitium/config mkdir -p /mnt/SSD/Containers/technitium/logs
cp example.env .env
# edit .env — set TECHNITIUM_ADMIN_PASSWORD and TECHNITIUM_DOMAIN
docker compose up -dOpen https://${TECHNITIUM_DOMAIN} (once DNS and Traefik are wired) or http://<host-ip>:5380 on first boot (web UI is not behind Traefik until Traefik is resolving your domain).
Default admin username: admin
After docker compose up -d, open the web UI at http://<host-ip>:5380 and log in with username admin and the password you set in .env.
| Setting | Recommended value | Why |
|---|---|---|
| DNS Server Domain | dns.virtuallyboring.com |
Pre-filled from DNS_SERVER_DOMAIN env var |
| Prefer IPv6 | Off | Avoids latency on single-stack LAN |
| QNAME Minimisation | On | Privacy — sends less query data to upstream |
| NSID | Off | Not needed for a private resolver |
| Log queries | On | Essential for debugging and stats |
| Log using local time | On | Matches your server's TZ |
Set Recursion to Allow Only For Private Networks. This prevents your resolver from being used as an open recursive resolver if port 53 is ever accidentally reachable from the internet.
If you need recursion for all clients unconditionally (e.g. split-horizon VPN), change this to
Allowand tighten at the firewall level instead.
Technitium can recurse fully on its own (like Unbound) or forward to upstream resolvers. For a homelab, forwarding to encrypted upstreams is simpler and faster:
- Go to Settings → Forwarders
- Enable Quick Forwarding (uses the fastest responding upstream)
- Add upstreams — recommended set:
| Address | Protocol | Provider |
|---|---|---|
https://cloudflare-dns.com/dns-query |
HTTPS (DoH) | Cloudflare |
https://dns.quad9.net/dns-query |
HTTPS (DoH) | Quad9 (malware-blocking) |
- Set Forwarder Protocol to
Https - Enable Fail on SERVFAIL — drops an upstream if it returns errors
To do full recursion instead (no forwarders, like Unbound), leave the forwarders list empty and set Recursion to
Allow Only For Private Networks.
- Enable Block List
- Add block list URLs. Recommended starting set:
| URL | List |
|---|---|
https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts |
StevenBlack (ads + malware) |
https://raw.githubusercontent.com/hagezi/dns-blocklists/main/domains/pro.txt |
Hagezi Pro |
https://raw.githubusercontent.com/hagezi/dns-blocklists/main/domains/tif.txt |
Hagezi Threat Intelligence |
- Set Blocking Type to
NxDomain(most compatible) orUnspecified(returns 0.0.0.0/:: which breaks some apps less) - Click Save then Update Now to pull all lists immediately
To serve internal A/CNAME records (replacing Pi-hole's Local DNS):
- Zones → Add Zone
- Type:
Primary Zone, name: your internal domain (e.g.virtuallyboring.comorhome.arpa) - Add records:
- Type
A→ hostname → LAN IP - Type
CNAME→ alias → target A record
- Type
- No TTL changes needed for a homelab (default 3600 is fine)
A conditional forwarder sends queries for a specific domain to a designated DNS server instead of going through your normal forwarders or recursion. Use cases:
- Route
corp.example.comqueries to a corporate DNS server over VPN - Route
home.arpaor a split-horizon domain to a secondary internal resolver - Forward
lanor.localto a router/NAS that serves DHCP hostnames
Steps:
- Zones → Add Zone
- Type:
Conditional Forwarder Zone - Zone Name: the domain to intercept (e.g.
corp.example.com,home.arpa, orlan) - Forwarder Protocol: choose
Udpfor a LAN DNS server,Httpsfor a corporate DoH endpoint - Forwarder: enter the IP or DoH URL of the target DNS server (e.g.
10.0.1.1for your UDM SE, or192.168.1.1for a corporate gateway) - Click Add
The zone name must be an exact suffix match — a zone for
corp.example.comcatcheshost.corp.example.combut notexample.com. For a catch-all (forward everything, no local recursion), create a conditional forwarder zone for.(the root).
Example — route .home.arpa to the UDM SE:
| Field | Value |
|---|---|
| Zone Name | home.arpa |
| Forwarder | 10.0.1.1 |
| Protocol | Udp |
This means reverse-DNS queries for RFC 1918 addresses handed out by UniFi DHCP resolve to actual hostnames.
- Export Pi-hole custom DNS entries (Settings → Local DNS → DNS Records) and recreate them as A/CNAME records under a local zone in Technitium.
- Export Pi-hole blocklists (Group Management → Adlists) and add them under Settings → Blocking in Technitium.
- Point your router's DHCP DNS to the TrueNAS host IP once Technitium is confirmed working.
- Shut down the Proxmox Pi-hole and Unbound LXCs after a validation period.
| Variable | Default | Required | Description |
|---|---|---|---|
TZ |
America/New_York |
no | Container timezone |
TECHNITIUM_DOMAIN |
— | yes | Web UI domain; also sets the server's SOA hostname |
TECHNITIUM_ADMIN_PASSWORD |
— | yes | Admin panel password (set before first boot) |
Additional Technitium-specific env vars (all optional — configure via web UI instead):
| Variable | Description |
|---|---|
DNS_SERVER_RECURSION |
Allow / AllowOnlyForPrivateNetworks / Deny |
DNS_SERVER_FORWARDERS |
Comma-separated upstream IPs/DoH URLs |
DNS_SERVER_FORWARDER_PROTOCOL |
Udp, Tcp, Tls, Https, HttpsJson |
Full variable reference: https://github.com/TechnitiumSoftware/DnsServer#environment-variables
| Port | Protocol | Purpose |
|---|---|---|
| 53 | UDP + TCP | DNS queries from LAN clients (host-bound) |
| 5380 | TCP | Web admin UI (internal, proxied by Traefik) |