Dynamic DNS with Gandi

I use Gandi for my domain names. For a while, I used a VPS to host all my sites, but I ran into technical issues (it was OpenVZ-based so the kernel was ancient) and after I upgraded my home internet, I decided to put my bandwidth to use and host everything at home.

Like many other North Americans, my ISP doesn’t give me a static IP, so this is an issue. You can use dynamic DNS providers like noip or afraid.org, but then you’re stuck with a subdomain on one of their owned domain names. Fortunately, Gandi has something called LiveDNS, which provides an HTTP REST API for modifying zonefiles. Here’s how I’m using their API to point my DNS records at my dynamic IP.

First, follow the instructions and visit the ‘Security’ page to get an API key. This key will need to be provided in the X-Api-Key header of all your requests. Next, figure out the URL for your site’s A (or AAAA) record.

  1. Fetch all zones, and select the zone for your domain. Copy the zone_records_href:

     $ curl -H "X-Api-Key: YOUR_API_KEY_HERE" https://dns.api.gandi.net/api/v5/zones
     [
       {
         "zone_records_href": "https://dns.api.gandi.net/api/v5/zones/......../records",
         ...
         "name": "qali.net"
       }
     ]
    
  2. List all records for your domain’s zonefile:

     $ curl -H "X-Api-Key: YOUR_API_KEY_HERE" https://dns.api.gandi.net/api/v5/zones/......../records 
     [
       {
         "rrset_type": "A",
         "rrset_ttl": 10800,
         "rrset_name": "@",
         "rrset_href": "https://dns.api.gandi.net/api/v5/zones/......../records/%40/A",
         "rrset_values": [
           "1.2.3.4"
         ]
       },
       {
         "rrset_type": "AAAA",
         "rrset_ttl": 1800,
         "rrset_name": "@",
         "rrset_href": "https://dns.api.gandi.net/api/v5/zones/......../records/%40/AAAA",
         "rrset_values": [
           "i:wish:i:had:an:ipv6:address"
         ]
       }
     ]
    
  3. Now that you have a URL for your A/AAAA records, you just need to PUT to their URLs. The payload for a given name/record type pair is a TTL and value (make sure you choose a low TTL, as this determines the maximum possible downtime for your site once your IP changes):

     $ curl -X PUT -d '{"rrset_ttl": 600, "rrset_values": ["1.1.1.1"]}' -H 'X-Api-Key: YOUR_API_KEY_HERE' https://dns.api.gandi.net/api/v5/zones/......../records/%40/A
    

Now that we know the URL of the record to update, we can write a script that, when run, fetches our current IP and updates the record accordingly:

#!/usr/bin/env bash

api_key_header="X-Api-Key: UPDATEME"
record_url="https://dns.api.gandi.net/api/v5/zones/......../records/%40/A"
curr_ip=$(curl ifconfig.me)
payload="{\"rrset_ttl\": 600, \"rrset_values\": [\"$curr_ip\"]}"

echo payload: $payload

curl -X PUT -H "Content-Type: application/json" -H "$api_key_header" -d "$payload" $record_url
if [ $? -ne 0 ]; then
    echo POST failed
    exit 1
fi

Running this on a schedule can be done with cron, or with systemd timers. Make sure that the timer frequency matches the TTL used for your DNS record. Also, keep your API key safe. Don’t add it to a VCS, and make sure that the updater script can only be read by the user scheduled to run it (i.e. chmod go-rwx update-dns.sh).