Relayd
relayd is a lightweight, "set and forget" external DNS synchronization agent for Docker. It seamlessly updates DNS records (A, AAAA, and TXT ownership records) across various providers based on your Docker container labels.
Features
- Docker Native: Automatically extracts hostnames from
relayd.hosts and Traefik .rule labels.
- Dual-Stack Support: Synchronizes both
A (IPv4) and AAAA (IPv6) records simultaneously.
- Safe Ownership: Uses specific
TXT records to track ownership, guaranteeing it will never overwrite or delete domains it doesn't own.
- Multi-Provider: Sync your public domains to Cloudflare, while simultaneously syncing your internal/local domains to Pi-hole, UniFi, or PowerDNS.
Usage
Simply run the container and mount the docker socket:
Docker Compose
In standard Docker, relayd automatically discovers the primary local IP using the OS routing table.
Use network_mode: host to give relayd direct access to the host's network interfaces so it can discover the host's actual LAN IP:
services:
relayd:
image: ghcr.io/mizuchilabs/relayd:latest
network_mode: host
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
environment:
- RELAYD_PROVIDER_CLOUDFLARE_TYPE=cloudflare
- RELAYD_PROVIDER_CLOUDFLARE_TOKEN=your-api-token
- RELAYD_PROVIDER_CLOUDFLARE_ZONES=example.com
Docker Swarm
relayd has native Docker Swarm support. Because Swarm's ingress routing mesh automatically routes traffic received on any node to the correct container, you only need to publish one IP address for your services.
You should run exactly one instance of relayd on a manager node. It will automatically detect all swarm services with the relayd.enable=true label across the entire cluster and publish its own node's IP.
services:
relayd:
image: ghcr.io/mizuchilabs/relayd:latest
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
deploy:
replicas: 1
placement:
constraints:
- node.role == manager
environment:
- RELAYD_PROVIDER_CLOUDFLARE_TYPE=cloudflare
- RELAYD_PROVIDER_CLOUDFLARE_TOKEN=your-api-token
- RELAYD_PROVIDER_CLOUDFLARE_ZONES=example.com
Note: Without host networking or manual overrides, relayd will discover its internal container IP. If you are using local-scoped providers and need the actual Host IP, you can configure the RELAYD_LOCAL_OVERRIDE_IPV4 and RELAYD_LOCAL_OVERRIDE_IPV6 environment variables manually. For public-scoped providers (the default), relayd uses external services to resolve the public IP automatically, so network mode does not matter.
Adding domains to your containers
You can configure DNS targets by adding the relayd.enable and relayd.hosts label to any container:
services:
whoami:
image: traefik/whoami
labels:
- relayd.enable=true
- relayd.hosts=whoami.example.com,test.example.com
# If you are using Traefik, you can also add the following and remove the `relayd.hosts` label:
- traefik.enable=true
- traefik.http.routers.whoami.rule=Host(`whoami.example.com`)
# To restrict which DNS providers or scopes a container uses use the `relayd.providers` label:
- relayd.providers=local # or cloudflare, pihole, unifi, powerdns
Architecture & Edge Cases
To prevent disaster, relayd uses a Safe Ownership model. Whenever it creates an A or AAAA record, it creates a companion TXT record (e.g., relayd.yoursubdomain="managed-by=relayd-yourhostname"). relayd will never delete or modify a DNS record unless it sees its exact matching TXT record.
Multiple Instances (Split-Brain)
If you run relayd on two entirely separate Docker hosts (Host A and Host B) that point to the same DNS Zone, they will natively ignore each other's records if their hostnames don't match and force mode is enabled.
Otherwise if force mode is disabled and these instances point to the same DNS Zone, they will constantly overwrite each other's records! Be careful when using it with Pi-hole since it doesn't support TXT records.
Using with tether & tetherd (Centralized Traefik)
If you are using relayd in combination with tether and tetherd to proxy Traefik configurations to a central hub, you may encounter a DNS routing issue.
By default, an agent running relayd will publish its own local IP for its containers. However, since all traffic must flow through your central Traefik instance, DNS queries will point to the agent instead of the central router, causing resolution to fail.
To fix this, you need to explicitly tell relayd on the agent machines to broadcast the central Traefik server's IP instead of their own. You can do this using the IP override environment variables:
services:
relayd:
image: ghcr.io/mizuchilabs/relayd:latest
environment:
# Override the detected IP with the IP of your central Traefik instance
- RELAYD_LOCAL_OVERRIDE_IPV4=192.168.1.10
Configuration
Relayd can be configured entirely via environment variables.
Global Options
| Variable |
Default |
Description |
RELAYD_INTERVAL |
5m |
Background sync interval (e.g. 5m, 1h). |
RELAYD_INSTANCE |
hostname |
Instance name (e.g. my-nas, test-server). |
RELAYD_IP_FAMILY |
ipv4 |
IP family to synchronize (ipv4, ipv6, dual). |
RELAYD_LOCAL_OVERRIDE_IPV4 |
auto |
Hardcode the local IPv4 address instead of auto-discovering. |
RELAYD_LOCAL_OVERRIDE_IPV6 |
auto |
Hardcode the local IPv6 address instead of auto-discovering. |
RELAYD_PUBLIC_OVERRIDE_IPV4 |
auto |
Hardcode the public IPv4 address instead of auto-discovering. |
RELAYD_PUBLIC_OVERRIDE_IPV6 |
auto |
Hardcode the public IPv6 address instead of auto-discovering. |
Configuring Providers
Providers are automatically discovered by scanning your environment variables for any variable ending in _TYPE with the RELAYD_PROVIDER_ prefix. You can name your providers anything you like (e.g., CF, LOCAL, MYDNS).
For detailed configuration examples per provider, see the docs/providers directory:
License
Apache 2.0 License - see LICENSE for details
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.