最近不小心手滑買了一個 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
