- Add compose stacks for apps already proxied by Caddy: bento-pdf,
it-tools, paperless-ngx, and beszel-hub (with Makefile targets)
- Remove the unused Vault stack and the drawio Caddyfile block
- Fix Caddyfile gramps upstream to port 80 to match the compose file
- Bring immich and gramps-web up to the shared template pattern
(no-new-privileges, log rotation, memory limits, healthchecks)
- Single-quote vaultwarden SSO_AUDIENCE_TRUSTED so regex values like
^\d{18}$ render as valid YAML
- Move root docs into kebab-case topic folders, rebuild the docs index
to cover every doc, and fix all broken links
310 lines
9.3 KiB
Markdown
310 lines
9.3 KiB
Markdown
# Technitium DNS
|
|
|
|
Docker deployment for Technitium DNS Server. Run all commands from the
|
|
repository root.
|
|
|
|
## Persistent Data
|
|
|
|
Technitium configuration, zones, and application state are stored in the
|
|
Docker-managed named volume `technitium_data`.
|
|
|
|
Existing deployments that use `appdata/technitium` must copy that data into
|
|
the named volume before recreating the container.
|
|
|
|
## Installation
|
|
|
|
1. Create `.env` if it does not exist:
|
|
|
|
```bash
|
|
cp -n .env.sample .env
|
|
```
|
|
|
|
2. Check the host addresses, then set the required values in `.env`:
|
|
|
|
```bash
|
|
ip -br address
|
|
```
|
|
|
|
```dotenv
|
|
TZ=Africa/Johannesburg
|
|
DNS_BIND_IP=10.0.10.5
|
|
TECHNITIUM_ADMIN_PASSWORD=<strong-password>
|
|
|
|
TECHNITIUM_WEB_BIND_IP=0.0.0.0
|
|
TECHNITIUM_WEB_PORT=5380
|
|
TECHNITIUM_MEM_LIMIT=512m
|
|
|
|
DOCKERLOGGING_MAXFILE=3
|
|
DOCKERLOGGING_MAXSIZE=10m
|
|
```
|
|
|
|
`DNS_BIND_IP` must be assigned to the host. Use `0.0.0.0` to listen on every
|
|
host interface.
|
|
|
|
3. Render the template:
|
|
|
|
```bash
|
|
./substitute_env.sh docker-compose-files/technitium/template.yaml docker-compose.technitium.yml .env
|
|
```
|
|
|
|
`docker-compose.technitium.yml` contains the rendered admin password and is
|
|
ignored by Git.
|
|
|
|
4. Validate and start Technitium:
|
|
|
|
```bash
|
|
docker compose -f docker-compose.technitium.yml config --quiet
|
|
docker compose -f docker-compose.technitium.yml up -d
|
|
```
|
|
|
|
5. Verify the container and open `http://10.0.10.5:5380`:
|
|
|
|
```bash
|
|
docker compose -f docker-compose.technitium.yml ps
|
|
docker compose -f docker-compose.technitium.yml logs --tail=100 technitium-dns
|
|
```
|
|
|
|
## Apply Configuration Changes
|
|
|
|
After changing `.env` or `template.yaml`, render the Compose file and recreate
|
|
the container:
|
|
|
|
```bash
|
|
./substitute_env.sh docker-compose-files/technitium/template.yaml docker-compose.technitium.yml .env
|
|
docker compose -f docker-compose.technitium.yml config --quiet
|
|
docker compose -f docker-compose.technitium.yml up -d
|
|
```
|
|
|
|
## Initial Setup
|
|
|
|
In the Technitium admin interface:
|
|
|
|
1. Create the primary internal zone, for example `home.example.com`.
|
|
2. Add records for internal services that do not use Caddy.
|
|
3. If using Caddy, add an `A` record named `caddy` pointing to `10.0.15.5`,
|
|
then add a wildcard `CNAME` named `*` pointing to `caddy.home.example.com`.
|
|
4. Configure DHCP to advertise the Technitium host as the DNS server.
|
|
5. Configure the recommended blocklists below.
|
|
|
|
Use relative record names such as `caddy` and `*`, not full domain names.
|
|
|
|
### Recommended Blocklists
|
|
|
|
In **Settings > Blocking > Block List URLs**, add the lists you need:
|
|
|
|
| Purpose | URL |
|
|
|---|---|
|
|
| Ads and trackers | `https://cdn.jsdelivr.net/gh/hagezi/dns-blocklists@latest/wildcard/pro-onlydomains.txt` |
|
|
| Porn / NSFW | `https://cdn.jsdelivr.net/gh/hagezi/dns-blocklists@latest/wildcard/nsfw-onlydomains.txt` |
|
|
| Gambling | `https://cdn.jsdelivr.net/gh/hagezi/dns-blocklists@latest/wildcard/gambling-onlydomains.txt` |
|
|
|
|
The wildcard `onlydomains` format uses less memory than hosts-format lists.
|
|
Avoid `tif-onlydomains.txt` with the default 512 MB limit because it can cause
|
|
`OutOfMemoryException`. Click **Update Now** after changing the lists.
|
|
|
|
## Enable On UDM Pro SE
|
|
|
|
Use this sequence with the latest UniFi OS and UniFi Network. It follows the
|
|
current zone design (see [firewall-rules.md](../../docs/network/firewall-rules.md)):
|
|
|
|
- Technitium: `10.0.10.5`, Office VLAN 10, `TRUSTED` zone
|
|
- Caddy: `10.0.15.5`, DMZ VLAN 15, `DMZ` zone
|
|
- Current client VLANs: LAN, Office, Family, IoT, Media, and Guest
|
|
|
|
Complete **Use Technitium Now** first. The custom-zone and DNS-enforcement
|
|
steps are later migration work; do not configure them until normal DNS works
|
|
from every VLAN.
|
|
|
|
### Use Technitium Now
|
|
|
|
#### 1. Confirm Technitium Answers DNS
|
|
|
|
From an Office VLAN client:
|
|
|
|
```bash
|
|
nslookup google.com 10.0.10.5
|
|
```
|
|
|
|
Expected result: the command returns one or more addresses and identifies
|
|
`10.0.10.5` as the DNS server.
|
|
|
|
#### 2. Confirm Technitium Allows Your VLANs
|
|
|
|
The Compose template initializes Technitium with recursion enabled for private
|
|
networks. To verify or change this in the latest Technitium web console:
|
|
|
|
1. Open `http://10.0.10.5:5380`.
|
|
2. Open **Settings** from the main navigation.
|
|
3. Find the **Recursion** section on the Settings page.
|
|
4. Set **Recursion** to **Use Specified Network ACL**.
|
|
5. In **Recursion Network ACL**, allow these networks in this order:
|
|
|
|
```text
|
|
127.0.0.1/32
|
|
::1/128
|
|
10.0.1.0/24
|
|
10.0.10.0/24
|
|
10.0.15.0/24
|
|
10.0.20.0/24
|
|
10.0.30.0/24
|
|
10.0.40.0/24
|
|
10.0.50.0/24
|
|
```
|
|
|
|
6. Save the Settings page.
|
|
|
|
`Recursion Network ACL` is the current field name. There is no separate
|
|
**Settings > DNS Server** menu.
|
|
|
|
Technitium reads the Docker recursion environment variables only when creating
|
|
its configuration for the first time. For an existing installation, the saved
|
|
value shown on the Settings page is authoritative.
|
|
|
|
#### 3. Test Direct DNS From Every VLAN
|
|
|
|
Connect a client to each current VLAN and run:
|
|
|
|
```bash
|
|
nslookup google.com 10.0.10.5
|
|
```
|
|
|
|
Expected result: every VLAN receives a DNS answer from `10.0.10.5`.
|
|
|
|
If a VLAN times out, create a temporary firewall allow before continuing:
|
|
|
|
1. In UniFi Network, open **Settings > Policy Engine > Policy Table**.
|
|
2. Create a new **Firewall** policy with action **Allow**.
|
|
3. Select the VLAN's current source zone and the specific source network.
|
|
4. Set destination zone to `TRUSTED`.
|
|
5. Set destination matcher to **IP** and enter `10.0.10.5`.
|
|
6. Select IPv4, protocol **TCP/UDP**, and destination port `53`.
|
|
7. Save the policy and retest the direct query.
|
|
|
|
Do not create final FAMILY, MEDIA, IOT, GUEST, or DMZ zone policies until those
|
|
custom zones exist.
|
|
|
|
#### 4. Advertise Technitium Through UDM DHCP
|
|
|
|
Repeat these steps for LAN, Office, Family, IoT, Media, and Guest:
|
|
|
|
1. In UniFi Network, open **Settings > Networks**.
|
|
2. Select the network.
|
|
3. Find the DHCP settings. In the current UI this is normally under
|
|
**DHCP Service Management**.
|
|
4. Set the DHCP DNS server/name server to **Manual**.
|
|
5. Enter only `10.0.10.5`. Do not add a public secondary resolver.
|
|
6. Save the network.
|
|
7. Reconnect the client or renew its DHCP lease.
|
|
|
|
This is a per-network DHCP setting. Do not change the UDM's WAN/Internet DNS
|
|
setting.
|
|
|
|
#### 5. Verify Clients Use Technitium
|
|
|
|
From one renewed client on each VLAN:
|
|
|
|
```bash
|
|
nslookup google.com
|
|
```
|
|
|
|
Expected result: the output identifies `10.0.10.5` as the server and returns a
|
|
DNS answer. The Technitium Dashboard query counters and client statistics
|
|
should increase.
|
|
|
|
Stop here until normal DNS works from every VLAN.
|
|
|
|
### Later: Apply Final Zone Policies
|
|
|
|
Create these only while migrating each VLAN into its target custom zone:
|
|
|
|
| Name | Source zone | Destination zone | Destination IP | Protocol | Destination port |
|
|
|---|---|---|---|---|---|
|
|
| `ALLOW DMZ to DNS` | `DMZ` | `TRUSTED` | `10.0.10.5` | TCP/UDP | `53` |
|
|
| `ALLOW FAMILY to DNS` | `FAMILY` | `TRUSTED` | `10.0.10.5` | TCP/UDP | `53` |
|
|
| `ALLOW MEDIA to DNS` | `MEDIA` | `TRUSTED` | `10.0.10.5` | TCP/UDP | `53` |
|
|
| `ALLOW IOT to DNS` | `IOT` | `TRUSTED` | `10.0.10.5` | TCP/UDP | `53` |
|
|
| `ALLOW GUEST to DNS` | `GUEST` | `TRUSTED` | `10.0.10.5` | TCP/UDP | `53` |
|
|
|
|
Place each DNS allow above that zone's RFC1918 and terminal block policies.
|
|
Remove the matching temporary rule after the final rule works.
|
|
|
|
### Later: Enable DNS Enforcement
|
|
|
|
UniFi Network 10.4.57 does not expose supported LAN-facing DNS DNAT. After the
|
|
final zone policies and DHCP-provided DNS work, verify that each restricted
|
|
zone can query Technitium and that port 53 queries to other destinations are
|
|
blocked.
|
|
|
|
Do not block public DNS from `TRUSTED` or `DMZ`. Office clients are trusted,
|
|
and Caddy in DMZ must query public resolvers for ACME DNS-01.
|
|
|
|
## Network Requirements
|
|
|
|
- Allow client networks to reach Technitium on TCP and UDP port `53`.
|
|
- Restrict the web admin port `5380` to trusted administration networks.
|
|
- If `DNS_BIND_IP=0.0.0.0`, use firewall rules to restrict DNS access.
|
|
- Ensure no other service on the host is already using port `53`.
|
|
|
|
Test DNS from a client:
|
|
|
|
```bash
|
|
nslookup google.com 10.0.10.5
|
|
nslookup proxmox.home.example.com 10.0.10.5
|
|
```
|
|
|
|
Replace `10.0.10.5` and `home.example.com` with your configured values.
|
|
|
|
## Troubleshooting
|
|
|
|
The following error means `DNS_BIND_IP` is not assigned to the host:
|
|
|
|
```text
|
|
failed to bind host port 10.0.10.5:53/tcp: cannot assign requested address
|
|
```
|
|
|
|
Check the host addresses and port usage:
|
|
|
|
```bash
|
|
ip -br address
|
|
sudo ss -lntup '( sport = :53 )'
|
|
```
|
|
|
|
Set `DNS_BIND_IP` to an assigned address or `0.0.0.0`, render the template
|
|
again, and restart the stack.
|
|
|
|
### Raspberry Pi OS Memory Limits
|
|
|
|
If Docker reports the following warning, Technitium still starts, but Docker
|
|
ignores `TECHNITIUM_MEM_LIMIT`:
|
|
|
|
```text
|
|
Your kernel does not support memory limit capabilities or the cgroup is not mounted. Limitation discarded.
|
|
```
|
|
|
|
Enable memory cgroups by appending these parameters to the existing single line
|
|
in `/boot/firmware/cmdline.txt`:
|
|
|
|
```text
|
|
cgroup_enable=memory cgroup_memory=1
|
|
```
|
|
|
|
Older Raspberry Pi OS releases may use `/boot/cmdline.txt`. Do not add a new
|
|
line to either file. Reboot and verify:
|
|
|
|
```bash
|
|
sudo reboot
|
|
docker info | grep -i warning
|
|
docker stats --no-stream technitium-dns
|
|
```
|
|
|
|
## Useful Commands
|
|
|
|
```bash
|
|
# Published admin port
|
|
docker compose -f docker-compose.technitium.yml port technitium-dns 5380
|
|
```
|
|
|
|
## Backup
|
|
|
|
Back up the `technitium_data` named volume. It contains the DNS configuration,
|
|
zones, and application state.
|