--- title: "Cómo configurar DNS dinámico con ddclient en Ubuntu 24.04" description: "Mantén un registro DNS apuntando a una IP que cambia: configura ddclient con Cloudflare en Ubuntu 24.04 o actualiza tu propio BIND con nsupdate y TSIG." category: "DNS Avanzado" language: es source: https://cubepath.com/es/docs/dns-avanzado/configuracion-de-dns-dinamico-ddns updated: 2026-09-25 --- # Cómo configurar DNS dinámico con ddclient en Ubuntu 24.04 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. > Nota: los 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` / `Read` y `Zone` / `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`: ```bash curl -s https://api.cloudflare.com/client/v4/user/tokens/verify \ -H "Authorization: Bearer tu_api_token" ``` ```output {"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: ```bash sudo apt update sudo DEBIAN_FRONTEND=noninteractive apt install -y ddclient ``` Comprueba la versión: ```bash dpkg -s ddclient | grep '^Version' ``` ```output Version: 3.11.2-1 ``` ## Paso 3: Configurar ddclient para Cloudflare Abre el archivo de configuración: ```bash sudo nano /etc/ddclient.conf ``` Sustituye todo su contenido por este bloque, con tu zona, tu token y tu nombre: ```ini # 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=webv4` hace que `ddclient` pregunte 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=token` indica que `password` es un token de API y no la clave global de la cuenta. - `ttl=1` significa 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: ```bash sudo chmod 600 /etc/ddclient.conf ``` ## Paso 4: Probar la actualización Ejecuta `ddclient` una vez en primer plano con salida detallada: ```bash sudo ddclient -daemon=0 -verbose -noquiet ``` Entre la salida debe aparecer la IP detectada y una línea de éxito similar a esta: ```output 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: ```bash dig +short NS example.com ``` Y consulta a uno de los nombres devueltos, que tienen la forma `nombre.ns.cloudflare.com`: ```bash dig +short casa.example.com @your_ns.ns.cloudflare.com ``` ```output 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: ```bash sudo systemctl enable ddclient sudo systemctl restart ddclient systemctl status ddclient --no-pager ``` ```output ● 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: ```bash 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: ```bash 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`: ```bash 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: ```bash 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: ```text 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: ```bash 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`). > Importante: una 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 con `sudo rndc thaw example.com`. ### En el equipo de casa: script de actualización Instala `nsupdate` y `dig` (ambos en `bind9-dnsutils`) y `curl`: ```bash sudo apt install -y bind9-dnsutils curl ``` Guarda la clave en un directorio accesible solo por root: ```bash 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: ```bash sudo nano /usr/local/bin/ddns-nsupdate ``` ```bash #!/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" < ${current_ip}" ``` Hazlo ejecutable y pruébalo: ```bash sudo chmod 755 /usr/local/bin/ddns-nsupdate sudo /usr/local/bin/ddns-nsupdate ``` ```output 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: ```bash sudo nano /etc/systemd/system/ddns-nsupdate.service ``` ```ini [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: ```bash sudo nano /etc/systemd/system/ddns-nsupdate.timer ``` ```ini [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: ```bash 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.