# Dynamic DNS (DDNS) in VyOS

> Dynamic DNS in VyOS - automatically update DNS records when the IP address changes using DynDNS, No-IP, Cloudflare, and other providers

Source: https://opennix.org/en/docs/vyos/services/vyos-ddns/



Dynamic DNS (DDNS) is a service that automatically updates DNS records when an IP address changes. VyOS includes a built-in DDNS client that can update records at various dynamic DNS providers, which is critical for remote access to devices with dynamic IP addresses.

## Overview

Dynamic DNS solves the problem of accessing devices with changing IP addresses:

- **Home/small offices**: Access to the network through a dynamic IP from the ISP
- **Branch offices**: Remote access without static IP addresses
- **VPN connections**: Stable endpoints for VPN tunnels
- **Remote management**: A persistent DNS name for SSH/HTTPS access
- **Self-hosted services**: Access to web servers, cameras, and NAS on a home IP

### How DDNS works

1. VyOS monitors the IP address on the specified interface
2. When the IP changes, the change is detected
3. VyOS sends an update to the DDNS provider via an API/protocol
4. The provider updates the DNS record
5. The DNS name now points to the new IP address

### Supported protocols

VyOS supports a wide range of DDNS providers and protocols:

- **Standard protocols**: DynDNS2, Cloudflare, Namecheap, No-IP, Google Domains
- **Custom protocols**: RFC2136 (DNS UPDATE), custom HTTP/HTTPS APIs
- **Multiple providers**: Several providers and records at the same time

## Basic configuration

### DynDNS2 protocol (No-IP, DynDNS)

```bash
# DDNS configuration for No-IP
set service dns dynamic name noip service noip
set service dns dynamic name noip host-name myrouter.ddns.net
set service dns dynamic name noip username myusername
set service dns dynamic name noip password mypassword
set service dns dynamic name noip interface eth0

commit
save
```

### Cloudflare DDNS

```bash
# Cloudflare uses email + API key
set service dns dynamic name cloudflare service cloudflare
set service dns dynamic name cloudflare host-name myrouter.example.com
set service dns dynamic name cloudflare username email@example.com
set service dns dynamic name cloudflare password cloudflare-api-key
set service dns dynamic name cloudflare zone example.com
set service dns dynamic name cloudflare interface eth0

commit
save
```

### Google Domains DDNS

```bash
# Google Domains DDNS
set service dns dynamic name google service googledomains
set service dns dynamic name google host-name myrouter.example.com
set service dns dynamic name google username generated-username
set service dns dynamic name google password generated-password
set service dns dynamic name google interface eth0

commit
save
```

### Namecheap DDNS

```bash
# Namecheap DDNS
set service dns dynamic name namecheap service namecheap
set service dns dynamic name namecheap host-name myrouter
set service dns dynamic name namecheap username example.com
set service dns dynamic name namecheap password ddns-password
set service dns dynamic name namecheap interface eth0

commit
save
```

## Advanced configuration

### RFC2136 (DNS UPDATE)

RFC2136 lets you update DNS records directly on a server that supports dynamic updates (for example, BIND).

```bash
# RFC2136 setup
set service dns dynamic name rfc2136 service rfc2136
set service dns dynamic name rfc2136 server ns1.example.com
set service dns dynamic name rfc2136 zone example.com
set service dns dynamic name rfc2136 host-name router.example.com
set service dns dynamic name rfc2136 key /config/auth/ddns-key.txt
set service dns dynamic name rfc2136 ttl 60
set service dns dynamic name rfc2136 interface eth0

commit
save
```

The key file `/config/auth/ddns-key.txt` (TSIG key):

```
key "ddns-key" {
    algorithm hmac-sha256;
    secret "base64-encoded-secret-here==";
};
```

### Multiple DDNS records

```bash
# First provider (No-IP for primary access)
set service dns dynamic name primary service noip
set service dns dynamic name primary host-name myrouter.ddns.net
set service dns dynamic name primary username user1
set service dns dynamic name primary password pass1
set service dns dynamic name primary interface eth0

# Second provider (DuckDNS as a backup)
set service dns dynamic name backup service duckdns
set service dns dynamic name backup host-name myrouter.duckdns.org
set service dns dynamic name backup password duckdns-token
set service dns dynamic name backup interface eth0

# Third provider (Cloudflare for the corporate domain)
set service dns dynamic name corporate service cloudflare
set service dns dynamic name corporate host-name vpn.company.com
set service dns dynamic name corporate username admin@company.com
set service dns dynamic name corporate password cloudflare-api-key
set service dns dynamic name corporate zone company.com
set service dns dynamic name corporate interface eth0

commit
save
```

### Monitoring through the provider web interface

```bash
# Configuration with an IP-check URL (to determine the external IP)
set service dns dynamic name myservice service custom
set service dns dynamic name myservice host-name myhost.example.com
set service dns dynamic name myservice username myuser
set service dns dynamic name myservice password mypass
set service dns dynamic name myservice interface eth0
set service dns dynamic name myservice protocol dyndns2
set service dns dynamic name myservice server updates.example.com

commit
save
```

### Using a web request to obtain the IP

If the router is behind NAT and you need to determine the external IP:

```bash
# DDNS setup with a web-based IP detection method
set service dns dynamic name myservice service noip
set service dns dynamic name myservice host-name myrouter.ddns.net
set service dns dynamic name myservice username myuser
set service dns dynamic name myservice password mypass
set service dns dynamic name myservice interface eth0
set service dns dynamic name myservice ip-check web
set service dns dynamic name myservice web-url https://api.ipify.org

commit
save
```

### Update interval

```bash
# Set the check interval (in seconds, default 300)
set service dns dynamic name myservice service noip
set service dns dynamic name myservice host-name myrouter.ddns.net
set service dns dynamic name myservice username myuser
set service dns dynamic name myservice password mypass
set service dns dynamic name myservice interface eth0
set service dns dynamic name myservice timeout 600  # Check every 10 minutes

commit
save
```

### IPv6 DDNS

```bash
# DDNS for an IPv6 address
set service dns dynamic name ipv6ddns service cloudflare
set service dns dynamic name ipv6ddns host-name myrouter-v6.example.com
set service dns dynamic name ipv6ddns username email@example.com
set service dns dynamic name ipv6ddns password cloudflare-api-key
set service dns dynamic name ipv6ddns zone example.com
set service dns dynamic name ipv6ddns interface eth0
set service dns dynamic name ipv6ddns ip-version ipv6

commit
save
```

## Configuration examples

### Example 1: Home router with No-IP

```bash
# Simple configuration for home access
set service dns dynamic name home service noip
set service dns dynamic name home host-name myhome.ddns.net
set service dns dynamic name home username homeuser
set service dns dynamic name home password HomePass123!
set service dns dynamic name home interface eth0

commit
save
```

Verification:

```bash
show dns dynamic status
```

### Example 2: VPN endpoint with Cloudflare

```bash
# DDNS for a VPN endpoint on a corporate domain
set service dns dynamic name vpn-endpoint service cloudflare
set service dns dynamic name vpn-endpoint host-name vpn.company.com
set service dns dynamic name vpn-endpoint username admin@company.com
set service dns dynamic name vpn-endpoint password cf_api_key_here
set service dns dynamic name vpn-endpoint zone company.com
set service dns dynamic name vpn-endpoint interface eth0

# The WireGuard VPN endpoint uses this DNS name
set interfaces wireguard wg0 address 10.10.0.1/24
set interfaces wireguard wg0 listen-port 51820
set interfaces wireguard wg0 peer client1 allowed-ips 10.10.0.2/32
set interfaces wireguard wg0 peer client1 public-key 'peer-public-key-here'

commit
save
```

WireGuard client configuration:

```ini
[Interface]
PrivateKey = client-private-key
Address = 10.10.0.2/24

[Peer]
PublicKey = server-public-key
Endpoint = vpn.company.com:51820
AllowedIPs = 0.0.0.0/0
PersistentKeepalive = 25
```

### Example 3: Multiple branch offices

```bash
# Head office
set service dns dynamic name hq service cloudflare
set service dns dynamic name hq host-name hq-router.company.com
set service dns dynamic name hq username admin@company.com
set service dns dynamic name hq password cloudflare-key
set service dns dynamic name hq zone company.com
set service dns dynamic name hq interface eth0

# Backup record on a free service
set service dns dynamic name hq-backup service duckdns
set service dns dynamic name hq-backup host-name company-hq.duckdns.org
set service dns dynamic name hq-backup password duckdns-token
set service dns dynamic name hq-backup interface eth0

commit
save
```

### Example 4: Self-hosted services (web, mail)

```bash
# DDNS for a web server
set service dns dynamic name webserver service cloudflare
set service dns dynamic name webserver host-name www.example.com
set service dns dynamic name webserver username admin@example.com
set service dns dynamic name webserver password cloudflare-api-key
set service dns dynamic name webserver zone example.com
set service dns dynamic name webserver interface eth0

# Port forwarding for the web server
set nat destination rule 100 description 'Port forward HTTP'
set nat destination rule 100 inbound-interface name eth0
set nat destination rule 100 destination port 80
set nat destination rule 100 protocol tcp
set nat destination rule 100 translation address 192.168.1.10

set nat destination rule 110 description 'Port forward HTTPS'
set nat destination rule 110 inbound-interface name eth0
set nat destination rule 110 destination port 443
set nat destination rule 110 protocol tcp
set nat destination rule 110 translation address 192.168.1.10

commit
save
```

### Example 5: RFC2136 with your own DNS server

```bash
# BIND DNS server with dynamic update support
set service dns dynamic name internal service rfc2136
set service dns dynamic name internal server 192.168.1.53
set service dns dynamic name internal zone internal.company.com
set service dns dynamic name internal host-name router.internal.company.com
set service dns dynamic name internal key /config/auth/ddns-update-key.txt
set service dns dynamic name internal ttl 300
set service dns dynamic name internal interface eth1

commit
save
```

On the BIND server (`/etc/bind/named.conf.local`):

```
key "ddns-key" {
    algorithm hmac-sha256;
    secret "your-base64-secret==";
};

zone "internal.company.com" {
    type master;
    file "/var/lib/bind/db.internal.company.com";
    allow-update { key ddns-key; };
};
```

### Example 6: Dual-stack (IPv4 + IPv6)

```bash
# IPv4 DDNS
set service dns dynamic name dual-v4 service cloudflare
set service dns dynamic name dual-v4 host-name router.example.com
set service dns dynamic name dual-v4 username admin@example.com
set service dns dynamic name dual-v4 password cloudflare-key
set service dns dynamic name dual-v4 zone example.com
set service dns dynamic name dual-v4 interface eth0
set service dns dynamic name dual-v4 ip-version ipv4

# IPv6 DDNS (AAAA record)
set service dns dynamic name dual-v6 service cloudflare
set service dns dynamic name dual-v6 host-name router.example.com
set service dns dynamic name dual-v6 username admin@example.com
set service dns dynamic name dual-v6 password cloudflare-key
set service dns dynamic name dual-v6 zone example.com
set service dns dynamic name dual-v6 interface eth0
set service dns dynamic name dual-v6 ip-version ipv6

commit
save
```

## Monitoring and diagnostics

### Viewing DDNS status

```bash
# Show the status of all DDNS services
show dns dynamic status

# The output includes:
# - Service name
# - Hostname
# - Current IP
# - Last update
# - Status (good, nochange, error)
```

Example output:

```
service: cloudflare
hostname: myrouter.example.com
address: 203.0.113.45
status: good
last update: Mon Jan 15 10:30:45 2024
```

### Forcing an update

```bash
# Force an update for a specific service
update dns dynamic interface eth0

# This makes VyOS immediately send an update to all configured providers
```

### Viewing DDNS logs

```bash
# DDNS logs in syslog
show log | match ddclient

# The last 50 entries
show log tail 50 | match ddclient

# Real-time monitoring
monitor log | match ddclient
```

### Checking the current IP

```bash
# Current IP on the interface
show interfaces ethernet eth0 | grep "inet "

# External IP (if behind NAT)
curl ifconfig.me
curl api.ipify.org
```

### Checking DNS resolution

```bash
# Verify that the DNS record has been updated
nslookup myrouter.ddns.net

# Or using dig
dig myrouter.ddns.net +short

# Check through a specific DNS server
dig @8.8.8.8 myrouter.ddns.net
```

### Debugging issues

```bash
# Check the configuration
show configuration service dns dynamic

# Detailed logs (enable debug)
set system syslog file ddns.log facility daemon level debug
commit

# View the logs
tail -f /var/log/ddns.log

# Disable debug after troubleshooting
delete system syslog file ddns.log facility daemon level debug
commit
```

### Monitoring with a script

```bash
#!/bin/bash
# /config/scripts/monitor-ddns.sh

HOSTNAME="myrouter.ddns.net"
EXPECTED_IP=$(curl -s ifconfig.me)
RESOLVED_IP=$(dig +short $HOSTNAME | head -n1)

if [ "$EXPECTED_IP" != "$RESOLVED_IP" ]; then
    echo "DDNS mismatch: Expected $EXPECTED_IP, Got $RESOLVED_IP"
    logger -t ddns-monitor "DDNS mismatch detected"
    # Send an alert
    echo "DDNS issue on $(hostname)" | mail -s "DDNS Alert" admin@example.com
else
    logger -t ddns-monitor "DDNS check OK: $RESOLVED_IP"
fi
```

Add it to the Task Scheduler:

```bash
set system task-scheduler task ddns-monitor interval '*/15 * * * *'
set system task-scheduler task ddns-monitor executable path '/config/scripts/monitor-ddns.sh'
commit
save
```

## Troubleshooting

### Problem: DDNS is not updating

**Diagnostics**:

```bash
# 1. Check the configuration
show configuration service dns dynamic

# 2. Check the status
show dns dynamic status

# 3. Check the logs
show log | match ddclient

# 4. Check the interface
show interfaces ethernet eth0

# 5. Check connectivity to the provider
ping updates.no-ip.com
```

**Solution**:

```bash
# Verify credentials
set service dns dynamic name myservice username correct-username
set service dns dynamic name myservice password correct-password

# Force an update
update dns dynamic interface eth0

# Restart the service (if necessary)
sudo systemctl restart ddclient

commit
save
```

### Problem: The IP is detected incorrectly (behind NAT)

**Cause**: VyOS uses the interface IP instead of the external IP.

**Solution**:

```bash
# Use the web method to determine the IP
set service dns dynamic name myservice ip-check web
set service dns dynamic name myservice web-url https://api.ipify.org

commit
save
```

Alternative URLs for IP detection:
- `https://api.ipify.org`
- `https://ifconfig.me/ip`
- `https://icanhazip.com`
- `https://checkip.amazonaws.com`

### Problem: Updates are too frequent

**Cause**: The provider may block you for abuse (too many requests).

**Solution**:

```bash
# Increase the check interval
set service dns dynamic name myservice timeout 3600  # 1 hour

commit
save
```

### Problem: Authentication fails

**Diagnostics**:

```bash
# Check the logs
show log | match "authentication failed"
show log | match ddclient
```

**Solution**:

```bash
# Verify the credentials in the provider's web interface

# Recreate the configuration with the correct credentials
delete service dns dynamic name myservice
set service dns dynamic name myservice service noip
set service dns dynamic name myservice host-name myrouter.ddns.net
set service dns dynamic name myservice username correct-username
set service dns dynamic name myservice password correct-password
set service dns dynamic name myservice interface eth0

commit
save

# Force an update
update dns dynamic interface eth0
```

### Problem: Hostname is not updating on Cloudflare

**Cause**: Cloudflare requires the zone to be specified.

**Solution**:

```bash
# Make sure the zone is specified
set service dns dynamic name cloudflare zone example.com

# Make sure you use the correct API key (not an API token)
# Cloudflare requires the Global API Key, not a Scoped API Token

commit
save
```

### Problem: RFC2136 updates do not work

**Diagnostics**:

```bash
# Check DNS server availability
ping 192.168.1.53

# Check UDP port 53
nc -vu 192.168.1.53 53

# Check the key
cat /config/auth/ddns-key.txt
```

**Solution**:

```bash
# Make sure the key is configured correctly on both sides

# On VyOS, the key file must contain:
# key "ddns-key" {
#     algorithm hmac-sha256;
#     secret "base64-secret==";
# };

# On BIND, the zone must allow updates:
# allow-update { key ddns-key; };

# Test the update manually from the client
nsupdate -k /config/auth/ddns-key.txt
> server 192.168.1.53
> zone example.com
> update add test.example.com 300 A 192.168.1.100
> send
> quit
```

## Popular DDNS providers

### Free services

| Provider | Protocol | Notes |
|-----------|----------|-------------|
| No-IP | noip | 3 hostnames free, requires confirmation every 30 days |
| DuckDNS | duckdns | Free, no limits, simple |
| FreeDNS | freedns | Many domains, free |
| Dynu | dynu | 4 hostnames free |

### Paid/Freemium services

| Provider | Protocol | Notes |
|-----------|----------|-------------|
| Cloudflare | cloudflare | Free plan + DNS management |
| Google Domains | googledomains | Requires a domain in Google Domains |
| Namecheap | namecheap | DDNS for Namecheap domains |
| DynDNS | dyndns2 | Paid service, reliable |

### Configuration for popular providers

**DuckDNS**:

```bash
set service dns dynamic name duckdns service duckdns
set service dns dynamic name duckdns host-name mysubdomain.duckdns.org
set service dns dynamic name duckdns password token-from-duckdns
set service dns dynamic name duckdns interface eth0
```

**FreeDNS**:

```bash
set service dns dynamic name freedns service freedns
set service dns dynamic name freedns host-name myhost.freeddns.org
set service dns dynamic name freedns password update-token
set service dns dynamic name freedns interface eth0
```

**Dynu**:

```bash
set service dns dynamic name dynu service dynu
set service dns dynamic name dynu host-name myhost.dynu.net
set service dns dynamic name dynu username dynu-username
set service dns dynamic name dynu password dynu-password
set service dns dynamic name dynu interface eth0
```

## Best practices

### 1. Use reliable providers

Choose proven providers with a good SLA:

```bash
# For production - Cloudflare or Google Domains
set service dns dynamic name primary service cloudflare

# For home/lab - DuckDNS or No-IP
set service dns dynamic name home service duckdns
```

### 2. DDNS redundancy

Configure multiple providers for redundancy:

```bash
# Primary
set service dns dynamic name primary service cloudflare
set service dns dynamic name primary host-name router.example.com

# Backup
set service dns dynamic name backup service duckdns
set service dns dynamic name backup host-name router-backup.duckdns.org
```

### 3. Secure password storage

Do not use simple passwords:

```bash
# Bad
set service dns dynamic name myservice password 123456

# Good - use complex tokens/keys
set service dns dynamic name myservice password 'aB3$kL9@mN5&pQ2#'
```

### 4. DDNS monitoring

Set up monitoring for critical hostnames:

```bash
# Task Scheduler for checks
set system task-scheduler task ddns-check interval '*/15 * * * *'
set system task-scheduler task ddns-check executable path '/config/scripts/check-ddns.sh'
```

### 5. Logging

Enable DDNS logging for auditing:

```bash
set system syslog file ddns.log facility daemon level info
```

### 6. A proper update interval

Do not set updates too frequently:

```bash
# 5-15 minutes is recommended for dynamic IPs
set service dns dynamic name myservice timeout 600  # 10 minutes
```

### 7. TTL for DNS records

Use a low TTL for DDNS records:

```bash
# For RFC2136
set service dns dynamic name internal ttl 60  # 1 minute
```

### 8. Documentation

Document the DDNS hostnames you use:

```bash
# Use descriptive names in the configuration
set service dns dynamic name vpn-endpoint ...
set service dns dynamic name web-server ...
set service dns dynamic name remote-access ...
```

### 9. Test after setup

Always test DDNS after configuring it:

```bash
# Force an update
update dns dynamic interface eth0

# Check the status
show dns dynamic status

# Check DNS
dig myrouter.ddns.net +short
```

### 10. Automation for multiple devices

Use Ansible to manage DDNS across many routers:

```yaml
- name: Configure DDNS
  vyos.vyos.vyos_config:
    lines:
      - set service dns dynamic name {{ ddns_name }} service {{ ddns_provider }}
      - set service dns dynamic name {{ ddns_name }} host-name {{ ddns_hostname }}
      - set service dns dynamic name {{ ddns_name }} username {{ ddns_username }}
      - set service dns dynamic name {{ ddns_name }} password {{ ddns_password }}
      - set service dns dynamic name {{ ddns_name }} interface {{ ddns_interface }}
```

## Useful commands

```bash
# Show DDNS status
show dns dynamic status

# Force an update
update dns dynamic interface eth0

# DDNS configuration
show configuration service dns dynamic

# DDNS logs
show log | match ddclient
show log tail 50 | match ddclient

# Real-time log monitoring
monitor log | match ddclient

# Check DNS resolution
nslookup myrouter.ddns.net
dig myrouter.ddns.net +short

# Check the external IP
curl ifconfig.me
curl api.ipify.org

# Restart the DDNS client (if needed)
sudo systemctl restart ddclient

# Service status
sudo systemctl status ddclient

# View the ddclient configuration
sudo cat /etc/ddclient/ddclient.conf
```

## Integration with other features

### DDNS + VPN

```bash
# DDNS for a WireGuard endpoint
set service dns dynamic name wg-endpoint service cloudflare
set service dns dynamic name wg-endpoint host-name wg.example.com
set service dns dynamic name wg-endpoint username admin@example.com
set service dns dynamic name wg-endpoint password cloudflare-key
set service dns dynamic name wg-endpoint zone example.com
set service dns dynamic name wg-endpoint interface eth0

# WireGuard configuration
set interfaces wireguard wg0 address 10.10.0.1/24
set interfaces wireguard wg0 listen-port 51820
```

Clients use `wg.example.com:51820` as the endpoint.

### DDNS + Port Forwarding

```bash
# DDNS for self-hosted services
set service dns dynamic name web service cloudflare
set service dns dynamic name web host-name web.example.com

# NAT for HTTP/HTTPS
set nat destination rule 100 inbound-interface name eth0
set nat destination rule 100 destination port 80
set nat destination rule 100 protocol tcp
set nat destination rule 100 translation address 192.168.1.10

set nat destination rule 110 inbound-interface name eth0
set nat destination rule 110 destination port 443
set nat destination rule 110 protocol tcp
set nat destination rule 110 translation address 192.168.1.10
```

### DDNS + Let's Encrypt

```bash
# DDNS updates the DNS record for the Let's Encrypt DNS-01 challenge
set service dns dynamic name acme service cloudflare
set service dns dynamic name acme host-name router.example.com

# Let's Encrypt can verify domain ownership through DNS
```

## Conclusion

Dynamic DNS in VyOS is a reliable solution for keeping devices with dynamic IP addresses reachable. The built-in DDNS client supports all the major providers and protocols, providing both flexibility and reliability.

Key benefits of DDNS in VyOS:
- Support for multiple providers at the same time
- Automatic updates when the IP changes
- Integration with VPN, web services, and remote access
- RFC2136 for integration with your own DNS servers
- Dual-stack support (IPv4 + IPv6)

Recommendations for production:
- Use reliable providers (Cloudflare, Google Domains)
- Configure backup DDNS records
- Monitor the status of DDNS updates
- Use a proper update interval (5-15 minutes)
- Document all DDNS hostnames
- Test after every configuration change

Dynamic DNS in VyOS provides stable remote access to your network infrastructure without the need for static IP addresses, which is critical for small offices, branch offices, and home networks.

