Automatically synchronize domain names from your Caddyfile to DNS providers. Supports Cloudflare, BIND9, and Pi-hole.
caddy-dns-sync parses your Caddyfile, extracts all domain names, and automatically creates/updates/deletes DNS A records across multiple DNS providers. It runs as a systemd service and keeps your DNS records in sync with your Caddy configuration.
- Multi-Provider Support: Cloudflare, BIND9 (RFC 2136), and Pi-hole
- Automatic IP Detection: Gets IP from network interfaces (eth0, ens3, etc.)
- Environment Variable Substitution: Secure credential management with
$(ENV_VAR)syntax - State Management: Tracks records in
/etc/caddy-dns-sync/dns.state - Diff-Based Sync: Creates, updates, and deletes records as needed
- Systemd Integration: Runs automatically via timer
- Multiple Zones: Support for multiple domains and DNS zones per provider
| Provider | Method | Documentation |
|---|---|---|
| Cloudflare | REST API | docs/cloudflare.md |
| BIND9 | RFC 2136 (Dynamic DNS) | docs/bind9.md |
| Pi-hole | Web API | docs/pihole.md |
Download the latest release for your architecture:
# AMD64
wget https://github.com/RuTHlessBEat200/caddy-dns-sync/releases/latest/download/caddy-dns-sync-linux-amd64
chmod +x caddy-dns-sync-linux-amd64
sudo mv caddy-dns-sync-linux-amd64 /usr/local/bin/caddy-dns-sync
# ARM64
wget https://github.com/RuTHlessBEat200/caddy-dns-sync/releases/latest/download/caddy-dns-sync-linux-arm64
chmod +x caddy-dns-sync-linux-arm64
sudo mv caddy-dns-sync-linux-arm64 /usr/local/bin/caddy-dns-syncInitialize for all providers:
sudo caddy-dns-sync --initOr for specific providers:
# Cloudflare only
sudo caddy-dns-sync --init --cloudflare
# Multiple providers
sudo caddy-dns-sync --init --cloudflare --piholeThis creates:
/etc/caddy-dns-sync/provider.conf- Provider configuration/etc/systemd/system/caddy-dns-sync.service- Systemd service/etc/systemd/system/caddy-dns-sync.timer- Systemd timer (runs every 5 minutes)
Edit /etc/caddy-dns-sync/provider.conf:
{
"cloudflare": {
"example.com": {
"ip_interface": "eth0",
"apitoken": "$(CLOUDFLARE_API_TOKEN)",
"zoneid": "$(CLOUDFLARE_ZONE_ID)"
}
},
"bind9": {
"internal.example.com": {
"ip_interface": "eth0",
"server": "127.0.0.1",
"port": 53,
"key_name": "ddns-key",
"key_secret": "$(BIND9_KEY_SECRET)",
"algorithm": "hmac-sha256"
}
},
"pihole": {
"pihole-1": {
"ip_interface": "eth0",
"server": "http://192.168.1.2",
"apitoken": "$(PIHOLE_API_TOKEN)"
}
}
}Set environment variables or use direct values in the config.
sudo systemctl daemon-reload
sudo systemctl enable caddy-dns-sync.timer
sudo systemctl start caddy-dns-sync.timer# Check timer status
sudo systemctl status caddy-dns-sync.timer
# View logs
sudo journalctl -u caddy-dns-sync.service -f
# Check state
sudo cat /etc/caddy-dns-sync/dns.stateLocation: /etc/caddy-dns-sync/provider.conf
The configuration uses JSON format with support for environment variable substitution.
Use $(ENV_VAR_NAME) syntax to reference environment variables:
{
"cloudflare": {
"example.com": {
"ip_interface": "eth0",
"apitoken": "$(CLOUDFLARE_API_TOKEN)",
"zoneid": "$(CLOUDFLARE_ZONE_ID)"
}
}
}Variables are expanded at runtime from the environment.
The ip_interface field specifies which network interface to get the IP address from:
# List available interfaces
ip addr show
# Common interface names
# eth0, ens3, ens5 - Ethernet
# wlan0 - WiFi
# enp0s3 - Predictable network interface namesLocation: /etc/caddy-dns-sync/dns.state
Tracks all managed DNS records in JSON format:
{
"records": {
"cloudflare:example.com:subdomain.example.com": {
"domain": "subdomain.example.com",
"record_id": "cloudflare-record-id",
"ip_address": "203.0.113.10",
"provider": "cloudflare",
"zone": "example.com",
"created_at": "2026-03-29T10:00:00Z",
"updated_at": "2026-03-29T10:00:00Z"
}
},
"updated_at": "2026-03-29T10:00:00Z"
}Usage: caddy-dns-sync [options]
Options:
-init
Initialize directories and systemd service
-cloudflare
Initialize Cloudflare provider (use with -init)
-bind9
Initialize BIND9 provider (use with -init)
-pihole
Initialize Pi-hole provider (use with -init)
-version
Show version information
-caddyfile string
Path to Caddyfile (default "/etc/caddy/Caddyfile")
-config string
Path to provider configuration file (default "/etc/caddy-dns-sync/provider.conf")
-state string
Path to DNS state file (default "/etc/caddy-dns-sync/dns.state")
- Parse Caddyfile: Extracts all domain names from server blocks
- Load Configuration: Reads provider settings from
provider.conf - Get IP Addresses: Retrieves IPv4 from configured network interfaces
- Load State: Reads current DNS records from
dns.state - Sync Records: For each provider:
- Creates DNS records for new domains
- Updates records if IP changed
- Deletes records for removed domains
- Save State: Updates state file with current records
Detailed configuration guides for each provider:
- Cloudflare DNS - API setup, zone configuration, troubleshooting
- BIND9 DNS - TSIG keys, RFC 2136 dynamic updates, zone setup
- Pi-hole DNS - API tokens, custom DNS records, multiple instances
# Config contains sensitive credentials
sudo chmod 600 /etc/caddy-dns-sync/provider.conf
# State file contains record IDs
sudo chmod 644 /etc/caddy-dns-sync/dns.state
# Directory
sudo chmod 755 /etc/caddy-dns-syncThe systemd service includes security hardening:
- Runs as
caddyuser (non-root) NoNewPrivileges=truePrivateTmp=trueProtectSystem=strictProtectHome=true- Read-write access only to
/etc/caddy-dns-sync - Read-only access to
/etc/caddy
Best practices:
- Use environment variables for sensitive data
- Never commit credentials to version control
- Use provider-specific tokens with minimal permissions
- Regularly rotate API tokens and keys
- Restrict DNS provider access to specific zones/domains
- Go 1.21 or later
git clone https://github.com/RuTHlessBEat200/caddy-dns-sync.git
cd caddy-dns-sync
go build -o caddy-dns-sync# Build for current platform
make build
# Build for all platforms
make build-all
# Install locally
make install
# Initialize system
make init# Check service status
sudo systemctl status caddy-dns-sync.service
# Check timer
sudo systemctl status caddy-dns-sync.timer
# View recent logs
sudo journalctl -u caddy-dns-sync.service -n 100# Ensure caddy user can read Caddyfile
sudo chmod 644 /etc/caddy/Caddyfile
# Check config directory permissions
ls -la /etc/caddy-dns-sync/# Test Caddyfile parsing
sudo -u caddy caddy-dns-sync -caddyfile /etc/caddy/Caddyfile# List network interfaces
ip addr show
# Update ip_interface in provider.conf to match an existing interfaceSee provider documentation:
# Enable timer (start on boot)
sudo systemctl enable caddy-dns-sync.timer
# Start timer
sudo systemctl start caddy-dns-sync.timer
# Stop timer
sudo systemctl stop caddy-dns-sync.timer
# Run service immediately
sudo systemctl start caddy-dns-sync.service
# View timer schedule
systemctl list-timers | grep caddy-dns-sync
# Change interval (edit timer file)
sudo nano /etc/systemd/system/caddy-dns-sync.timer
sudo systemctl daemon-reload
sudo systemctl restart caddy-dns-sync.timerContributions are welcome! Please:
- Fork the repository
- Create a feature branch
- Make your changes
- Submit a pull request
MIT License - see LICENSE file for details
- GitHub Issues: Report bugs and request features
- Documentation: Check provider-specific docs in
docs/directory - Logs: Review systemd journal for detailed information
- Multi-provider support (Cloudflare, BIND9, Pi-hole)
- Environment variable substitution in config
- Network interface IP detection
- State-based record tracking
- Systemd integration with timer
- Comprehensive documentation