El DNS dinámico (DDNS) mantiene un nombre, por ejemplo casa.example.com, apuntando a un equipo cuya IP pública cambia, como un servidor en casa o en una oficina con conexión de IP dinámica. Un cliente en ese equipo detecta la IP actual y actualiza el registro DNS cuando cambia. En este tutorial configurarás ddclient en Ubuntu 24.04 para actualizar un registro en Cloudflare y, como alternativa, verás cómo hacerlo contra tu propio servidor BIND9 con nsupdate y una clave TSIG.
Requisitos previos
Para seguir esta guía necesitas:
- El equipo con IP dinámica ejecutando Ubuntu 24.04 LTS, con un usuario no root con privilegios
sudo. - Un dominio. Para el método principal, el dominio debe usar los servidores DNS de Cloudflare (el plan gratuito es suficiente).
- Para el método alternativo, un servidor BIND9 autoritativo para tu dominio, por ejemplo en un VPS de CubePath, al que tengas acceso con
sudo.
En los ejemplos se usa el dominio example.com y el nombre casa.example.com. Sustitúyelos por los tuyos.
Notalos servidores de CubePath tienen IP pública fija, así que no necesitan DDNS. Esta guía es para el otro extremo: el equipo de casa u oficina que quieres alcanzar por nombre.
Paso 1: Crear el registro y un token de API en Cloudflare
ddclient actualiza un registro existente, así que créalo primero. En el panel de Cloudflare, abre tu dominio, ve a DNS > Records y añade un registro:
- Type:
A - Name:
casa - IPv4 address: cualquier IP provisional, por ejemplo
192.0.2.1 - Proxy status: desactivado (DNS only), para que el nombre resuelva a tu IP real
Después crea un token con los permisos mínimos. Ve a My Profile > API Tokens > Create Token, elige Create Custom Token y configura:
- Permissions:
Zone/Zone/ReadyZone/DNS/Edit - Zone Resources:
Include/Specific zone/example.com
Copia el token; Cloudflare solo lo muestra una vez. Comprueba que es válido sustituyendo tu_api_token:
curl -s https://api.cloudflare.com/client/v4/user/tokens/verify \
-H "Authorization: Bearer tu_api_token"
{"result":{"id":"...","status":"active"},"success":true,"errors":[],"messages":[{"code":10000,"message":"This API Token is valid and active","type":null}]}
Paso 2: Instalar ddclient
ddclient está en los repositorios de Ubuntu. El paquete lanza un asistente de configuración que no necesitas, porque escribirás el archivo a mano; puedes evitarlo en modo no interactivo:
sudo apt update
sudo DEBIAN_FRONTEND=noninteractive apt install -y ddclient
Comprueba la versión:
dpkg -s ddclient | grep '^Version'
Version: 3.11.2-1
Paso 3: Configurar ddclient para Cloudflare
Abre el archivo de configuración:
sudo nano /etc/ddclient.conf
Sustituye todo su contenido por este bloque, con tu zona, tu token y tu nombre:
# Comprobar la IP cada 5 minutos y registrar en syslog
daemon=300
syslog=yes
ssl=yes
# Detectar la IP pública IPv4 mediante un servicio web
usev4=webv4, webv4=ipify-ipv4
# Cloudflare con token de API
protocol=cloudflare
zone=example.com
ttl=1
login=token
password=tu_api_token
casa.example.com
Algunos detalles de esta configuración:
usev4=webv4hace queddclientpregunte a un servicio externo cuál es tu IP pública. Es necesario cuando el equipo está detrás de un router con NAT, porque la IP de la interfaz sería privada.login=tokenindica quepasswordes un token de API y no la clave global de la cuenta.ttl=1significa TTL automático en Cloudflare.- La última línea es el nombre a actualizar. Puedes poner varios separados por comas.
El archivo contiene el token, así que restringe los permisos:
sudo chmod 600 /etc/ddclient.conf
Paso 4: Probar la actualización
Ejecuta ddclient una vez en primer plano con salida detallada:
sudo ddclient -daemon=0 -verbose -noquiet
Entre la salida debe aparecer la IP detectada y una línea de éxito similar a esta:
SUCCESS: casa.example.com: IPv4 address set to 198.51.100.23
Comprueba el registro consultando directamente a los servidores autoritativos de Cloudflare, que no tienen caché intermedia. Averigua cuáles son:
dig +short NS example.com
Y consulta a uno de los nombres devueltos, que tienen la forma nombre.ns.cloudflare.com:
dig +short casa.example.com @your_ns.ns.cloudflare.com
198.51.100.23
Paso 5: Ejecutar ddclient como servicio
Con daemon=300, el servicio comprueba la IP cada 5 minutos y solo llama a la API cuando cambia. Actívalo y reinícialo para que lea la configuración nueva:
sudo systemctl enable ddclient
sudo systemctl restart ddclient
systemctl status ddclient --no-pager
● ddclient.service - Update dynamic domain name service entries
Loaded: loaded (/usr/lib/systemd/system/ddclient.service; enabled; preset: enabled)
Active: active (running)
Los mensajes de cada actualización quedan en el journal:
sudo journalctl -u ddclient -n 20 --no-pager
ddclient guarda la última IP enviada en /var/cache/ddclient/ddclient.cache. Si cambias el registro a mano en Cloudflare, ejecuta sudo ddclient -daemon=0 -force para forzar una actualización.
Alternativa: DDNS con tu propio BIND9 y nsupdate
Si el dominio lo sirve tu propio BIND9, no necesitas un proveedor externo: BIND admite actualizaciones dinámicas (RFC 2136) autenticadas con una clave TSIG compartida. Los pasos siguientes suponen que ya tienes la zona example.com funcionando en el servidor.
En el servidor BIND: crear la clave y permitir la actualización
Genera una clave TSIG y guárdala con permisos restringidos:
tsig-keygen -a hmac-sha256 ddns-casa | sudo tee /etc/bind/ddns-casa.key > /dev/null
sudo chown root:bind /etc/bind/ddns-casa.key
sudo chmod 640 /etc/bind/ddns-casa.key
BIND escribe en el archivo de zona y en su diario (.jnl) al aplicar actualizaciones, y en Ubuntu solo puede escribir en /var/lib/bind. Mueve allí el archivo de zona si estaba en /etc/bind:
sudo mv /etc/bind/db.example.com /var/lib/bind/db.example.com
sudo chown bind:bind /var/lib/bind/db.example.com
Edita la definición de la zona:
sudo nano /etc/bind/named.conf.local
Incluye la clave y usa update-policy para que solo pueda modificar el registro A de casa.example.com, nada más:
include "/etc/bind/ddns-casa.key";
zone "example.com" {
type primary;
file "/var/lib/bind/db.example.com";
update-policy {
grant ddns-casa name casa.example.com. A;
};
};
Valida y recarga:
sudo named-checkconf
sudo rndc reload
Asegúrate de que el puerto 53/TCP y 53/UDP está abierto en el servidor. Copia el contenido de /etc/bind/ddns-casa.key al equipo de casa por un canal seguro (por ejemplo, scp).
Importanteuna zona con actualizaciones dinámicas no se debe editar directamente. Para cambios manuales, congélala con
sudo rndc freeze example.com, edita el archivo, incrementa el serial y descongélala consudo rndc thaw example.com.
En el equipo de casa: script de actualización
Instala nsupdate y dig (ambos en bind9-dnsutils) y curl:
sudo apt install -y bind9-dnsutils curl
Guarda la clave en un directorio accesible solo por root:
sudo install -d -m 700 /etc/ddns
sudo nano /etc/ddns/ddns-casa.key
sudo chmod 600 /etc/ddns/ddns-casa.key
Crea el script que compara la IP pública con la del DNS y solo actualiza si difieren:
sudo nano /usr/local/bin/ddns-nsupdate
#!/usr/bin/env bash
set -euo pipefail
SERVER="ns1.example.com"
ZONE="example.com"
RECORD="casa.example.com"
TTL=300
KEY="/etc/ddns/ddns-casa.key"
current_ip="$(curl -4 -fsS --max-time 10 https://api.ipify.org)"
if [[ ! "$current_ip" =~ ^[0-9]{1,3}(\.[0-9]{1,3}){3}$ ]]; then
echo "Respuesta no válida al detectar la IP: ${current_ip}" >&2
exit 1
fi
dns_ip="$(dig +short A "$RECORD" @"$SERVER" | head -n 1)"
if [[ "$current_ip" == "$dns_ip" ]]; then
exit 0
fi
nsupdate -k "$KEY" <<EOF
server ${SERVER}
zone ${ZONE}
update delete ${RECORD}. A
update add ${RECORD}. ${TTL} A ${current_ip}
send
EOF
echo "${RECORD} actualizado: ${dns_ip:-sin registro} -> ${current_ip}"
Hazlo ejecutable y pruébalo:
sudo chmod 755 /usr/local/bin/ddns-nsupdate
sudo /usr/local/bin/ddns-nsupdate
casa.example.com actualizado: sin registro -> 198.51.100.23
Si lo ejecutas otra vez sin que cambie la IP, no muestra nada. En el servidor verás la actualización en sudo journalctl -u named.
Programar la actualización con un timer de systemd
Crea la unidad de servicio:
sudo nano /etc/systemd/system/ddns-nsupdate.service
[Unit]
Description=Actualizar casa.example.com con nsupdate
Wants=network-online.target
After=network-online.target
[Service]
Type=oneshot
ExecStart=/usr/local/bin/ddns-nsupdate
Crea el timer, que lo ejecuta 2 minutos después del arranque y luego cada 5 minutos:
sudo nano /etc/systemd/system/ddns-nsupdate.timer
[Unit]
Description=Ejecutar ddns-nsupdate cada 5 minutos
[Timer]
OnBootSec=2min
OnUnitActiveSec=5min
[Install]
WantedBy=timers.target
Actívalo y comprueba la próxima ejecución:
sudo systemctl daemon-reload
sudo systemctl enable --now ddns-nsupdate.timer
systemctl list-timers ddns-nsupdate.timer
Solución de problemas
ddclient no actualiza y el log dice que la IP no ha cambiado: compara la IP detectada con la de /var/cache/ddclient/ddclient.cache. Si el registro se cambió fuera de ddclient, fuerza la actualización con sudo ddclient -daemon=0 -force.
Error de autenticación en Cloudflare: vuelve a verificar el token con la llamada tokens/verify del paso 1 y confirma que tiene los permisos Zone Read y DNS Edit sobre la zona correcta.
update failed: REFUSED con nsupdate: el nombre o el tipo no coinciden con la regla grant de update-policy, o la clave del cliente no es la misma que la del servidor. El journal de named indica el motivo exacto.
NOTAUTH(BADTIME) o tsig verify failure: la hora de los dos equipos difiere demasiado. TSIG rechaza firmas con más de 5 minutos de desfase; comprueba que ambos sincronizan la hora con timedatectl.
permission denied al escribir el diario de la zona: el archivo de zona no está en /var/lib/bind o no pertenece al usuario bind.
Conclusión
Tu dominio sigue ahora la IP pública del equipo automáticamente, ya sea con ddclient contra la API de Cloudflare o con nsupdate contra tu propio BIND9, con credenciales limitadas a un único registro. Como siguientes pasos, puedes añadir un registro AAAA si tu conexión tiene IPv6, publicar servicios de casa detrás de un proxy inverso con TLS, o sustituir la exposición directa por una VPN como WireGuard.
