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

Índice
- Opción 1 — DuckDNS: el más sencillo
- Opción 2 — qzz.io con Cloudflare: un dominio propio sin coste
- Opción 3 — ddclient: el cliente estándar de Linux
- Opción 4 — Mi configuración en producción
- Comparativa rápida
- Recursos
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
- Ve a duckdns.org e inicia sesión con GitHub, Google, Twitter o Reddit — no crea cuenta nueva, usa tu cuenta existente.
- En el campo “sub domain”, escribe el nombre que quieras. Por ejemplo:
miservidor→ obtienesmiservidor.duckdns.org. - Haz clic en “add domain”. Aparece en tu lista con la IP actual.
- 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
- 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).
- Haz clic en Register Domain.
- Elige el sufijo que quieres (
.qzz.io,.us.kg, etc.) y escribe el subdominio: por ejemplotunombre→ obtienestunombre.qzz.io. - Por ahora deja la configuración de nameservers en blanco — los necesitarás del paso siguiente.
- 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.
- Crea una cuenta en cloudflare.com.
- Ve a Add a Site (o “Añadir un sitio”) y escribe
tunombre.qzz.io. - Selecciona el plan Free.
- Cloudflare te mostrará dos nameservers:
Anótalos y vuelve a DigitalPlat → configura esos nameservers en tu dominio → confirma.xxx.ns.cloudflare.com yyy.ns.cloudflare.com
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:
| Servicio | Qué hace | Límite gratuito |
|---|---|---|
| Tunnel | Expone servicios locales sin abrir puertos en el router | Gratuito, sin límite de ancho de banda |
| Zero Trust / Access | Controla quién accede a tus apps con SSO e identidad | Hasta 50 usuarios |
| Pages | Hosting de sitios estáticos con CI/CD integrado | Ancho de banda ilimitado, 500 builds/mes |
| Workers | Código serverless ejecutado en el edge de Cloudflare | 100.000 peticiones/día |
| R2 | Almacenamiento de objetos compatible con S3 | 10 GB, sin coste de salida |
| Email Routing | Reenvía emails recibidos en tu dominio a cualquier cuenta | Gratuito |
| WAF básico | Protecció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
| Tipo | Nombre | Contenido | Proxy |
|---|---|---|---|
| A | tunombre.qzz.io | IP 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
- En el panel de Cloudflare: My Profile → API Tokens → Create Token
- Selecciona la plantilla “Edit zone DNS” — permisos exactos para lo que necesitamos
- En Zone Resources, cambia “All zones” por “Specific zone” → selecciona solo
tunombre.qzz.io - 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
- 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
| DuckDNS | qzz.io + Cloudflare | ddclient | Script propio | |
|---|---|---|---|---|
| Tiempo de setup | 5 minutos | 15-30 minutos | 5 minutos | 15 minutos |
| Aspecto del dominio | nombre.duckdns.org | nombre.qzz.io | cualquiera | cualquiera |
| Control DNS | Solo el registro A | Total (A, CNAME, MX, TXT…) | según proveedor | Total (API Cloudflare) |
| Proxy/CDN | No | Sí (Cloudflare) | No | No |
| DDNS automático | Script oficial simple | Script con API Cloudflare | systemd nativo | Cron + logging por mes |
| Token en URL | ⚠️ Sí (limitación API) | No — header cifrado | No | No — fichero temporal |
| Logging | Solo duck.log | Solo syslog | journald | Fichero mensual propio |
| Múltiples dominios | No | No (un registro A) | Sí | Sí (array configurable) |
| Coste | Gratuito | Gratuito | Gratuito | Gratuito |
| Ideal para | Acceso rápido, laboratorio | Dominio profesional | Sin gestionar scripts | RPi, múltiples dominios, logs detallados |
