BIND, the venerable DNS server, can keep your dynamic IP address in sync with your DNS records using Dynamic DNS (DDNS).

Let’s see BIND in action. Imagine you have a home server with a dynamic IP address assigned by your ISP. You want to access it reliably using a hostname like myhome.example.com. DDNS allows your home server to automatically update the DNS record for myhome.example.com whenever its IP address changes.

Here’s a simplified view of the process:

  1. Client Initiates Update: Your home server (the DDNS client) detects a change in its public IP address.
  2. Client Sends Update Request: The client constructs a DNS update message containing the new IP address and sends it to your authoritative DNS server for example.com (which is running BIND).
  3. BIND Receives Update: BIND, configured to accept updates for example.com, receives the request.
  4. BIND Validates Update: BIND checks if the request is authorized (usually via a shared secret or TSIG key).
  5. BIND Updates Zone: If authorized, BIND modifies its in-memory zone data for example.com to reflect the new IP address.
  6. BIND Writes to Disk: BIND periodically writes the updated zone data back to its zone file on disk.
  7. Client Receives Response: BIND sends a success or failure response back to the client.

To set this up, you’ll need two main components: the BIND server and a DDNS client on your home server.

BIND Server Configuration

On your BIND server (let’s say it’s authoritative for example.com), you need to enable dynamic updates.

1. Enable Updates in named.conf:

In your main BIND configuration file (named.conf or a file it includes), you need to grant update privileges for your zone.

zone "example.com" IN {
    type master;
    file "db.example.com";
    allow-update { key "rndc-key"; }; // Or an IP address/subnet
};
  • allow-update { key "rndc-key"; };: This is the crucial part. It specifies which clients are allowed to send update requests. Using a key is the most secure method. You’ll generate this key shortly. Alternatively, you could use allow-update { 192.168.1.0/24; }; for a specific subnet, but this is less secure.

2. Generate a TSIG Key:

You’ll use a TSIG (Transaction Signature) key for secure authentication between the client and the server.

dnssec-keygen -a HMAC-SHA256 -b 128 -n HOST example.com -r /dev/urandom update-key

This command generates two files: Kupdate-key.+157+00000.key and Kupdate-key.+157+00000.private. You’ll need the content of the .key file.

3. Include the Key in named.conf:

Add the key definition to your named.conf file.

key "rndc-key" {
    algorithm hmac-sha256;
    secret "YOUR_BASE64_ENCODED_SECRET_FROM_KEY_FILE";
};

Replace "YOUR_BASE64_ENCODED_SECRET_FROM_KEY_FILE" with the actual secret string from the Kupdate-key.+157+00000.key file (it’s the long string after secret:).

4. Restart BIND:

After modifying named.conf, restart your BIND service for the changes to take effect.

sudo systemctl restart named

DDNS Client Configuration

On your home server, you’ll use a DDNS client. nsupdate is a common command-line tool that comes with BIND.

1. Create an nsupdate Script:

Create a script (e.g., update-ddns.sh) to handle the updates.

#!/bin/bash

# --- Configuration ---
DDNS_SERVER="ns1.example.com"       # Your BIND server's FQDN
ZONE="example.com"                  # The zone being updated
HOSTNAME="myhome.example.com"       # The hostname to update
KEY_FILE="/etc/bind/update-key.key" # Path to your TSIG key file
IP_FILE="/tmp/current_ip.txt"       # File to store the current IP
LOG_FILE="/var/log/ddns-update.log" # Log file

# --- Get Public IP ---
# Use a reliable service to get your current public IP
CURRENT_IP=$(curl -s "https://api.ipify.org")
if [ -z "$CURRENT_IP" ]; then
    echo "$(date): Failed to retrieve public IP." >> "$LOG_FILE"
    exit 1
fi

# --- Check if IP has changed ---
if [ -f "$IP_FILE" ]; then
    LAST_IP=$(cat "$IP_FILE")
    if [ "$CURRENT_IP" == "$LAST_IP" ]; then
        echo "$(date): IP address has not changed ($CURRENT_IP)." >> "$LOG_FILE"
        exit 0
    fi
fi

# --- Perform DNS Update ---
echo "$(date): Updating IP for $HOSTNAME to $CURRENT_IP" >> "$LOG_FILE"

nsupdate -y hmac-sha256:update-key -k "$KEY_FILE" <<EOF
server $DDNS_SERVER
zone $ZONE
update delete $HOSTNAME A
update add $HOSTNAME 300 A $CURRENT_IP
send
EOF

# Check nsupdate exit status
if [ $? -eq 0 ]; then
    echo "$CURRENT_IP" > "$IP_FILE"
    echo "$(date): Update successful. IP saved to $IP_FILE." >> "$LOG_FILE"
else
    echo "$(date): nsupdate command failed." >> "$LOG_FILE"
fi

exit 0

2. Prepare the Key File for nsupdate:

The nsupdate command needs the key in a specific format. Create a file (e.g., /etc/bind/update-key.key) with the following content:

key "rndc-key" {
    algorithm hmac-sha256;
    secret "YOUR_BASE64_ENCODED_SECRET_FROM_KEY_FILE";
};

Again, replace "YOUR_BASE64_ENCODED_SECRET_FROM_KEY_FILE" with the actual secret. Ensure this file has restricted permissions: chmod 600 /etc/bind/update-key.key.

3. Make the Script Executable:

chmod +x update-ddns.sh

4. Automate the Script:

Use cron to run this script periodically (e.g., every 15 minutes).

crontab -e

Add the following line:

*/15 * * * * /path/to/your/update-ddns.sh

How it works mechanically: The nsupdate command constructs a DNS UPDATE message. The update delete $HOSTNAME A command removes any existing A record for myhome.example.com. The update add $HOSTNAME 300 A $CURRENT_IP command then adds a new A record for myhome.example.com with a TTL of 300 seconds (5 minutes) pointing to the new IP address. The TSIG key ensures that only authorized clients can perform these updates.

The most overlooked detail is often the algorithm and secret matching between named.conf and the client’s key file. A mismatch here will cause the update to fail silently or with an REFUSED error.

Once this is all set up, the next thing you’ll likely want to tackle is how to secure your DNS zone files themselves with DNSSEC.

Want structured learning?

Take the full Bind course →