Files
technitium-ddns/README.md

4.9 KiB

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

  1. Create .env and fill in secrets and other variables.
  2. 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 or FATAL.
  • LOG_FILE - path of the log file or empty to log to stdout/stderr.
  • BACKENDHTTP_API (default) or RFC2136.
  • RUN_ONCE1 to run a single update and exit, 0 to loop continuously (default 0).
  • UPDATE_INTERVAL — seconds between updates when looping (default 300).
  • 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 (default 300).
  • SKIP_IPV6 — set to 1 to skip AAAA updates.

HTTP API backend (Technitium):

  • TDNS_HOST — Technitium base URL.
  • TDNS_API_TOKEN — API token generated in Technitium web console (required for HTTP_API).

RFC2136 backend:

  • RFC2136_SERVER — authoritative DNS server IP or hostname for dynamic updates (required for RFC2136).
  • RFC2136_PORT — port (default 53).
  • RFC2136_TSIG_NAME — optional TSIG key name.
  • RFC2136_TSIG_KEY — optional TSIG key (base64).
  • RFC2136_TSIG_ALGO — optional TSIG algorithm (default hmac-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:
    1. Detects public IPv4 (via api.ipify.org, fallback to other services).
    2. Detects public IPv6 (unless SKIP_IPV6=1).
    3. Queries current A / AAAA values; if they already match the current public IP, update is skipped.
    4. Otherwise updates the record(s) using the selected backend.
  • HTTP_API backend calls Technitium endpoint /api/zones/records/add with overwrite=true.
  • RFC2136 backend issues a dynamic DNS update packet and can use TSIG for authentication.

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 and RFC2136_TSIG_KEY (base64). Provide RFC2136_TSIG_ALGO like hmac-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 and RFC2136_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.