Technitium DDNS
Python utility that fetches the host's public IPv4 and IPv6 and updates A
and AAAA
records for a given hostname in a zone hosted on a Technitium DNS server.
This repository contains a single service that supports two update backends:
- HTTP_API — use Technitium's HTTP API (token-based) to add/overwrite records.
- RFC2136 — use standard DNS dynamic updates (RFC2136), optionally authenticated with a TSIG key.
The service runs in a continuous loop by default and updates every UPDATE_INTERVAL
seconds (configurable via environment variables).
Quick start
- Create
.env
and fill in secrets and other variables. - Build and run:
docker compose build
docker compose up -d
Or to run with Docker directly:
docker build -t technitium-ddns .
docker run -d --name technitium-ddns --env-file .env technitium-ddns
Environment variables
You can set these in .env
, via docker-compose.yml
, or when running the container.
General:
LOG_LEVEL
-DEBUG
,INFO
,WARNING
,ERROR
orFATAL
.LOG_FILE
- path of the log file or empty to log to stdout/stderr.BACKEND
—HTTP_API
(default) orRFC2136
.RUN_ONCE
—1
to run a single update and exit,0
to loop continuously (default0
).UPDATE_INTERVAL
— seconds between updates when looping (default300
).ZONE
— DNS zone to update the record into.RECORD_NAME
— record name (left part) — the script updates<RECORD_NAME>.<ZONE>
.TTL
— record TTL in seconds (default300
).SKIP_IPV6
— set to1
to skip AAAA updates.
HTTP API backend (Technitium):
TDNS_HOST
— Technitium base URL.TDNS_API_TOKEN
— API token generated in Technitium web console (required forHTTP_API
).
RFC2136 backend:
RFC2136_SERVER
— authoritative DNS server IP or hostname for dynamic updates (required forRFC2136
).RFC2136_PORT
— port (default53
).RFC2136_TSIG_NAME
— optional TSIG key name.RFC2136_TSIG_KEY
— optional TSIG key (base64).RFC2136_TSIG_ALGO
— optional TSIG algorithm (defaulthmac-sha256.
).
Example .env
# Log level and log file
LOG_LEVEL=INFO
LOG_FILE=/var/log/technitium-ddns.log
# Choose HTTP_API or RFC2136
BACKEND=HTTP_API
# If RUN_ONCE=1 the container runs once and exits (good for host cron).
RUN_ONCE=0
UPDATE_INTERVAL=300
ZONE=example.com
RECORD_NAME=ddns
TTL=300
SKIP_IPV6=0
# HTTP API (Technitium)
TDNS_HOST=http://technitium:5380
TDNS_API_TOKEN=YOUR_TECHNITIUM_TOKEN_HERE
# RFC2136 (if using RFC2136 backend)
RFC2136_SERVER=192.0.2.5
RFC2136_PORT=53
# Optional TSIG:
RFC2136_TSIG_NAME=ddns-key
RFC2136_TSIG_KEY=base64encodedkey==
RFC2136_TSIG_ALGO=hmac-sha256.
How it behaves
- On each run (single-run or loop iteration) the script:
- Detects public IPv4 (via
api.ipify.org
, fallback to other services). - Detects public IPv6 (unless
SKIP_IPV6=1
). - Queries current
A
/AAAA
values; if they already match the current public IP, update is skipped. - Otherwise updates the record(s) using the selected backend.
- Detects public IPv4 (via
HTTP_API
backend calls Technitium endpoint/api/zones/records/add
withoverwrite=true
.RFC2136
backend issues a dynamic DNS update packet and can use TSIG for authentication.
Running as a cron job on the host (recommended for many setups)
If you prefer not to run a continuously-looping container, set RUN_ONCE=1
and run the container from a host cron or systemd timer:
Example host systemd timer unit (recommended):
# /etc/systemd/system/tdns-updater-run.service
[Unit]
Description=Run tdns-updater once
[Service]
Type=oneshot
WorkingDirectory=/path/to/project
ExecStart=/usr/bin/docker compose run --rm tdns-updater
# /etc/systemd/system/tdns-updater-run.timer
[Unit]
Description=Run tdns-updater every 5 minutes
[Timer]
OnBootSec=1min
OnUnitActiveSec=5min
[Install]
WantedBy=timers.target
RFC2136 notes
- Many servers expect dynamic updates to come from an allowed IP or require TSIG authentication. If using Technitium as the RFC2136 target, check its dynamic update / ACL settings and configure TSIG if required.
- The script supports TSIG via
RFC2136_TSIG_NAME
andRFC2136_TSIG_KEY
(base64). ProvideRFC2136_TSIG_ALGO
likehmac-sha256.
if needed.
TLS / self-signed certs
If your TDNS_HOST
is https://
with a self-signed certificate, mount CA certificate(s) into the container and set REQUESTS_CA_BUNDLE
or SSL_CERT_FILE
appropriately, or use valid certificates to avoid modifying the image:
Example docker-compose.yml
snippet for a CA bundle file:
volumes:
- ./my-ca.pem:/usr/local/share/ca-certificates/my-ca.crt:ro
(then update CA store inside the image at build time, or pass REQUESTS_CA_BUNDLE
pointing to the file.)
Security
- Treat
TDNS_API_TOKEN
andRFC2136_TSIG_KEY
as secrets. Do not commit them to VCS. - For production, use Docker secrets, environment injection from your orchestrator, or mount a read-only file containing secrets.