Dynamic DNS via Digital Ocean API

This is a quick tutorial about setting up a simple shell script that runs periodically to update a DNS record on the internet with the IP address of your network. If you’ve ever wanted to have myhome.mydomain.com always updated with your home IP address, this is one way you can do it. There are paid and free services that offer this functionality, but I choose to use this method because it gives me ultimate flexibility over my domain.
This tutorial assumes you have your own Top Level Domain (TLD) and that you’ve configured it to use Digital Ocean as the authoritative DNS for it. Here, I’ll use “example.com” as the top level domain.

Generate a Personal Access Token

Log in to Digital Ocean and click on API from the top Menu. Click the Generate New Token button and give it a name. The name is not parsed, it’s just for your own reference. I like to give it something descriptive to my application. For this example, I’ll use MyDNS. Be sure to check the box next to Write so we can update the records using this API Key.

Once you have the API Key, save it somewhere for reference. It’s one of the 3 variables we’ll need to update in the update_do_dns.sh script.

The API Key will look something like this:
fe3aeda96b7wer8wer1e6bb5erae528sdf3a6120dfrf7e492bwer6343fsdf

The domain value will be your Top Level Domain (TLD) or base domain name. Here I’m using
example.com

Now you need the record ID that you want to update. To get this record ID, create an entry in the Digital Ocean Control Panel: Networking -> Domains page. You should see your TLD listed. Click on it and you will see the records that are defined. If you want to use myhome.example.com as your name, you’ll create an A Record. This record type is just a straight name to IP mapping.

At this point, you can give it whatever IP you want. Our script will be updating it automatically later. We just want to define the name and generate a RecordID.

Getting the RecordID

To get the RecordID, you run the view_do_dns.sh script and look for the record you’re going to be updating. In this case, we’ll use myhome.example.com. After running the script, you should see output like this:

1
2
3
4
5
6
7
25749301  @                NS     ns1.digitalocean.com
25749302 @ NS ns2.digitalocean.com
25749309 www A 192.30.252.154
29016685 @ A 192.30.252.153
29016687 www A 192.30.252.153
29908465 mediaserver A 67.205.170.54
30979354 myhome A 72.91.214.201

Here we can see the myhome record. It’s ID is value in the first column: 30979354. We’ll use this value in the RecordID variable of our update_do_dns.sh script.

The Scripts

view_do_dns.sh

1
2
3
4
5
6
7
#!/bin/bash

# Digital Ocean variables
domain="example.com"
api_key="71684ce442234b7e2364r3e23e1f9beef0c2040342ef6197acb3461"

$(which curl) -ks -X GET -H "Content-Type: application/json" -H "Authorization: Bearer 71684ce442234b7e2364r3e23e1f9beef0c2040342ef6197acb3461" "https://api.digitalocean.com/v2/domains/example.com/records" | jq -r '.domain_records[] | "\(.id) \(.name) \(.type) \(.data)"' | column -t

update_do_dns.sh

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#!/bin/bash

# Digital Ocean variables
domain="example.com"
record_id="30979354" # Get this value from the view_do_dns.sh script output
api_key="fe3aeda96b7wer8wer1e6bb5erae528sdf3a6120dfrf7e492bwer6343fsdfewerewr"
# -----------------------

echo "Checking IP for change on: $(date)"
curip=$(cat /tmp/ip.txt)
ip=$(/usr/bin/curl -ks https://wtfismyip.com/text)
echo "Got IP: ($ip) from wtfismyip.com. Existing IP is: ($curip)"

# if IP is different
if [ "$ip" != "$curip" ]; then
# put our new IP into a file to compare against next run
echo $ip > /tmp/ip.txt
echo "Updating Digital Ocean."
/usr/bin/curl -ks -X PUT -H "Content-Type: application/json" -H "Authorization: Bearer $api_key" -d "{\"data\":\"$ip\"}" "https://api.digitalocean.com/v2/domains/$domain/records/$record_id"
#echo '/usr/bin/curl -X PUT -H "Content-Type: application/json" -H "Authorization: Bearer $api_key" -d "{\"data\":\"$curip\"}" "https://api.digitalocean.com/v2/domains/$domain/records/$record_id"'
echo "Done."
else
echo "IP hasn't changed. Not updating."
fi

Sample output

When you run the script it will indicate if the DNS entry was updated or not.

DNS Record was updated

1
2
3
4
5
Checking IP for change on: Thu May  3 22:45:01 EDT 2018
Got IP: (71.90.204.251) from wtfismyip.com. Current IP is: (97.81.21.217)
Updating Digital Ocean.
{"domain_record":{"id":30972423354,"type":"A","name":"home","data":"73.91.224.151","priority":null,"port":null,"ttl":3600,"weight":null,"flags":null,"tag":null}}
Done.

DNS Record was NOT updated

1
2
3
Checking IP for change on: Fri May  4 00:45:01 EDT 2018
Got IP: (72.93.214.125) from wtfismyip.com. Current IP is: (72.93.214.125)
IP hasn't changed. Not updating.

Scheduling the task

Don’t run your IP script too frequently because it can piss off your provider by abusing their service. I set mine to 5 minutes. I feel this is a good balance between getting frequent updates and not being an asshole.

Create a crontab entry like so:

1
*/5	*	*	*	*	/path/to/update_do_dyndns.sh >> /tmp/update_do_dns.txt 2>&1

Using your new dynamic DNS entry(s)

If this script is run from an always on computer or server within your network, it will update the DNS A Record for the name you selected at the interval you request. It may take some time for the DNS changes to propagate to the Master Zone files on the internet. This isn’t a high resolution solution, so if you need something faster you’ll have to write something yourself that lowers the TTL or use a paid or enterprise grade solution.

The uses of this new Dynamic DNS entry are endless. You can use it to host a website on your home network, run a Sonarr or Plex instance, or use it as your VPN endpoint.

1
myhome.example.com

Digital Ocean API Documentation

Author

Chris Bergeron

Posted on

05-04-2018

Updated on

03-31-2023

Licensed under

Comments