homelab-blueprint/docker-compose-files/technitium/README.md
Brian Pooe 1815beadf4 refactor: restructure docs, sync stacks with Caddyfile, harden compose templates
- 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
2026-07-03 17:45:16 +02:00

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.