Docker Swarm¶
Docker Swarm mode provides service discovery across a cluster. dnsweaver integrates with Swarm to manage DNS for services.
How Swarm Mode Differs¶
| Aspect | Standalone Docker | Docker Swarm |
|---|---|---|
| Labels on | Containers | Services |
| Watch events | Container start/stop | Service create/update/remove |
| Replicas | 1 container | Multiple tasks |
| DNS target | Container IP or gateway | Service VIP or ingress |
Enabling Swarm Mode¶
dnsweaver auto-detects Swarm mode, but you can force it:
Swarm Service Labels¶
Labels must be on the service definition, not deploy labels:
services:
myapp:
image: myapp:latest
labels: # ✅ Service labels (top-level)
- "traefik.http.routers.myapp.rule=Host(`app.example.com`)"
deploy:
labels: # ❌ Deploy labels (not read by dnsweaver)
- "some.deploy.label=value"
Target Configuration¶
For Swarm, TARGET typically points to your reverse proxy or Swarm ingress:
VIP Mode (Recommended)¶
Point to the Swarm VIP for your reverse proxy:
Ingress Mode¶
Point to the Swarm ingress network gateway:
CNAME to Proxy¶
Point to the proxy's DNS name:
Deployment Example¶
Complete Swarm stack with dnsweaver:
version: "3.8"
services:
dnsweaver:
image: maxamill/dnsweaver:latest
environment:
- DNSWEAVER_INSTANCES=internal
- DNSWEAVER_INTERNAL_TYPE=technitium
- DNSWEAVER_INTERNAL_URL=http://dns:5380
- DNSWEAVER_INTERNAL_TOKEN_FILE=/run/secrets/dns_token
- DNSWEAVER_INTERNAL_ZONE=home.example.com
- DNSWEAVER_INTERNAL_RECORD_TYPE=A
- DNSWEAVER_INTERNAL_TARGET=192.0.2.100
- DNSWEAVER_INTERNAL_DOMAINS=*.home.example.com
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
deploy:
mode: replicated
replicas: 1
placement:
constraints:
- node.role == manager
secrets:
- dns_token
secrets:
dns_token:
external: true
Important
dnsweaver must run on a manager node to access the Swarm API.
Manager Node Constraint¶
dnsweaver needs access to the Swarm manager API. Always include:
High Availability¶
For HA, run dnsweaver with a single replica:
deploy:
mode: replicated
replicas: 1 # Only one instance should manage DNS
update_config:
parallelism: 1
delay: 10s
restart_policy:
condition: any
Running multiple instances could cause duplicate record creation or deletion race conditions.
Service Updates¶
When a Swarm service is updated:
- dnsweaver detects the update event
- Compares old vs new labels
- Removes records for deleted hostnames
- Creates records for new hostnames
- Updates records if target changes
Per-Entrypoint Routing¶
When the same Traefik router is bound to multiple entrypoints, dnsweaver
emits one extraction per (host, entrypoint) pair. Combined with the
per-instance DNSWEAVER_{NAME}_ENTRYPOINTS filter, this lets you point
each Traefik entrypoint at a different DNS target — useful for
split-horizon LAN/VPN setups, or any scenario where one router needs to
resolve to different IPs depending on which listener it was published on.
Example: Split LAN / VPN Targets¶
Two entrypoints (webA, webB) on the same Traefik router need to
resolve web.example.com to different IPs:
services:
reverse-proxy:
image: traefik:v3
command:
- "--entrypoints.webA.address=:80"
- "--entrypoints.webB.address=:8080"
myapp:
image: myapp:latest
labels:
- "traefik.http.routers.myapp.rule=Host(`web.example.com`)"
- "traefik.http.routers.myapp.entrypoints=webA,webB"
dnsweaver:
image: maxamill/dnsweaver:latest
environment:
- DNSWEAVER_INSTANCES=lan,vpn
# LAN instance answers only routers bound to webA -> 10.0.0.10
- DNSWEAVER_LAN_TYPE=technitium
- DNSWEAVER_LAN_TARGET=10.0.0.10
- DNSWEAVER_LAN_DOMAINS=*.example.com
- DNSWEAVER_LAN_ENTRYPOINTS=webA
# VPN instance answers only routers bound to webB -> 10.99.0.10
- DNSWEAVER_VPN_TYPE=technitium
- DNSWEAVER_VPN_TARGET=10.99.0.10
- DNSWEAVER_VPN_DOMAINS=*.example.com
- DNSWEAVER_VPN_ENTRYPOINTS=webB
Matching Semantics¶
DNSWEAVER_{NAME}_ENTRYPOINTSaccepts a comma-separated allowlist (webA,webB). Whitespace is trimmed; empty values are ignored.- A router with no
entrypointslabel produces a wildcard extraction that matches every instance regardless ofENTRYPOINTSfilter (preserves pre-1.4 behavior). - A router with
entrypoints=webA,webBproduces two distinct extractions; each instance only receives the ones whose entrypoint is in its allowlist. - The same
(host, entrypoint)pair declared in multiple containers/files is deduplicated.
Traefik asDefault Entrypoints¶
Traefik supports flagging entrypoints as
asDefault = true.
When set, routers without an explicit entryPoints declaration bind
only to the asDefault entrypoints — not to all entrypoints.
dnsweaver cannot read Traefik's static config, so the wildcard behavior
above will silently over-publish records for unlabeled routers in this
case. If you use asDefault, declare the same defaults to dnsweaver via
the source-level setting:
With this set, an unlabeled router fans out one extraction per default
entrypoint — exactly mirroring what Traefik itself does — and per-instance
ENTRYPOINTS filters then claim each pair as usual. Routers with explicit
entrypoints labels are unaffected.
Unset (default) preserves pre-1.4.2 wildcard behavior.
Troubleshooting¶
Labels Not Detected¶
Verify labels are on the service (not deploy):
Socket Connection Failed¶
Ensure dnsweaver is on a manager node:
Records Not Creating¶
Check dnsweaver logs: