DDNS gratuito en Linux: DuckDNS, Cloudflare y ddclient para IP dinámica

DDNS gratuito en Linux: DuckDNS, Cloudflare y ddclient para IP dinámica

Índice

En el universo Star Wars, la HoloRed permite contactar con cualquier nave o planeta independientemente de su posición en la galaxia. El equivalente en tu homelab e Internet se llama DDNS: un nombre de dominio que siempre apunta a tu servidor, aunque tu IP cambie cada vez que el router se reinicia.

En próximos artículos (VPS en Oracle Cloud y relay WireGuard) será importante no perder de vista nuestros nuevos servicios para la rebelión. Pero en tu cuartel general la IP es dinámica — tu ISP la cambia sin avisar, y de repente tu dominio apunta a un agujero negro que no lleva a ningún sitio. La solución es un cliente DDNS que detecta el cambio y actualiza el registro DNS automáticamente.

Este artículo cubre tres opciones completamente gratuitas, de menor a mayor sofisticación. Desde el script de cron más simple que puedas imaginar hasta control total del DNS con tu propio dominio. Elige el nivel de armamento que necesita tu base.


Nota: Este artículo describe configuraciones para proyectos personales, homelab y aprendizaje. Los servicios gratuitos mencionados tienen sus propios Términos de Servicio — respétalos. No uses estos dominios para actividades ilegales, spam ni suplantación de identidad.


Opción 1 — DuckDNS: el más sencillo

DuckDNS es el punto de entrada ideal. Sin instalaciones, sin configuración de nameservers, sin cuenta de pago. En cinco minutos tienes un subdominio tudominio.duckdns.org apuntando a tu servidor.

Registro y creación del subdominio

  1. Ve a duckdns.org e inicia sesión con GitHub, Google, Twitter o Reddit — no crea cuenta nueva, usa tu cuenta existente.
  2. En el campo “sub domain”, escribe el nombre que quieras. Por ejemplo: miservidor → obtienes miservidor.duckdns.org.
  3. Haz clic en “add domain”. Aparece en tu lista con la IP actual.
  4. Anota el token que aparece en la parte superior de la página — lo necesitarás para el script.

Cliente en Linux — actualización automática

DuckDNS no instala nada en el sistema. El cliente es un script que se ejecuta periódicamente con cron. Antes de crearlo, protege el token igual que harías con cualquier credencial — en un fichero solo legible por tu usuario:

echo "TU_TOKEN_DUCKDNS" > ~/.duckdns_token
chmod 600 ~/.duckdns_token
chown $USER:$USER ~/.duckdns_token

Ahora crea el script:

mkdir -p ~/duckdns
cat > ~/duckdns/duck.sh <<'EOF'
#!/bin/bash
DOMINIO="TUDOMINIO"   # sin .duckdns.org
TOKEN=$(cat ~/.duckdns_token)
echo url="https://www.duckdns.org/update?domains=${DOMINIO}&token=${TOKEN}&ip=" \
  | curl -k -o ~/duckdns/duck.log -K -
EOF
chmod +x ~/duckdns/duck.sh

Fíjate en el | curl -K -: le pasa la URL a curl por stdin en lugar de como argumento. Esto evita que el token aparezca en ps aux mientras el proceso está corriendo — cualquiera que ejecute ps aux en ese momento solo verá curl -k -o duck.log -K -, sin el token.

Limitación que no puedes evitar con DuckDNS: el token viaja en la URL (?token=...), no en un header HTTP. Es la API que DuckDNS ofrece y no admite alternativa. Eso significa que el token queda registrado en los logs de acceso del servidor de DuckDNS, y que duck.log puede contenerlo si hay un error de red. Protege ese log:

chmod 600 ~/duckdns/duck.log

Si este nivel de exposición no te convence, la Opción 2 (Cloudflare) permite tokens en header con permisos mínimos y filtro por IP — control que DuckDNS no da.

Prueba que funciona:

~/duckdns/duck.sh
cat ~/duckdns/duck.log
# debe mostrar: OK

Automatiza con cron para que se ejecute cada 5 minutos:

crontab -e
# añade:
*/5 * * * * ~/duckdns/duck.sh >/dev/null 2>&1

A partir de ese momento, cada vez que tu IP cambie, DuckDNS lo detectará en los siguientes 5 minutos y actualizará el registro.

Verificar que funciona

dig +short tudominio.duckdns.org
# debe devolver tu IP pública actual
curl ifconfig.me
# compara con el resultado anterior — deben coincidir

Cuándo usar DuckDNS

DuckDNS es perfecto para:

  • Acceso remoto a un servidor doméstico o de laboratorio
  • Relay VPN propio (como el relay WireGuard que veremos próximamente)
  • Cualquier servicio que necesite un nombre fijo pero no requiera un dominio “presentable”

La limitación es el aspecto: tudominio.duckdns.org es funcional pero identifica inmediatamente el servicio como algo casero. Si quieres algo más neutro o con tu nombre propio, la opción 2 es mejor.


Opción 2 — qzz.io con Cloudflare: un dominio propio sin coste

DigitalPlat FreeDomain es un proyecto sin ánimo de lucro respaldado por The Hack Foundation (organización 501(c)(3) de EEUU). Ofrece subdominios gratuitos en varios dominios propios — el más usado es .qzz.io, aunque también hay .us.kg, .dpdns.org y .xx.kg.

La diferencia con DuckDNS: aquí tú gestionas el DNS con tu propio proveedor (Cloudflare, en este caso). Tienes control total sobre los registros — A, CNAME, MX, TXT, lo que necesites.

Paso 1 — Registrar el dominio en DigitalPlat

  1. Ve a domain.digitalplat.org e inicia sesión con tu cuenta de GitHub — es el único método de autenticación (sirve como verificación de identidad).
  2. Haz clic en Register Domain.
  3. Elige el sufijo que quieres (.qzz.io, .us.kg, etc.) y escribe el subdominio: por ejemplo tunombre → obtienes tunombre.qzz.io.
  4. Por ahora deja la configuración de nameservers en blanco — los necesitarás del paso siguiente.
  5. Confirma el registro.

El dominio queda reservado a tu nombre. En el siguiente paso lo apuntaremos a Cloudflare.

Paso 2 — Cuenta gratuita en Cloudflare

Cloudflare gestionará el DNS de tu nuevo dominio. El plan gratuito es suficiente para todo lo que vamos a hacer.

  1. Crea una cuenta en cloudflare.com.
  2. Ve a Add a Site (o “Añadir un sitio”) y escribe tunombre.qzz.io.
  3. Selecciona el plan Free.
  4. Cloudflare te mostrará dos nameservers:
    xxx.ns.cloudflare.com
    yyy.ns.cloudflare.com
    
    Anótalos y vuelve a DigitalPlat → configura esos nameservers en tu dominio → confirma.

La propagación puede tardar desde unos minutos hasta 24 horas. Cuando Cloudflare verifique el dominio te llegará un email de confirmación.

Bonus — qué más incluye la cuenta gratuita de Cloudflare:

ServicioQué haceLímite gratuito
TunnelExpone servicios locales sin abrir puertos en el routerGratuito, sin límite de ancho de banda
Zero Trust / AccessControla quién accede a tus apps con SSO e identidadHasta 50 usuarios
PagesHosting de sitios estáticos con CI/CD integradoAncho de banda ilimitado, 500 builds/mes
WorkersCódigo serverless ejecutado en el edge de Cloudflare100.000 peticiones/día
R2Almacenamiento de objetos compatible con S310 GB, sin coste de salida
Email RoutingReenvía emails recibidos en tu dominio a cualquier cuentaGratuito
WAF básicoProtección contra ataques comunes (SQLi, XSS…)Incluido en el plan Free

El más interesante para homelab es Tunnel: accede a servicios corriendo en tu red local desde Internet sin tocar el firewall ni abrir un solo puerto. El WAF tiene más chicha de lo que parece — lo vimos en detalle en WAF en Cloudflare para una web estática.

Paso 3 — Configurar los registros DNS en Cloudflare

Una vez confirmado el dominio, añade el registro A desde el panel:

DNS → Records → Add record

TipoNombreContenidoProxy
Atunombre.qzz.ioIP de tu servidor☁️ sí (opcional) o DNS only

Si activas el proxy de Cloudflare (la nube naranja), el tráfico HTTP/HTTPS pasa por sus servidores — protección DDoS y ocultación de tu IP real. Para SSH o WireGuard, desactívalo (nube gris) — Cloudflare no proxifica UDP ni SSH.

Paso 4 — Crear un API Token seguro en Cloudflare

Aquí es donde la mayoría de guías lo hacen mal. Antes de escribir una sola línea de script, hay que tener claras dos premisas:

Premisa 1 — permisos mínimos: el token solo debe poder hacer exactamente lo que necesita. Nada más. Premisa 2 — protección en el sistema: la credencial nunca aparece como argumento de proceso ni viaja en claro en logs o repos.

Por qué importa: un token Cloudflare con permisos de “toda la cuenta” es la llave maestra de todo lo que tienes — dominios, WAF, Workers, DNS… Si ese token se filtra (en un repo público, en .bash_history, en un log de cron), el atacante controla tu infraestructura. Un token limitado a “actualizar un registro DNS de una zona concreta” no sirve para nada más aunque caiga en malas manos.

Crear el token con permisos mínimos

  1. En el panel de Cloudflare: My Profile → API Tokens → Create Token
  2. Selecciona la plantilla “Edit zone DNS” — permisos exactos para lo que necesitamos
  3. En Zone Resources, cambia “All zones” por “Specific zone” → selecciona solo tunombre.qzz.io
  4. En IP Address Filtering, añade la IP pública de tu servidor — el token solo funcionará desde esa dirección aunque alguien lo robe
  5. Copia el token cuando aparezca — Cloudflare solo lo muestra una vez

Proteger el token en el sistema

echo "TU_TOKEN_AQUI" > ~/.cloudflare_token
chmod 600 ~/.cloudflare_token
chown $USER:$USER ~/.cloudflare_token

El token vive en ese fichero. El script nunca lo expone como argumento — usaremos un fichero de configuración temporal de curl para que no aparezca en ps aux mientras el proceso corre (ver Paso 5).

Si necesitas rotar el token, solo actualizas el fichero — el script no cambia.

Paso 5 — DDNS automático desde el servidor

qzz.io no tiene cliente propio. Usas la API de Cloudflare directamente. Necesitas el Zone ID (en la página principal de tu dominio en Cloudflare, columna derecha) y el Record ID del registro A:

TMPCONF=$(mktemp); chmod 600 "$TMPCONF"
printf 'header = "Authorization: Bearer %s"\n' "$(cat ~/.cloudflare_token)" > "$TMPCONF"
curl -s -X GET \
  "https://api.cloudflare.com/client/v4/zones/TU_ZONE_ID/dns_records?type=A&name=tunombre.qzz.io" \
  --config "$TMPCONF" | python3 -m json.tool | grep '"id"'
rm -f "$TMPCONF"

Script de actualización. Comprueba si la IP cambió antes de llamar a la API — evita peticiones innecesarias. El token nunca aparece como argumento de proceso: va en un fichero temporal de configuración de curl que se borra justo después:

#!/bin/bash
# ddns-cloudflare.sh — actualiza el registro A solo si la IP cambió

ZONE_ID="TU_ZONE_ID"
RECORD_ID="TU_RECORD_ID"
DOMAIN="tunombre.qzz.io"
CACHE="/tmp/ddns_last_ip"

IP_ACTUAL=$(curl -s https://ifconfig.me)
IP_ANTERIOR=$(cat "$CACHE" 2>/dev/null)

if [ "$IP_ACTUAL" = "$IP_ANTERIOR" ]; then
  exit 0
fi

# Fichero temporal de configuración curl — el token nunca aparece en ps aux
TMPCONF=$(mktemp)
chmod 600 "$TMPCONF"
printf 'header = "Authorization: Bearer %s"\n' "$(cat ~/.cloudflare_token)" > "$TMPCONF"

RESULTADO=$(curl -s -X PATCH \
  "https://api.cloudflare.com/client/v4/zones/${ZONE_ID}/dns_records/${RECORD_ID}" \
  --config "$TMPCONF" \
  -H "Content-Type: application/json" \
  --data "{\"type\":\"A\",\"name\":\"${DOMAIN}\",\"content\":\"${IP_ACTUAL}\",\"ttl\":60}")

rm -f "$TMPCONF"

if echo "$RESULTADO" | grep -q '"success":true'; then
  echo "$IP_ACTUAL" > "$CACHE"
  logger "ddns-cloudflare: actualizado $DOMAIN$IP_ACTUAL"
else
  logger "ddns-cloudflare: ERROR — $RESULTADO"
fi

Automatiza con cron cada 5 minutos:

chmod +x ~/ddns-cloudflare.sh
crontab -e
# añade:
*/5 * * * * ~/ddns-cloudflare.sh

Opción 3 — ddclient: el cliente estándar de Linux

Si prefieres no gestionar scripts propios, ddclient es el cliente DDNS más extendido en Linux. Está en los repositorios oficiales de Debian, Ubuntu y Rocky, soporta más de 50 proveedores (DuckDNS, Cloudflare, No-IP, Google Domains…) y se integra como servicio de systemd.

sudo apt install ddclient   # Debian/Ubuntu
sudo dnf install ddclient   # RedHat/Rocky

Durante la instalación aparece un asistente. Si prefieres configurarlo a mano, el fichero es /etc/ddclient.conf.

ddclient con DuckDNS

# /etc/ddclient.conf
daemon=300                      # comprobar cada 5 minutos
syslog=yes
pid=/run/ddclient/ddclient.pid

protocol=duckdns
login=nouser
password=TU_TOKEN_DUCKDNS
tudominio                       # sin .duckdns.org

ddclient con Cloudflare

# /etc/ddclient.conf
daemon=300
syslog=yes
pid=/run/ddclient/ddclient.pid

protocol=cloudflare
zone=tunombre.qzz.io
login=TU_EMAIL_CLOUDFLARE
password=TU_API_TOKEN
ttl=1
tunombre.qzz.io

Activa y arranca el servicio:

sudo systemctl enable --now ddclient
sudo systemctl status ddclient

Comprueba que funciona:

sudo ddclient -daemon=0 -debug -verbose -noquiet

Fíjate en que el token/contraseña va en claro en /etc/ddclient.conf. ddclient lo crea con permisos 600 (solo root puede leerlo), así que el riesgo es menor que en un script manual. Verifica que los permisos son correctos:

ls -la /etc/ddclient.conf
# -rw------- 1 root root ... /etc/ddclient.conf

Si no es así, corrígelo:

sudo chmod 600 /etc/ddclient.conf

Limitación que no puedes evitar con ddclient: igual que en DuckDNS, el token viaja en la URL cuando el protocolo lo exige (DuckDNS). Con Cloudflare ddclient sí usa headers — mejor opción si la seguridad te preocupa.

La ventaja de ddclient frente a los scripts manuales: ya gestiona el caché de IP (no llama a la API si no hay cambio), reintentos en caso de error y logging integrado con journald.


Opción 4 — Mi configuración en producción: script propio con logging

Si las opciones anteriores te parecen demasiado simples o demasiado complejas, aquí está lo que uso en mis propias RPis de monitorización. Un script bash con logging estructurado, soporte para múltiples dominios y el token correctamente protegido.

#!/bin/bash
# ddns-cloudflare-prod.sh — actualiza registros A en Cloudflare con logging

CF_ZONE_ID="TU_ZONE_ID"
DOMAINS=("tunombre.qzz.io")   # añadir más dominios si es necesario

LOG_DIR=~/cloudflare/logs
LOG_FILE="${LOG_DIR}/cloudflare-$(date +%Y-%m).log"
mkdir -p "$LOG_DIR"

log() {
  echo "$(date '+%b %d %H:%M:%S') $(hostname) ddns[$$]: [$1] $2" | tee -a "$LOG_FILE"
}

CURRENT_IP=$(curl -s https://ipv4.icanhazip.com)
log "NOTICE" "Iniciando actualización — IP actual: $CURRENT_IP"

for DOMAIN in "${DOMAINS[@]}"; do
  TMPCONF=$(mktemp); chmod 600 "$TMPCONF"
  printf 'header = "Authorization: Bearer %s"\n' "$(cat ~/.cloudflare_token)" > "$TMPCONF"

  RECORD_ID=$(curl -s -X GET \
    "https://api.cloudflare.com/client/v4/zones/${CF_ZONE_ID}/dns_records?type=A&name=${DOMAIN}" \
    --config "$TMPCONF" -H "Content-Type: application/json" | jq -r '.result[0].id')

  RESPONSE=$(curl -s -X PUT \
    "https://api.cloudflare.com/client/v4/zones/${CF_ZONE_ID}/dns_records/${RECORD_ID}" \
    --config "$TMPCONF" -H "Content-Type: application/json" \
    --data "{\"type\":\"A\",\"name\":\"${DOMAIN}\",\"content\":\"${CURRENT_IP}\",\"ttl\":120,\"proxied\":false}")

  rm -f "$TMPCONF"

  echo "$RESPONSE" | grep -q '"success":true' \
    && log "INFO" "✓ $DOMAIN$CURRENT_IP" \
    || log "ERROR" "✗ $DOMAIN$RESPONSE"
done

Cron cada 30 minutos:

*/30 * * * * ~/cloudflare/ddns-cloudflare-prod.sh >/dev/null 2>&1

Los logs se rotan automáticamente por mes en ~/cloudflare/logs/.


Comparativa rápida

DuckDNSqzz.io + CloudflareddclientScript propio
Tiempo de setup5 minutos15-30 minutos5 minutos15 minutos
Aspecto del dominionombre.duckdns.orgnombre.qzz.iocualquieracualquiera
Control DNSSolo el registro ATotal (A, CNAME, MX, TXT…)según proveedorTotal (API Cloudflare)
Proxy/CDNNoSí (Cloudflare)NoNo
DDNS automáticoScript oficial simpleScript con API Cloudflaresystemd nativoCron + logging por mes
Token en URL⚠️ Sí (limitación API)No — header cifradoNoNo — fichero temporal
LoggingSolo duck.logSolo syslogjournaldFichero mensual propio
Múltiples dominiosNoNo (un registro A)Sí (array configurable)
CosteGratuitoGratuitoGratuitoGratuito
Ideal paraAcceso rápido, laboratorioDominio profesionalSin gestionar scriptsRPi, múltiples dominios, logs detallados

Recursos