使用 GoDaddy v3 API 建立自己的 DDNS 服務

最近不小心手滑買了一個 GoDaddy 網域,打算之後在 QNAP 的 Container Station 運行 WordPress 來架設自己的部落格。不過在開始之前,我們得先解決中華電信浮動 IP 的問題。

如果你是在家裡架設網站或其他服務,家用網路的 IP 位址通常是浮動的。
我們需要透過 DDNS 服務,在 IP 位址發生變更時,自動更新 DNS 紀錄,確保網域名稱永遠指向目前的公開 IP。

GoDaddy DNS 紀錄

剛建立好的 GoDaddy DNS 紀錄如下,其中 @ 代表的是這個網域的根網域(Apex / Root Domain)。

通常企業會把這個根網域指向官方的形象網站,對我們來說當然就希望這個根網域會導向自己的部落格。

這也是我們這次的主題,希望透過 DDNS 服務,確保網域名稱永遠指向目前的公開 IP。我們希望在 Ubuntu 24.04 透過 GoDaddy API 來管理 DNS 紀錄。

先在 GoDaddy 建立 PAT

現在的 API 使用 Personal Access Token,點選右上角的 Generate Token。

https://developer.godaddy.com/en/personal-access-token

Generate Personal Access Token

  • Name:輸入 Token 的名字,例如 DDNS。
  • Expiration:1 Year

Scopes 只需給予下列兩個權限

  • View Doamin List and Details(domains.domain:read)
  • Modify DNS Records(domains.dns:update)

點選 Generate Token

請將其妥善保管,關閉此對話方塊後,您將無法再次看到此 Token。

gd_pat_XRHsGsZqqJdGYZ85ysiL6agdPme2iDFaL7ThHCtzq6h_3975d322

建立 DDNS 設定檔

先建立設定檔,讓 PAT 不會直接寫死在 Script 裡。

sudo mkdir -p /etc/godaddy-ddns
sudo nano /etc/godaddy-ddns/config

內容如下

GODADDY_PAT="gd_pat_XRHsGsZqqJdGYZ85ysiL6agdPme2iDFaL7ThHCtzq6h_3975d322"

DOMAIN="ivancheng.tw"
RECORD_NAME="@"
TTL="600"

調整設定檔權限

sudo chmod 600 /etc/godaddy-ddns/config

建立 DDNS Script

首先取得目前的 Public IP,接著向 GoDaddy DNS 查詢目前 A Record 所指向的 IP,並將兩者進行比對。

如果 IP 位址沒有變更,就不進行任何操作。如果發現 IP 已經改變,則透過 GoDaddy API 更新 DNS Record,將網域名稱重新指向目前的 Public IP。

sudo nano /usr/local/bin/godaddy-ddns.sh

實作範例如下

#!/usr/bin/env bash

set -euo pipefail

CONFIG="/etc/godaddy-ddns/config"

if [[ ! -f "$CONFIG" ]]; then
    echo "Config file not found: $CONFIG"
    exit 1
fi

source "$CONFIG"

BASE_URL="https://api.godaddy.com/v3/domains/zones/${DOMAIN}/dns-records"

echo "========================================"
echo "GoDaddy DDNS"
echo "Domain : ${DOMAIN}"
echo "Record : ${RECORD_NAME}"
echo "API    : v3"
echo "========================================"

# ============================================================
# 1. Get the current public IPv4 address
# ============================================================

CURRENT_IP=$(curl -4 -fsS https://api.ipify.org)

if [[ -z "$CURRENT_IP" ]]; then
    echo "Unable to get current public IP."
    exit 1
fi

echo "Current Public IP: ${CURRENT_IP}"

# ============================================================
# 2. Query the A record from GoDaddy API v3
#
# GET /v3/domains/zones/{zone}/dns-records
# ?type=A&name=@
# ============================================================

DNS_RESPONSE=$(curl -4 -fsS \
    -H "Authorization: Bearer ${GODADDY_PAT}" \
    -H "Accept: application/json" \
    "${BASE_URL}?type=A&name=${RECORD_NAME}")

echo "GoDaddy Response: ${DNS_RESPONSE}"

# ============================================================
# 3. Get DNS record information
# ============================================================

RECORD_COUNT=$(echo "$DNS_RESPONSE" | jq '.items | length')

if [[ "$RECORD_COUNT" -eq 0 ]]; then
    echo "DNS A record not found."
    echo "Domain : ${DOMAIN}"
    echo "Record : ${RECORD_NAME}"
    exit 1
fi

if [[ "$RECORD_COUNT" -gt 1 ]]; then
    echo "Warning: Multiple A records found."
    echo "Using the first record."
fi

RECORD_ID=$(echo "$DNS_RESPONSE" | jq -r '.items[0].recordId')
DNS_IP=$(echo "$DNS_RESPONSE" | jq -r '.items[0].data')
DNS_NAME=$(echo "$DNS_RESPONSE" | jq -r '.items[0].name')
DNS_TYPE=$(echo "$DNS_RESPONSE" | jq -r '.items[0].type')
DNS_TTL=$(echo "$DNS_RESPONSE" | jq -r '.items[0].ttl')

echo "Record ID       : ${RECORD_ID}"
echo "DNS Name        : ${DNS_NAME}"
echo "DNS Type        : ${DNS_TYPE}"
echo "GoDaddy DNS IP  : ${DNS_IP}"
echo "GoDaddy DNS TTL : ${DNS_TTL}"

# ============================================================
# 4. The IP address has not changed, so no update is required
# ============================================================

if [[ "$CURRENT_IP" == "$DNS_IP" ]]; then
    echo "IP is unchanged. Nothing to do."
    exit 0
fi

# ============================================================
# 5. The IP address has changed
# ============================================================

echo ""
echo "IP changed!"
echo "${DNS_IP} -> ${CURRENT_IP}"

# ============================================================
# 6. Replace the DNS record using GoDaddy API v3
#
# PUT /v3/domains/zones/{zone}/dns-records/{recordId}
#
# API v3 requires the complete record information:
# name
# type
# data
# ttl
# ============================================================

RECORD_URL="${BASE_URL}/${RECORD_ID}"

REQUEST_BODY=$(jq -n \
    --arg name "$DNS_NAME" \
    --arg type "$DNS_TYPE" \
    --arg data "$CURRENT_IP" \
    --argjson ttl "$TTL" \
    '{
        name: $name,
        type: $type,
        data: $data,
        ttl: $ttl
    }')

echo ""
echo "Update Request:"
echo "$REQUEST_BODY"

# ============================================================
# 7. Send the DNS update request
# ============================================================

HTTP_CODE=$(curl -4 -sS \
    -o /tmp/godaddy-ddns-response.json \
    -w "%{http_code}" \
    -X PUT \
    -H "Authorization: Bearer ${GODADDY_PAT}" \
    -H "Accept: application/json" \
    -H "Content-Type: application/json" \
    "${RECORD_URL}" \
    -d "${REQUEST_BODY}")

# ============================================================
# 8. Check the result
# ============================================================

if [[ "$HTTP_CODE" == "200" ]]; then

    echo ""
    echo "DNS update successful."

    UPDATED_IP=$(jq -r '.data // empty' \
        /tmp/godaddy-ddns-response.json)

    echo "${DNS_NAME}.${DOMAIN} -> ${UPDATED_IP}"

else

    echo ""
    echo "DNS update failed."
    echo "HTTP Status: ${HTTP_CODE}"

    if [[ -s /tmp/godaddy-ddns-response.json ]]; then
        echo "Response:"
        cat /tmp/godaddy-ddns-response.json
    fi

    exit 1
fi

賦予 Script 執行權限

sudo chmod +x /usr/local/bin/godaddy-ddns.sh

Script 需要 jq 解析 GoDaddy API 回傳的 JSON。

sudo apt update
sudo apt install -y curl jq

先手動測試,由於 IP 沒有變動,未進行更新。

sudo /usr/local/bin/godaddy-ddns.sh
========================================
GoDaddy DDNS
Domain : ivancheng.tw
Record : @
========================================
Current Public IP: 1.172.152.170
GoDaddy Response: [{"data":"1.172.152.170","name":"@","ttl":600,"type":"A"}]
GoDaddy DNS IP : 1.172.152.170
IP is unchanged. Nothing to do.

可以先將根網域指向 8.8.8.8 來驗證 Script 是否正常

再次手動測試,這次就正常更新了。

sudo /usr/local/bin/godaddy-ddns.sh
========================================
GoDaddy DDNS
Domain : ivancheng.tw
Record : @
========================================
Current Public IP: 1.172.152.170
GoDaddy Response: [{"data":"8.8.8.8","name":"@","ttl":600,"type":"A"}]
GoDaddy DNS IP : 8.8.8.8
IP changed!
8.8.8.8 -> 1.172.152.170
DNS update successful.
@.ivancheng.tw -> 1.172.152.170

建立 systemd Service

sudo nano /etc/systemd/system/godaddy-ddns.service
[Unit]
Description=GoDaddy Dynamic DNS Update
After=network-online.target
Wants=network-online.target

[Service]
Type=oneshot
ExecStart=/usr/local/bin/godaddy-ddns.sh

建立 Timer

sudo nano /etc/systemd/system/godaddy-ddns.timer
[Unit]
Description=GoDaddy Dynamic DNS Timer

[Timer]
OnBootSec=1min
OnUnitActiveSec=5min
Persistent=true

[Install]
WantedBy=timers.target

啟動 Timer

sudo systemctl daemon-reload
sudo systemctl enable --now godaddy-ddns.timer

查看 Timer 的排程狀態,沒有正常運作與下一次什麼時候執行。

systemctl list-timers godaddy-ddns.timer

應該會看到類似

NEXT                            LEFT LAST                         PASSED UNIT               ACTIVATES
Mon 2026-08-31 00:58:05 CST 4min 32s Mon 2026-08-31 00:53:05 CST 27s ago godaddy-ddns.timer godaddy-ddns.service

1 timers listed.
Pass --all to see loaded but inactive timers, too.

查看 DDNS 執行紀錄

journalctl -u godaddy-ddns.service
Aug 31 00:53:05 jieshiun systemd[1]: Starting godaddy-ddns.service - GoDaddy Dynamic DNS Update...
Aug 31 00:53:05 jieshiun godaddy-ddns.sh[17500]: ========================================
Aug 31 00:53:05 jieshiun godaddy-ddns.sh[17500]: GoDaddy DDNS
Aug 31 00:53:05 jieshiun godaddy-ddns.sh[17500]: Domain : ivancheng.tw
Aug 31 00:53:05 jieshiun godaddy-ddns.sh[17500]: Record : @
Aug 31 00:53:05 jieshiun godaddy-ddns.sh[17500]: ========================================
Aug 31 00:53:06 jieshiun godaddy-ddns.sh[17500]: Current Public IP: 1.172.152.170
Aug 31 00:53:08 jieshiun godaddy-ddns.sh[17500]: GoDaddy Response: [{"data":"1.172.152.170","name":"@","ttl":600,"type":"A"}]
Aug 31 00:53:08 jieshiun godaddy-ddns.sh[17500]: GoDaddy DNS IP : 1.172.152.170
Aug 31 00:53:08 jieshiun godaddy-ddns.sh[17500]: IP is unchanged. Nothing to do.
Aug 31 00:53:08 jieshiun systemd[1]: godaddy-ddns.service: Deactivated successfully.
Aug 31 00:53:08 jieshiun systemd[1]: Finished godaddy-ddns.service - GoDaddy Dynamic DNS Update.

GoDaddy 目前 DNS API 的 TTL 最低是 600 秒,所以即使 Ubuntu 5 分鐘偵測一次,外部 DNS 快取仍可能讓變更在部分 Resolver 上延遲約 10 分鐘以上。

今天的分享就到這邊,感謝收看。

參考文件

https://developer.godaddy.com/en/docs/api-users/concepts/how-godaddy-apis-work

https://developer.godaddy.com/en/docs/api-users/domains/manage/dns