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
.envand 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,ERRORorFATAL.LOG_FILE- path of the log file or empty to log to stdout/stderr.BACKEND—HTTP_API(default) orRFC2136.RUN_ONCE—1to run a single update and exit,0to 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 to1to 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/AAAAvalues; 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_APIbackend calls Technitium endpoint/api/zones/records/addwithoverwrite=true.RFC2136backend 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_NAMEandRFC2136_TSIG_KEY(base64). ProvideRFC2136_TSIG_ALGOlikehmac-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_TOKENandRFC2136_TSIG_KEYas 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.