Published 2022-01-30.
Last modified 2022-06-30.
Time to read: 2 minutes.
Now that I have fiber-optic internet service in my apartment, with 500 GB/s upload and download,
I thought I would save money by hosting my scalacourses.com
website on an Ubuntu server that runs here,
instead of AWS.
My home IP address is quite stable, and only changes when the fiber modem boots up.
The modem is branded as a
Bell Home Hub 4000,
but I believe it is actually made by Arris (formerly known as Motorola).
It makes little sense to pay the commercial cost of dedicated dynamic DNS services (typically $55 USD / year) when it is so easy to automate, and the operational cost is less than one cent per year.
I wrote two little scripts that automatically check my public IP address, and modifies the DNS record for my home IP address whenever the IP address changes. One script is for sites that use DNS provided by AWS Route53, and the other is for DNS provided by Namecheap.
The approach shown here could be used for all DNS servers that have a command-line interface. This was originally written for AWS Route53, but when I moved off AWS I made a version for Namecheap. Alternative DNS providers include Azure DNS, Cloudflare DNS, DNSMadeEasy, DNSimple, Google Cloud DNS, and UltraDNS.
Forwarding HTTP Requests
I added some entries to the modem so incoming HTTP traffic on ports 80 and 443 would be forwarded to ports
9000 and 9443 on my home server, gojira
.
Using the Scripts
The version for AWS Route53 is called dynamicDnsAws
,
and the version for Namecheap is called dynamicDnsNamecheap
.
The scripts save the IP address to a file, and periodically compare the saved value to the current value. Then the scripts modify the DNS record for a specified subdomain whenever the value of the public IP address changes.
Using the AWS Script
Here is the help information for the script:
$ dynamicDnsAws dynamicDnsAws - Maintains a dynamic DNS record in AWS Route53
Saves data in '/home/mslinn/.dynamicDnsAws'
Syntax: dynamicDnsAws [OPTIONS] SUB_DOMAIN DOMAIN
OPTIONS: -v Verbose mode
Example usage: dynamicDnsAws www scalacourses.com dynamicDnsAws -v www scalacourses.com
Here is a sample usage:
$ dynamicDnsAws www scalacourses.com { "ChangeInfo": { "Id": "/change/C075751811HI18SH4L8L0", "Status": "PENDING", "SubmittedAt": "2022-01-30T21:10:09.261Z", "Comment": "UPSERT a record for www.scalacourses.com" } }
Using the Namecheap Script
Here is the help information for the script:
$ dynamicDnsNamecheap dynamicDnsNamecheap - Maintains two Namecheap dynamic DNS records
Saves data in '/home/mslinn/.dynamicDns'
Syntax: dynamicDnsNamecheap [OPTIONS] DOMAIN PASSWORD
OPTIONS: -v Verbose mode
Example usage: dynamicDnsNamecheap mydomain.com asdfasdfasdfasdfasdf dynamicDnsNamecheap -v mydomain.com asdfasdfasdfasdf
Here is sample usage:
$ dynamicDnsNamecheap scalacourses.com asdfasdfasdfasdfasdf <?xml version="1.0" encoding="utf-16"?> <interface-response> <Command>SETDNSHOST</Command> <Language>eng</Language> <IP>142.126.4.220</IP> <ErrCount>0</ErrCount> <errors /> <ResponseCount>0</ResponseCount> <responses /> <Done>true</Done> <debug><![CDATA[]]></debug> </interface-response><?xml version="1.0" encoding="utf-16"?> <interface-response> <Command>SETDNSHOST</Command> <Language>eng</Language> <IP>142.126.4.220</IP> <ErrCount>0</ErrCount> <errors /> <ResponseCount>0</ResponseCount> <responses /> <Done>true</Done> <debug><![CDATA[]]></debug> </interface-response>
Invoking the Scripts from Crontab
A personal crontab
can be modified by typing:
$ crontab -e
I pasted in the following into crontab
on my Ubuntu server, running at home.
These lines invoke the dynamicDnsNamecheap
script via crontab
every 5 minutes.
*/5 * * * * /path/to/dynamicDnsNamecheap my_domain.com asdfasdfasdfasdfasdf
One the above is saved, crontab
will run the script every 5 minutes.
Script Source Codes
Here are the bash scripts:
#!/bin/bash # Author: Mike Slinn mslinn@mslinn.com # Written 2022-01-30 export SAVE_FILE_NAME="$HOME/.dynamicDns" function help { echo "$( basename $0 ) - Maintains a dynamic DNS record in AWS Route53 Saves data in '$SAVE_FILE_NAME' Syntax: $( basename $0) [OPTIONS] SUB_DOMAIN DOMAIN OPTIONS: -v Verbose mode Example usage: $( basename $0) my_subdomain mydomain.com $( basename $0) -v my_subdomain mydomain.com " exit 1 } function upsert { export HOSTED_ZONES="$( aws route53 list-hosted-zones )" export HOSTED_ZONE_RECORD="$( jq -r ".HostedZones[] | select(.Name == \"$DOMAIN.\")" <<< "$HOSTED_ZONES" )" export HOSTED_ZONE_RECORD_ID="$( jq -r .Id <<< "$HOSTED_ZONE_RECORD" )" aws route53 change-resource-record-sets \ --hosted-zone-id "$HOSTED_ZONE_RECORD_ID" \ --change-batch "{ \"Comment\": \"UPSERT a record for $SUBDOMAIN.$DOMAIN\", \"Changes\": [{ \"Action\": \"UPSERT\", \"ResourceRecordSet\": { \"Name\": \"$SUBDOMAIN.$DOMAIN\", \"Type\": \"A\", \"TTL\": 300, \"ResourceRecords\": [{ \"Value\": \"$IP\"}] } }] }" echo "$IP" > "$SAVE_FILE_NAME" } if [ "$1" == -v ]; then export VERBOSE=true shift fi if [ -z "$2" ]; then help; fi set -e export SUBDOMAIN="$1" export DOMAIN="$2" export IP="$( dig +short myip.opendns.com @resolver1.opendns.com )" if [ ! -f "$SAVE_FILE_NAME" ]; then if [ "$VERBOSE" ]; then echo "Creating $SAVE_FILE_NAME"; fi upsert; elif [ $( cat "$SAVE_FILE_NAME" ) != "$IP" ]; then if [ "$VERBOSE" ]; then echo "Updating $SAVE_FILE_NAME" echo "'$IP' was not equal to '$( cat "$SAVE_FILE_NAME" )'" fi upsert; else if [ "$VERBOSE" ]; then echo "No change necessary for $SAVE_FILE_NAME"; fi fi
#!/bin/bash # Author: Mike Slinn mslinn@mslinn.com # Modified from AWS version (dynameicDnsAws) 2022-06-30 # See https://www.namecheap.com/support/knowledgebase/article.aspx/36/11/how-do-i-start-using-dynamic-dns/ export SAVE_FILE_NAME="$HOME/.dynamicDns" function help { echo "$( basename $0 ) - Maintains two Namecheap dynamic DNS records Saves data in '$SAVE_FILE_NAME' Syntax: $( basename $0) [OPTIONS] DOMAIN PASSWORD OPTIONS: -v Verbose mode Example usage: $( basename $0) mydomain.com asdfasdfasdfasdfasdf $( basename $0) -v mydomain.com asdfasdfasdfasdf " exit 1 } function upsert { curl "https://dynamicdns.park-your-domain.com/update?host=@&domain=$DOMAIN&password=$PASSWORD&ip=$IP" curl "https://dynamicdns.park-your-domain.com/update?host=www&domain=$DOMAIN&password=$PASSWORD&ip=$IP" echo "$IP" > "$SAVE_FILE_NAME" echo "" } if [ "$1" == -v ]; then export VERBOSE=true shift fi if [ -z "$2" ]; then help; fi set -e export DOMAIN="$1" export PASSWORD="$2" export IP="$( dig +short myip.opendns.com @resolver1.opendns.com )" if [ ! -f "$SAVE_FILE_NAME" ]; then if [ "$VERBOSE" ]; then echo "Creating $SAVE_FILE_NAME"; fi upsert; elif [ "$( cat "$SAVE_FILE_NAME" )" != "$IP" ]; then if [ "$VERBOSE" ]; then echo "Updating $SAVE_FILE_NAME" echo "'$IP' was not equal to '$( cat "$SAVE_FILE_NAME" )'" fi upsert; else if [ "$VERBOSE" ]; then echo "No change necessary for $SAVE_FILE_NAME"; fi fi