Technitium DNS Setup Guide
π Technitium DNS Server Setup Guide
This guide documents how to set up Technitium DNS Server on Ubuntu with nginx reverse proxy, Letβs Encrypt SSL certificates, and DNS-over-HTTPS (DoH) / DNS-over-TLS (DoT) support.
Prerequisites
- Ubuntu server (tested on Ubuntu 24)
- Domain registered with Cloudflare (for DNS management and cert automation)
- Root/sudo access
Architecture Overview
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β INTERNET β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β YOUR VPS (Ubuntu) β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β Port 443 (HTTPS) Port 853 (DoT) Port 53 (DNS) β
β β β β β
β βΌ β β β
β ββββββββββββ β β β
β β NGINX β β β β
β β (TLS β β β β
β β Termination) β β β
β ββββββ¬ββββββ β β β
β β β β β
β βΌ βΌ βΌ β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β TECHNITIUM DNS SERVER β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€ β
β β Port 5380 - Web UI (proxied via nginx) β β
β β Port 8053 - DNS-over-HTTP (proxied via nginx) β β
β β Port 853 - DNS-over-TLS (native) β β
β β Port 53 - Standard DNS (native) β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Traffic Flow:
https://dns.yourdomain.comβ nginx (443) β Technitium Web UI (5380)https://dns.yourdomain.com/dns-queryβ nginx (443) β Technitium DoH (8053)dns.yourdomain.com:853β Technitium DoT (853) directlydns.yourdomain.com:53β Technitium DNS (53) directly
Part 1: Install Technitium DNS Server
Ubuntu Installation
1
2
# Download and run the installer
curl -sSL https://download.technitium.com/dns/install.sh | sudo bash
The installer creates a systemd service. Verify itβs running:
1
2
3
sudo systemctl status technitium-dns-server
# or depending on your setup:
sudo systemctl status dns
Check what ports Technitium is listening on:
1
sudo ss -tlnp | grep dotnet
Expected output:
1
2
3
LISTEN 0 100 0.0.0.0:853 0.0.0.0:* users:(("dotnet",...))
LISTEN 0 100 0.0.0.0:53 0.0.0.0:* users:(("dotnet",...))
LISTEN 0 512 *:5380 *:* users:(("dotnet",...))
Access the Web UI at http://YOUR_SERVER_IP:5380 to complete initial setup.
Part 2: Install and Configure Nginx
Install Nginx
1
2
sudo apt update
sudo apt install nginx
Configure Nginx as Reverse Proxy
Create the configuration file:
1
sudo nano /etc/nginx/sites-available/dns.conf
Add this configuration (replace yourdomain.com with your actual domain):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
server {
listen 80;
server_name dns.yourdomain.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name dns.yourdomain.com;
ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/yourdomain.com/chain.pem;
access_log /var/log/nginx/dns-access.log;
error_log /var/log/nginx/dns-error.log;
# DoH endpoint
location = /dns-query {
proxy_pass http://127.0.0.1:8053/dns-query;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# Technitium Web UI (everything else)
location / {
proxy_pass http://127.0.0.1:5380;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
Enable the site:
1
2
sudo ln -s /etc/nginx/sites-available/dns.conf /etc/nginx/sites-enabled/
sudo nginx -t
β οΈ Donβt start nginx yet - we need SSL certificates first.
Part 3: SSL Certificates with Letβs Encrypt
Install Certbot with Cloudflare Plugin
1
2
3
sudo apt install certbot python3-certbot-dns-cloudflare
# If on a redhat system like fedora also enable the service:
sudo systemctl enable --now certbot-renew.timer
Create Cloudflare API Credentials
- Go to Cloudflare Dashboard β My Profile β API Tokens
- Create Token β Use βEdit zone DNSβ template
- Zone Resources: Include β Specific zone β yourdomain.com
- Create and copy the token
Create the credentials file:
1
2
sudo mkdir -p /etc/letsencrypt/cloudflare
sudo nano /etc/letsencrypt/cloudflare/credentials.ini
Add:
1
dns_cloudflare_api_token = YOUR_API_TOKEN_HERE
Secure the file:
1
sudo chmod 600 /etc/letsencrypt/cloudflare/credentials.ini
Obtain Wildcard Certificate
1
2
3
4
5
sudo certbot certonly \
--dns-cloudflare \
--dns-cloudflare-credentials /etc/letsencrypt/cloudflare/credentials.ini \
-d "*.yourdomain.com" \
-d "yourdomain.com"
Verify Auto-Renewal
1
sudo certbot renew --dry-run
Check the timer is enabled:
1
sudo systemctl status certbot.timer
Part 4: Configure Technitium for Nginx Proxy
Critical: Port Conflict Resolution
By default, Technitiumβs web service may try to use port 443 for HTTPS, which conflicts with nginx.
Access Technitium UI (temporarily if needed):
1
2
3
4
5
6
# If nginx isn't running yet, access directly:
http://YOUR_SERVER_IP:5380
# Or use SSH tunnel from your local machine:
ssh -L 5380:127.0.0.1:5380 user@YOUR_SERVER_IP
# Then browse to http://localhost:5380
Web Service Settings
Go to Settings β Web Service:
- Enable HTTPS: Uncheck (nginx handles TLS)
- Web Service HTTPS Port: Change from 443 to 8443 (or any unused port)
- Real IP Header: Set to
X-Real-IP
Optional Protocols Settings
Go to Settings β Optional Protocols:
- Enable DNS-over-HTTP: β Check
- DNS-over-HTTP Port: 8053
- Enable DNS-over-TLS: β Check
- DNS-over-TLS Port: 853
- Enable DNS-over-HTTPS: β Check (optional, for direct access)
- DNS-over-HTTPS Port: 8443 (not 443!)
- Reverse Proxy Network ACL: Add
127.0.0.1and::1
β οΈ Important: The Reverse Proxy Network ACL must include
127.0.0.1for nginx to proxy requests successfully.
Save settings and restart Technitium:
1
sudo systemctl restart dns # or technitium-dns-server
Verify ports:
1
sudo ss -tlnp | grep dotnet
Expected:
1
2
3
4
5
LISTEN 0 100 0.0.0.0:853 0.0.0.0:* users:(("dotnet",...))
LISTEN 0 100 0.0.0.0:53 0.0.0.0:* users:(("dotnet",...))
LISTEN 0 512 *:8053 *:* users:(("dotnet",...))
LISTEN 0 512 *:5380 *:* users:(("dotnet",...))
LISTEN 0 512 *:8443 *:* users:(("dotnet",...))
Part 5: Start Services and Configure DNS
Start Nginx
1
2
3
sudo systemctl start nginx
sudo systemctl enable nginx
sudo systemctl status nginx
Cloudflare DNS Records
In Cloudflare DNS management, add:
| Type | Name | Content | Proxy Status |
|---|---|---|---|
| A | dns | YOUR_VPS_IP | DNS only (grey cloud) |
| AAAA | dns | YOUR_VPS_IPv6 | DNS only (grey cloud) |
β οΈ Important: Use βDNS onlyβ (grey cloud), not βProxiedβ (orange cloud). Cloudflareβs proxy interferes with DoH/DoT and serves its own certificate.
Part 6: Testing
Test Web UI
1
curl -I https://dns.yourdomain.com
Expected: HTTP/2 200
Test DoH Endpoint
β οΈ Known Issue: Technitiumβs DoH only accepts POST requests with binary DNS messages. GET requests with query parameters return a 302 redirect.
This does NOT work:
1
2
# GET request returns 302 redirect
curl -H "accept: application/dns-json" "https://dns.yourdomain.com/dns-query?name=google.com&type=A"
This WORKS:
1
2
3
4
5
6
7
8
# Create binary DNS query
echo -n "AAABAAABAAAAAAAAB2dvb2dsZQNjb20AAAEAAQ" | base64 -d > /tmp/dns-query.bin
# POST request with binary DNS message
curl -X POST \
-H "content-type: application/dns-message" \
--data-binary @/tmp/dns-query.bin \
https://dns.yourdomain.com/dns-query
Expected: Binary response (HTTP 200 with content-type: application/dns-message)
Test DoT
1
2
# Using kdig (install: apt install knot-dnsutils)
kdig @dns.yourdomain.com +tls google.com
Test Standard DNS
1
dig @dns.yourdomain.com google.com
Part 7: Client Configuration
DoH URL
1
https://dns.yourdomain.com/dns-query
DoT Server
1
dns.yourdomain.com:853
DNS Stamp Generator
For devices that require DNS stamps (like Ubiquiti), use: https://dnscrypt.info/stamps
Troubleshooting
Port 443 Already in Use
Symptom:
1
nginx: [emerg] bind() to 0.0.0.0:443 failed (98: Address already in use)
Cause: Technitium is using port 443 for its own HTTPS.
Fix:
- Stop both services:
sudo systemctl stop nginx dns - In Technitium UI β Settings β Web Service, change HTTPS port to 8443 or disable HTTPS
- Start Technitium:
sudo systemctl start dns - Start nginx:
sudo systemctl start nginx
DoH Returns 502 Bad Gateway
Symptom: /dns-query returns 502 error
Cause: Technitiumβs DNS-over-HTTP service isnβt running on port 8053.
Fix:
- Check if port 8053 is listening:
sudo ss -tlnp | grep 8053 - In Technitium UI β Settings β Optional Protocols, enable βDNS-over-HTTPβ on port 8053
- Restart Technitium
DoH Returns 302 Redirect
Symptom: DoH requests redirect to root URL
Cause: Two possible issues:
- Reverse Proxy Network ACL doesnβt include
127.0.0.1 - Using GET instead of POST
Fix:
- Add
127.0.0.1to Reverse Proxy Network ACL in Technitium - Use POST requests with binary DNS messages (not GET with query params)
SSL Certificate Mismatch
Symptom:
1
SSL: no alternative certificate subject name matches target host name
Cause: Certificate doesnβt cover the subdomain, or Cloudflare proxy is on.
Fix:
- Verify cert covers the domain:
sudo openssl x509 -in /etc/letsencrypt/live/yourdomain.com/fullchain.pem -text -noout | grep -A1 "Subject Alternative Name" - Ensure Cloudflare DNS record is βDNS onlyβ (grey cloud), not βProxiedβ
- Update nginx config to use correct certificate path
Testing Locally (Bypass DNS/Cloudflare)
To test nginx directly, bypassing DNS resolution:
1
curl -I --resolve dns.yourdomain.com:443:127.0.0.1 https://dns.yourdomain.com
Maintenance
Certificate Renewal
Certificates auto-renew via certbot timer. Test renewal:
1
sudo certbot renew --dry-run
Logs
1
2
3
4
5
6
7
8
# Nginx access log
sudo tail -f /var/log/nginx/dns-access.log
# Nginx error log
sudo tail -f /var/log/nginx/dns-error.log
# Technitium logs
ls /etc/dns/logs/
Service Management
1
2
3
4
5
6
7
8
9
10
# Restart services
sudo systemctl restart nginx
sudo systemctl restart dns
# Check status
sudo systemctl status nginx
sudo systemctl status dns
# View ports in use
sudo ss -tlnp | grep -E '(nginx|dotnet)'
Summary
| Service | URL/Port |
|---|---|
| Web UI | https://dns.yourdomain.com |
| DoH | https://dns.yourdomain.com/dns-query (POST only) |
| DoT | dns.yourdomain.com:853 |
| DNS | dns.yourdomain.com:53 |