⚠️ DEPRECATED — This project has been replaced by dnsweaverThis repository will be removed on January 15, 2026.
After releasing this project, I discovered that another project called Technitium DNS Companion by @Fail-Safe already existed with essentially the same name. To avoid confusion in the Technitium community, I've rebranded and expanded this project into dnsweaver.
dnsweaver is the spiritual successor with expanded capabilities:
- Multiple DNS providers: Technitium, Cloudflare (Route53, Pi-hole, AdGuard Home coming soon)
- Multiple sources: Traefik labels + static config files (nginx, Caddy, HAProxy planned)
- Ownership tracking: TXT records prevent accidental deletion of manual DNS entries
- Multi-provider routing: Internal hostnames → Technitium, public hostnames → Cloudflare
dnsweaver is a drop-in replacement. Main config changes:
- Environment prefix:
TC_/TECHNITIUM_→DNSWEAVER_- Provider config is now named:
DNSWEAVER_{NAME}_TYPE=technitiumExample migration:
# Old (technitium-companion) TECHNITIUM_URL=http://dns:5380 TECHNITIUM_TOKEN=xxx TECHNITIUM_ZONE=home.example.com TARGET_IP=192.168.1.100 # New (dnsweaver) DNSWEAVER_TECHNITIUM_TYPE=technitium DNSWEAVER_TECHNITIUM_URL=http://dns:5380 DNSWEAVER_TECHNITIUM_TOKEN=xxx DNSWEAVER_TECHNITIUM_ZONE=home.example.com DNSWEAVER_TECHNITIUM_TARGET=192.168.1.100 DNSWEAVER_TECHNITIUM_DOMAINS=*.home.example.com DNSWEAVER_PROVIDERS=technitium
- dnsweaver (successor): https://github.com/maxfield-allison/dnsweaver
- Technitium DNS Companion (the "official" holder of the companion name): https://github.com/Fail-Safe/Technitium-DNS-Companion
Automatic DNS record management for Docker containers.
technitium-companion watches Docker events and automatically creates and deletes DNS A records in Technitium DNS Server for services with Traefik labels. Built for homelabs and self-hosted environments where you want automatic DNS resolution for your containerized services.
- Docker and Swarm Support: Works with standalone Docker and Docker Swarm clusters
- Traefik Integration: Parses
traefik.http.routers.*.rulelabels to extract hostnames - Real-time Sync: Watches Docker events and creates/deletes records instantly
- Startup Reconciliation: Full sync on startup ensures consistency
- Flexible Filtering: Include/exclude patterns to control which hostnames are managed
- Prometheus Metrics: Full observability with detailed metrics
- Secrets Support: Docker secrets compatible via
_FILEsuffix variables - Health Endpoints:
/health,/ready, and/metricsfor monitoring - Socket Proxy Support: Works with Docker socket proxies for enhanced security
- Multi-arch Images: Supports linux/amd64 and linux/arm64
docker pull maxamill/technitium-companion:latestdocker pull ghcr.io/maxfield-allison/technitium-companion:latestservices:
technitium-companion:
image: maxamill/technitium-companion:latest
restart: unless-stopped
environment:
- TECHNITIUM_URL=http://your-technitium-server:5380
- TECHNITIUM_TOKEN=your-api-token
- TECHNITIUM_ZONE=home.example.com
- TARGET_IP=192.168.1.100
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
ports:
- "8080:8080"-
A container starts with a Traefik label:
labels: - "traefik.http.routers.myapp.rule=Host(`myapp.home.example.com`)"
-
technitium-companion detects the event and creates an A record:
myapp.home.example.com -> 192.168.1.100 -
When the container stops, the A record is automatically deleted.
All configuration is via environment variables. Variables support the _FILE suffix for Docker secrets (e.g., TECHNITIUM_TOKEN_FILE=/run/secrets/dns_token).
| Variable | Description |
|---|---|
TECHNITIUM_URL |
Technitium DNS server URL (e.g., http://dns.example.com:5380) |
TECHNITIUM_TOKEN |
API token from Technitium Admin, Settings, API |
TECHNITIUM_ZONE |
DNS zone to manage (e.g., home.example.com) |
TARGET_IP |
IP address for all A records (typically your ingress or load balancer) |
| Variable | Default | Description |
|---|---|---|
TTL |
300 |
DNS record TTL in seconds |
INCLUDE_PATTERN |
.* |
Regex pattern; only matching hostnames are managed |
EXCLUDE_PATTERN |
(none) | Regex pattern; matching hostnames are skipped |
DOCKER_HOST |
unix:///var/run/docker.sock |
Docker daemon socket or TCP address |
DOCKER_MODE |
auto |
auto (detect), swarm, or standalone |
RECONCILE_ON_STARTUP |
true |
Run full reconciliation at startup |
DRY_RUN |
false |
Log changes without applying them |
HEALTH_PORT |
8080 |
Port for health and metrics endpoints |
LOG_LEVEL |
info |
Logging level: debug, info, warn, error |
# Only manage *.internal.example.com hostnames
INCLUDE_PATTERN='^[^.]+\.internal\.example\.com$'
# Exclude monitoring and test services
EXCLUDE_PATTERN='^(grafana|prometheus|test-).*'
# Combine both: only internal, but skip monitoring
INCLUDE_PATTERN='\.internal\.example\.com$'
EXCLUDE_PATTERN='^(grafana|prometheus)\.'services:
technitium-companion:
image: ghcr.io/maxfield-allison/technitium-companion:latest
container_name: technitium-companion
restart: unless-stopped
environment:
- TECHNITIUM_URL=http://technitium:5380
- TECHNITIUM_TOKEN=${TECHNITIUM_TOKEN}
- TECHNITIUM_ZONE=home.example.com
- TARGET_IP=192.168.1.100
- LOG_LEVEL=info
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
ports:
- "8080:8080"
healthcheck:
test: ["CMD", "wget", "-qO-", "http://localhost:8080/health"]
interval: 30s
timeout: 5s
retries: 3For Swarm deployments, run on manager nodes to access Swarm service information:
version: "3.8"
services:
technitium-companion:
image: ghcr.io/maxfield-allison/technitium-companion:latest
deploy:
mode: replicated
replicas: 1
placement:
constraints:
- node.role == manager
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 3
environment:
- TECHNITIUM_URL=http://technitium:5380
- TECHNITIUM_TOKEN_FILE=/run/secrets/technitium_token
- TECHNITIUM_ZONE=home.example.com
- TARGET_IP=192.168.1.100
- DOCKER_MODE=swarm
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
secrets:
- technitium_token
networks:
- internal
healthcheck:
test: ["CMD", "wget", "-qO-", "http://localhost:8080/health"]
interval: 30s
timeout: 5s
retries: 3
secrets:
technitium_token:
external: true
networks:
internal:
external: trueDeploy with:
docker stack deploy -c docker-stack.yml technitium-companionFor enhanced security, you can use a Docker socket proxy instead of mounting the Docker socket directly. This limits what API calls technitium-companion can make.
Example with Tecnativa/docker-socket-proxy:
services:
socket-proxy:
image: tecnativa/docker-socket-proxy:latest
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
environment:
- CONTAINERS=1
- SERVICES=1
- TASKS=1
- NETWORKS=1
- EVENTS=1
networks:
- socket-proxy
technitium-companion:
image: ghcr.io/maxfield-allison/technitium-companion:latest
environment:
- DOCKER_HOST=tcp://socket-proxy:2375
- TECHNITIUM_URL=http://technitium:5380
- TECHNITIUM_TOKEN=${TECHNITIUM_TOKEN}
- TECHNITIUM_ZONE=home.example.com
- TARGET_IP=192.168.1.100
networks:
- socket-proxy
depends_on:
- socket-proxy
networks:
socket-proxy:
driver: bridgeRequired socket proxy permissions:
CONTAINERS=1: Read container information and labelsSERVICES=1: Read Swarm service information (Swarm mode only)TASKS=1: Read Swarm task information (Swarm mode only)NETWORKS=1: Read network informationEVENTS=1: Subscribe to Docker events
| Endpoint | Description |
|---|---|
/health |
Liveness probe; returns 200 if service is running |
/ready |
Readiness probe; returns 200 after startup reconciliation |
/metrics |
Prometheus metrics endpoint |
Counters:
technitium_companion_dns_records_created_total{zone}: DNS records createdtechnitium_companion_dns_records_deleted_total{zone}: DNS records deletedtechnitium_companion_dns_records_existed_total{zone}: Records that already existedtechnitium_companion_api_requests_total{endpoint,status}: Technitium API callstechnitium_companion_docker_events_total{type,action}: Docker events processedtechnitium_companion_reconciliations_total{status}: Reconciliation runs
Histograms:
technitium_companion_api_request_duration_seconds{endpoint}: API latencytechnitium_companion_reconciliation_duration_seconds: Reconciliation duration
Gauges:
technitium_companion_up: Service health (1 = up)technitium_companion_workloads_scanned: Workloads in last reconciliationtechnitium_companion_hostnames_found: Hostnames found in last reconciliationtechnitium_companion_last_reconciliation_timestamp_seconds: Last successful reconciliationtechnitium_companion_build_info{version,go_version}: Build information
Example queries for a Grafana dashboard:
# DNS record creation rate (per minute)
rate(technitium_companion_dns_records_created_total[5m]) * 60
# API success rate
sum(rate(technitium_companion_api_requests_total{status="success"}[5m])) /
sum(rate(technitium_companion_api_requests_total[5m])) * 100
# Reconciliation success rate
sum(rate(technitium_companion_reconciliations_total{status="success"}[1h])) /
sum(rate(technitium_companion_reconciliations_total[1h])) * 100
# Time since last reconciliation
time() - technitium_companion_last_reconciliation_timestamp_seconds
scrape_configs:
- job_name: 'technitium-companion'
static_configs:
- targets: ['technitium-companion:8080']
scrape_interval: 15sNo DNS records being created:
- Verify Traefik labels on your containers:
docker inspect <container> | grep -i traefik
- Check the label format. The Host value must be wrapped in backticks:
traefik.http.routers.<name>.rule=Host(`hostname.example.com`) - Enable debug logging:
LOG_LEVEL=debug - Check if hostname matches
INCLUDE_PATTERNand does not matchEXCLUDE_PATTERN
Connection refused to Technitium:
- Verify
TECHNITIUM_URLis reachable from the container - Check API token permissions in Technitium Admin under Settings, API
- If running in Docker, ensure network connectivity to the DNS server
Records created but not resolving:
- Verify the zone exists in Technitium
- Check that
TARGET_IPis correct - Ensure the zone is authoritative in Technitium
Swarm mode not detecting services:
- Ensure container runs on a manager node (constraint:
node.role == manager) - Set
DOCKER_MODE=swarmexplicitly - Verify Docker socket is mounted:
/var/run/docker.sock:/var/run/docker.sock:ro
Using a socket proxy and getting permission errors:
- Ensure the proxy has
EVENTS=1enabled for event watching - For Swarm mode, enable
SERVICES=1andTASKS=1 - Check proxy logs for denied API calls
Enable verbose logging to troubleshoot:
docker run -e LOG_LEVEL=debug ...Logs include:
- Docker events received
- Hostnames parsed from labels
- DNS API calls and responses
- Record create/delete operations
Test configuration without making changes:
docker run -e DRY_RUN=true ...# Clone
git clone https://github.com/maxfield-allison/technitium-companion.git
cd technitium-companion
# Build
go build -o technitium-companion ./cmd/technitium-companion
# Run tests
go test -v ./...
# Build Docker image
docker build -t technitium-companion:local .Contributions are welcome. See CONTRIBUTING.md for guidelines.
This project is licensed under the MIT License. See the LICENSE file for details.