Proxy DDNS Updater
Actualizador de DNS dinámico auto-hospedado y multi-provider que mantiene tu homelab apuntando a tu IP pública tras cortes de luz, reinicios del router o cambios del ISP.
- Node.js
- Express
- React
- Vite
- Tailwind
- TypeScript
- Docker
- JWT
Situación
En mi homelab corro varios sitios y servicios detrás de una conexión con IP pública dinámica. Mi registrador de dominio no actualizaba los registros automáticamente, así que cualquier apagón, reinicio del router o rotación interna del ISP dejaba los dominios apuntando a una IP obsoleta. Los sitios se caían hasta que yo entraba al panel del registrador y editaba el registro a mano.
Ese ciclo se repetía cada pocas semanas. Quería una solución que se recuperara sola después de cada evento, no otra alerta más para gestionar.
Tarea
Construir un actualizador de DNS dinámico auto-hospedado, robusto y agnóstico de proveedor que:
- Detectara cambios de IP pública en segundos y empujara los registros al proveedor sin intervención manual.
- Fuera tolerante a fallos puntuales: detección de IP con fallback entre varios proveedores públicos y timeout por proveedor, de modo que un endpoint caído no congelara el ciclo.
- Sobreviviera reinicios y caídas: estado en disco, contenedor con volumen nombrado, listo para levantarse en otra máquina con un único
docker compose up -d. - Tuviera una API + UI mínimas para gestionar dominios y subdominios, ya que varios cuelgan del mismo proveedor y no quería re-introducir credenciales por cada registro.
Acción
- Detección de IP con cadena de proveedores configurables (
IP_PROVIDERS) y timeout por proveedor (IP_REQUEST_TIMEOUT_MS): si uno se cae, los demás cubren la lectura y el siguiente tick lo reintenta. - Capa de proveedores extensible:
BaseProviderpara APIs REST (Cloudflare) yNicUpdateProviderpara el protocolo clásiconic/update(Namecheap, DuckDNS, No-IP, Dynu, deSEC). Añadir uno nuevo = una subclase + una entrada en_PROVIDERS. - Modelo de datos en dos niveles (v2):
domains[]lleva las credenciales;records[]cuelga de un dominio y representa un subdominio o apex@. Un único token de Cloudflare puede administrar todos los subdominios de una zona sin re-pedir credenciales. - Scheduler single-flight con timeout por proveedor (30 s): un proveedor colgado no puede parar el bucle y dos ciclos nunca compiten entre sí.
- Heartbeat de re-actualización cada 25 días — los proveedores de DDNS imponen un TTL máximo; sin re-escritura periódica los registros expiran aunque la IP no haya cambiado.
- Backend Node.js/Express + JWT (TTL configurable con
TOKEN_TTL_SECONDS), bcrypt (cost 12), rate-limiter de login (20 intentos / 15 min) yhelmetactivado por defecto. - Frontend React (Vite + Tailwind), servido por Nginx, con reverse-proxy de
/api/*al backend sobre la red interna de Docker. Un únicodocker compose up -d --buildlevanta todo. - Persistencia en un único volumen (
ddns-data) con dos archivos JSON (admin.json+config.json): snapshot del volumen = backup completo y portable.
Resultado
- Cero ediciones manuales desde que está en producción: la única interacción es añadir un dominio o un registro desde la UI.
- Sobrevivió múltiples apagones, reinicios del NUC y al menos dos rotaciones de IP del ISP sin intervención: los registros se actualizaron al siguiente tick del scheduler y los servicios volvieron solos.
- 6 proveedores funcionales out-of-the-box; Cloudflare además crea los registros inexistentes en automático.
- Un proveedor caído no tumba el ciclo: el timeout por proveedor (30 s) absorbe el fallo y el siguiente ciclo lo reintenta contra la cadena entera.
- Toda la operativa se ve en un único dashboard: estado por dominio, último IP conocido, último timestamp, último error.
- Migrado de máquina dos veces (cambios de host físico) sin tocar el estado: copiar el volumen
ddns-data,docker compose up -d, listo.


