You have a domain with a zone hosted on your own DNS server (see my HOWTO on self-hosting DNS). You want a subdomain that you can update programmatically with changes reflected in seconds.
My use case is ephemeral AWS instances that need to be available at a public domain name right after launching and domain should disappear right after instance terminates.
You can’t use your main zone because it points to secondary nameservers, you can’t update them too often and can’t wait for changes to propagate. Instead you can delegate that subdomain to your DNS server (instead of secondaries) and set a very low TTL.
Bind9 config
We’ll use site.org as an example and run.site.org as the dynamic subdomain. Bind9 is running at site.org. In your zone file:
$ORIGIN site.org.
@ IN SOA site.org. root.site.org.
...
@ NS ns1.he.net.
@ NS ns2.he.net.
run NS site.org. ; Delegate run.site.org to our DNS server
Now any recursive DNS resolver will query your DNS server instead of secondaries for anything at run.site.org. Create a zone in /var/lib/bind/db.run.site.org – this is the expected location for dynamic zone files.
$ORIGIN run.site.org.
$TTL 30 ; TTL of 30 seconds
@ IN SOA site.org. root.site.org. (
1 ; serial
600 ; irrelevant
600 ; irrelevant
30 ; irrelevant
30 ; "not found" response TTL
)
Add it to bind config at /etc/bind/named.conf.local:
zone "run.site.org" {
type master;
file "/var/lib/bind/db.run.site.org"
// Make domain dynamic and allow changes from localhost.
update-policy local;
}
Read Bind 9 Config Reference for explanation of the update-policy setting and how to allow update queries from other machines.
systemctl reload bind9
Don’t edit dynamic zones by hand!
When a zone is dynamic, bind creates a journal file at db.run.site.org.jnl that keeps all changes and gets written to the main zone file once in a while. If you edit the zone file by hand, journaling will break! If you want to do that, freeze the zone first:
rndc freeze run.site.org # writes journal to zone file and suspends dynamic updates
# now edit the zone file
rndc thaw run.site.org # re-enable journal and updates
Use nsupdate to make zone changes
Read man nsupdate to understand how to use it.
Add a record to your zone from the command line (1 is one-second TTL):
$ cat | nsupdate -l
zone run.site.org.
add run.site.org. 1 A 1.1.1.1
send
CTRL+D
Note that you can’t use relative domain names or @ in the add statement. Examples of common tasks:
# Inside a bash script
# Get IP
IP=`aws ec2 describe-instances --instance-ids=$INSTANCE_ID |
jq -r '.Reservations[].Instances[].PublicIpAddress'`
# Add record.
nsupdate -l <<EOL
zone run.site.org.
add run.site.org. 1 A $IP
send
EOL
# Remove record.
nsupdate -l <<EOL
zone run.site.org.
delete run.site.org.
send
EOL
# Update record: note how you have to delete it first,
# otherwise you'll end up with two records!
nsupdate -l <<EOL
zone run.site.org.
delete run.site.org.
add run.site.org. 1 A $IP
send
EOL
Results
Test your new domain with watch dig run.site.org. You’ll notice that it updates within a few seconds (unless your ISP caches 1 second TTL domains, which is rare). Try deleting the record and see how quickly it becomes NXDOMAIN.
The cost you have to pay is that every query takes longer, since it has to hit your DNS server every time. Depending on ping it can take close to 300ms! Set the TTL for your run.site.org. NS site.org. record to a large value, since that’s unlikely to change. That should make query times a bit better.