Files
ddns/README.md
T
carlitosbondandClaude Sonnet 5 d68f1f828f Fix stale script filename references in README
Docs referenced ddns_porkbun.sh / ddns_porkbun_test.sh, which never
matched the actual files (porkbun-ddns.sh, porkbun-ddns-test.sh).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-22 23:25:24 +00:00

4.9 KiB

Porkbun DDNS — Dynamic DNS para carloselugo.com

Fecha: Abril 2026
Servidor: labmini-01 (Dell Optiplex)
Dominio: carloselugo.com
Ubicación del script: /opt/ddns/porkbun-ddns.sh


Objetivo

Detectar cambios en la IP pública del homelab y actualizar automáticamente los registros DNS en Porkbun — sin depender de un servicio DDNS de terceros.


Arquitectura

cron (cada 30 min)
  → porkbun-ddns.sh
      → obtiene IP pública (api.ipify.org)
      → compara con state file (~/.porkbun-ddns.last-ip)
      → si cambió → Porkbun API (editByNameType)
                  → actualiza registros A

Registros DNS actualizados

Subdominio FQDN Tipo
@ carloselugo.com A
gamesever1 gamesever1.carloselugo.com A
git git.carloselugo.com A
void void.carloselugo.com A
tools tools.carloselugo.com A
mealie mealie.carloselugo.com A
md md.carloselugo.com A
hits hits.carloselugo.com A
fotos fotos.carloselugo.com A

Nota: cada subdominio es su propio registro A independiente (no hay wildcard/CNAME heredando la IP del root) — un subdominio nuevo debe agregarse explícitamente al array SUBDOMAINS del script, si no, el script nunca lo va a mantener actualizado aunque el registro exista en Porkbun.


Archivos

Archivo Ruta
Script /opt/ddns/porkbun-ddns.sh
State file /home/netintel/.porkbun-ddns.last-ip
Log /home/netintel/.porkbun-ddns.log

Configuración del script

API_KEY="pk1_..."           # Porkbun API key
SECRET_KEY="sk1_..."        # Porkbun secret key
DOMAIN="carloselugo.com"
SUBDOMAINS=("@" "gamesever1" "git" "void" "tools" "mealie" "md" "hits" "fotos")
STATE_FILE="/home/netintel/.porkbun-ddns.last-ip"
LOG_FILE="/home/netintel/.porkbun-ddns.log"

Endpoint API usado

POST /dns/editByNameType/{domain}/A/{subdomain}

Se usa editByNameType en lugar de edit por ID porque es más robusto — no requiere hacer un retrieve previo para obtener el record ID, y evita el riesgo de crear registros duplicados accidentalmente.

Para el root (@) el endpoint es:

POST /dns/editByNameType/carloselugo.com/A

Para subdomains:

POST /dns/editByNameType/carloselugo.com/A/git

Prerequisitos en Porkbun

  1. Generar API key y secret en Account Settings → API Access
  2. Habilitar API access para el dominio en Domain Management → carloselugo.com → API Access (toggle)

Sin el toggle por dominio, el API devuelve DOMAIN_IS_NOT_OPTED_IN_TO_API_ACCESS aunque las keys sean correctas.


Lógica del script

  1. Obtiene la IP pública via api.ipify.org (fallback: ifconfig.me, icanhazip.com)
  2. Lee la última IP conocida del state file
  3. Si son iguales → exit silencioso (no llama al API)
  4. Si son diferentes → por cada subdominio:
    • Obtiene la IP actual en DNS via retrieveByNameType
    • Si ya coincide → skip
    • Si no → llama a editByNameType con la nueva IP
  5. Guarda la nueva IP en el state file

Cron

# crontab -e (usuario netintel)
*/30 * * * * /opt/ddns/porkbun-ddns.sh

Corre cada 30 minutos. Solo genera output en el log cuando la IP cambia.


Verificar funcionamiento

# Correr manualmente
/opt/ddns/porkbun-ddns.sh

# Ver log
tail -f ~/.porkbun-ddns.log

# Ver última IP registrada
cat ~/.porkbun-ddns.last-ip

# Forzar una IP específica (solo script de prueba)
/opt/ddns/porkbun-ddns-test.sh -f 1.2.3.4

Ejemplo de log

# Primera ejecución
[2026-04-28 11:44:54] IP cambió: (primera ejecución) → 24.139.235.46
[2026-04-28 11:44:55]   [@] Ya actualizado (24.139.235.46)
[2026-04-28 11:44:56]   [gamesever1] Ya actualizado (24.139.235.46)
[2026-04-28 11:44:57]   [git] Ya actualizado (24.139.235.46)

# Cuando la IP cambia
[2026-04-28 12:00:00] IP cambió: 24.139.235.46 → 24.139.236.10
[2026-04-28 12:00:01]   [@] ✓ 24.139.235.46 → 24.139.236.10
[2026-04-28 12:00:02]   [gamesever1] ✓ 24.139.235.46 → 24.139.236.10
[2026-04-28 12:00:03]   [git] ✓ 24.139.235.46 → 24.139.236.10
[2026-04-28 12:00:03] Listo. 3 registro(s) actualizado(s).

# Cuando no hay cambio (silencioso, no aparece en log)

Troubleshooting

El script no puede escribir el log
Verificar que las rutas de STATE_FILE y LOG_FILE sean escribibles por el usuario que corre el cron. Usar rutas en $HOME si no se corre como root.

Error: DOMAIN_IS_NOT_OPTED_IN_TO_API_ACCESS
El toggle de API access del dominio en Porkbun está desactivado. Activarlo en Domain Management.

El API conecta por IPv6 y falla
El servidor tiene conectividad IPv6. Todos los curl usan -4 para forzar IPv4 y evitar timeouts silenciosos.

El record desaparece después de editar
No usar dns/edit por ID cuando hay Cloudflare proxy habilitado en Porkbun — puede borrar el record. Usar siempre editByNameType.