# Apply: kubectl apply -f manifests/network/ddns-cronjob.yaml # Delete: kubectl delete -f manifests/network/ddns-cronjob.yaml # Description: CronJob that updates nik4nao.com and home.nik4nao.com DNS on Porkbun every 5 minutes. apiVersion: v1 kind: Namespace metadata: name: ddns --- apiVersion: batch/v1 kind: CronJob metadata: name: porkbun-ddns namespace: ddns spec: schedule: "*/5 * * * *" successfulJobsHistoryLimit: 3 failedJobsHistoryLimit: 1 jobTemplate: spec: template: spec: restartPolicy: OnFailure containers: - name: ddns image: alpine:latest env: - name: PORKBUN_API_KEY valueFrom: secretKeyRef: name: porkbun-ddns key: api-key - name: PORKBUN_SECRET_KEY valueFrom: secretKeyRef: name: porkbun-ddns key: secret-api-key command: - /bin/sh - -c - | set -euo pipefail apk add --no-cache curl jq -q DOMAIN="nik4nao.com" TTL=300 timestamp() { date +"%Y-%m-%d %H:%M:%S%z"; } # Get current WAN IP WAN_IP="$(curl -sf https://api.ipify.org)" if [ -z "$WAN_IP" ]; then echo "[$(timestamp)] ERROR: Could not detect WAN IP" exit 1 fi echo "[$(timestamp)] WAN IP: $WAN_IP" # Get all DNS records once (reused for all iterations) RECORDS="$(curl -sf -X POST \ "https://api.porkbun.com/api/json/v3/dns/retrieve/${DOMAIN}" \ -H 'Content-Type: application/json' \ -d "{\"apikey\":\"$PORKBUN_API_KEY\",\"secretapikey\":\"$PORKBUN_SECRET_KEY\"}")" for RECORD_NAME in "" "home"; do RECORD_FQDN="${DOMAIN}" [ -n "$RECORD_NAME" ] && RECORD_FQDN="${RECORD_NAME}.${DOMAIN}" echo "[$(timestamp)] Processing: ${RECORD_FQDN}" # Get all A record IDs for this record RECORD_IDS="$(echo "$RECORDS" | jq -r \ --arg fqdn "$RECORD_FQDN" \ '.records[] | select(.type=="A" and .name==$fqdn) | .id')" # Get the current DNS IP from the first record DNS_IP="$(echo "$RECORDS" | jq -r \ --arg fqdn "$RECORD_FQDN" \ '.records[] | select(.type=="A" and .name==$fqdn) | .content' | head -1)" echo "[$(timestamp)] DNS IP: ${DNS_IP:-none}" # Delete all stale duplicate records RECORD_COUNT="$(echo "$RECORD_IDS" | grep -c . || true)" if [ "$RECORD_COUNT" -gt 1 ]; then echo "[$(timestamp)] Found $RECORD_COUNT duplicate records, deleting all..." for ID in $RECORD_IDS; do curl -sf -X POST \ "https://api.porkbun.com/api/json/v3/dns/delete/${DOMAIN}/${ID}" \ -H 'Content-Type: application/json' \ -d "{\"apikey\":\"$PORKBUN_API_KEY\",\"secretapikey\":\"$PORKBUN_SECRET_KEY\"}" > /dev/null echo "[$(timestamp)] Deleted record ID $ID" done DNS_IP="" fi # Get the single remaining record ID (if any) RECORD_ID="$(echo "$RECORDS" | jq -r \ --arg fqdn "$RECORD_FQDN" \ '.records[] | select(.type=="A" and .name==$fqdn) | .id' | head -1)" # Skip if already correct if [ "$RECORD_COUNT" -le 1 ] && [ "$WAN_IP" = "$DNS_IP" ]; then echo "[$(timestamp)] No change, skipping." continue fi echo "[$(timestamp)] IP changed ${DNS_IP:-none} -> $WAN_IP, updating..." if [ -z "$RECORD_ID" ] || [ "$RECORD_COUNT" -gt 1 ]; then # Create fresh record echo "[$(timestamp)] Creating new record..." RESP="$(curl -sf -X POST \ "https://api.porkbun.com/api/json/v3/dns/create/${DOMAIN}" \ -H 'Content-Type: application/json' \ -d "{\"apikey\":\"$PORKBUN_API_KEY\",\"secretapikey\":\"$PORKBUN_SECRET_KEY\",\"type\":\"A\",\"name\":\"$RECORD_NAME\",\"content\":\"$WAN_IP\",\"ttl\":$TTL}")" else # Update existing single record echo "[$(timestamp)] Updating record ID $RECORD_ID..." RESP="$(curl -sf -X POST \ "https://api.porkbun.com/api/json/v3/dns/edit/${DOMAIN}/${RECORD_ID}" \ -H 'Content-Type: application/json' \ -d "{\"apikey\":\"$PORKBUN_API_KEY\",\"secretapikey\":\"$PORKBUN_SECRET_KEY\",\"type\":\"A\",\"name\":\"$RECORD_NAME\",\"content\":\"$WAN_IP\",\"ttl\":$TTL}")" fi echo "[$(timestamp)] Response: $(echo "$RESP" | jq -c .)" echo "$RESP" | grep -q '"status":"SUCCESS"' && \ echo "[$(timestamp)] Update successful" || \ echo "[$(timestamp)] Update failed" done