Skip to content

Instantly share code, notes, and snippets.

@besmirzanaj
Last active March 13, 2025 09:40
Show Gist options
  • Save besmirzanaj/490c7f8ff61f6e9681fa5656220e3910 to your computer and use it in GitHub Desktop.
Save besmirzanaj/490c7f8ff61f6e9681fa5656220e3910 to your computer and use it in GitHub Desktop.
synch two technitium servers
#!/bin/bash
# Author: Besmir Zanaj, 2024
# This is a very raw script to backup configs (no logs and no stats) from a technitium server
# to another
#
# first create two tokens: one on the source server and another one on the destination one
# fill out the vars below
# create a cronjob with this script on the destinaton host
# eg:
# 30 */6 * * * /path-to/technitium-sync.sh
set -euxo pipefail
src_dns_server='source.ip.address'
dst_dns_server='dest.ip.address'
src_dns_serverdomain='fqdn.of.source.server'
dst_dns_serverdomain='fqdn.of.dest.server'
src_dns_token='SOURCE_TECHNITIUM_TOKEN_HERE'
dst_dns_token='DEST_TECHNITIUM_TOKEN_HERE'
backup_file="/tmp/technitium-backup.zip"
# update the dhcp scope as per your local settings
dhcp_scope_name="local-home"
# Ensure required tools are installed
command -v curl >/dev/null 2>&1 || { echo "curl is not installed. Aborting." >&2; exit 1; }
# Check the primary server's health before running the script
echo "Checking primary Technitium server status"
status_code=$(curl --write-out %{http_code} --silent --output /dev/null http://$src_dns_server:5380)
if [[ "$status_code" -ne 200 ]] ; then
echo "Primary DNS server is not available. Skipping backup"
exit 1
else
echo "Getting the backup archive from the primary server"
curl -s "http://$src_dns_server:5380/api/settings/backup?token=$src_dns_token&blockLists=true&logs=false&scopes=true&stats=false&zones=true&allowedZones=true&blockedZones=true&dnsSettings=true&logSettings=true&authConfig=true&apps=true" -o $backup_file
fi
# restore_backup
if [[ -f "$backup_file" ]]; then
echo "Restoring the backup on $HOSTNAME"
curl -s --form file="@$backup_file" "http://$dst_dns_server:5380/api/settings/restore?token=$dst_dns_token&blockLists=true&logs=true&scopes=true&stats=true&apps=true&zones=true&allowedZones=true&blockedZones=true&dnsSettings=true&logSettings=true&deleteExistingFiles=true&authConfig=true" --output /dev/null
# wait for server to come back
echo "Waiting for 10 seconds for the destination server to start up"
sleep 10
# set dnsServerDomain on destination server
echo "Updating DNS server Domain in destination server"
curl -X POST "http://$dst_dns_server:5380/api/settings/set?token=$dst_dns_token&dnsServerDomain=$dst_dns_serverdomain"
# disable DHCP on the destination server
echo "disabling DHCP in destination server"
curl -X POST "http://$dst_dns_server:5380/api/dhcp/scopes/disable?token=$dst_dns_token&name=$dhcp_scope_name"
# cleanup
echo "Cleaning up temporary files"
rm -rf $backup_file
fi
@cicatrix87
Copy link

To make the script more versatile you could add another variable for the DHCP Scope, as not everyone is using "local-home". Thanks for your effort with the script itself.

@besmirzanaj
Copy link
Author

besmirzanaj commented Jul 1, 2024

Hey @cicatrix87 this is done. You can use now use the variable dhcp_scope_name.
Thanks for the feedback

@Naldjer
Copy link

Naldjer commented Dec 12, 2024

Hey mate,

Could you also create one which could run on a Truenas server?

Many thanks!

@Cicatr1x
Copy link

Cicatr1x commented Dec 12, 2024

Hey mate,

Could you also create one which could run on a Truenas server?

Many thanks!

try to run this code

#!/bin/sh

# Author: Besmir Zanaj, 2024

# This script syncs Technitium server configs between servers.
# Place this script in a location accessible to the TrueNAS cron system.
# Configure permissions and ensure required binaries are installed (e.g., curl).

set -eux

src_dns_server='source.ip.address'
dst_dns_server='dest.ip.address'
src_dns_serverdomain='fqdn.of.source.server'
dst_dns_serverdomain='fqdn.of.dest.server'
src_dns_token='SOURCE_TECHNITIUM_TOKEN_HERE'
dst_dns_token='DEST_TECHNITIUM_TOKEN_HERE'
backup_file="/tmp/technitium-backup.zip"
dhcp_scope_name="local-home"

# Ensure required tools are installed
command -v curl >/dev/null 2>&1 || { echo "curl is not installed. Aborting." >&2; exit 1; }

# Check the primary server's health
echo "Checking primary Technitium server status"
status_code=$(curl --write-out '%{http_code}' --silent --output /dev/null "http://$src_dns_server:5380")

if [ "$status_code" -ne 200 ]; then
  echo "Primary DNS server is not available. Skipping backup."
  exit 1
else
  echo "Getting the backup archive from the primary server"
  curl -s "http://$src_dns_server:5380/api/settings/backup?token=$src_dns_token&blockLists=true&logs=false&scopes=true&stats=false&zones=true&allowedZones=true&blockedZones=true&dnsSettings=true&logSettings=true&authConfig=true&apps=true" -o "$backup_file"
fi

# Restore backup
if [ -f "$backup_file" ]; then
  echo "Restoring the backup on $HOSTNAME"
  curl -s --form file="@$backup_file" "http://$dst_dns_server:5380/api/settings/restore?token=$dst_dns_token&blockLists=true&logs=true&scopes=true&stats=true&apps=true&zones=true&allowedZones=true&blockedZones=true&dnsSettings=true&logSettings=true&deleteExistingFiles=true&authConfig=true" --output /dev/null

  # Wait for the server to restart
  echo "Waiting for 10 seconds for the destination server to start up"
  sleep 10

  # Update DNS server domain
  echo "Updating DNS server domain on destination server"
  curl -X POST "http://$dst_dns_server:5380/api/settings/set?token=$dst_dns_token&dnsServerDomain=$dst_dns_serverdomain"

  # Disable DHCP on the destination server
  echo "Disabling DHCP on destination server"
  curl -X POST "http://$dst_dns_server:5380/api/dhcp/scopes/disable?token=$dst_dns_token&name=$dhcp_scope_name"

  # Cleanup
  echo "Cleaning up temporary files"
  rm -f "$backup_file"
else
  echo "Backup file not found. Skipping restore."
fi

# End of script

@besmirzanaj
Copy link
Author

I liked the suggestions @Cicatr1x and have included them in the main gist. Thank you!

@Naldjer
Copy link

Naldjer commented Dec 12, 2024

Hey,

At first, many thanks for your super swift reply!

I tried that code, but i get the message 1: set: illigal option -

@Cicatr1x
Copy link

Hey,

At first, many thanks for your super swift reply!

I tried that code, but i get the message 1: set: illigal option -

unfortunatly i dont have a FreeBSD machine to test it right now, can you check which options are available for "set"?

@Naldjer
Copy link

Naldjer commented Dec 12, 2024

Hmm strange. If i do " set -eux" within the shell, i dont get any error
Yet the same command in the script give that error. Also when i try to run the script as root user.

What can i do to check which options are available for "set"?

@Cicatr1x
Copy link

Cicatr1x commented Dec 13, 2024

the Github Comments just ate up the interpreter part at the first line so add this one. I just updated the above code sample.

#!/bin/sh

@Naldjer
Copy link

Naldjer commented Dec 13, 2024

Many thanks again!

Ive tried to use the #!/bin/sh command, and get the following error:

zsh: event not found: /bin/sh

If i run it via:
cd /mnt/Mirrored_disks/apps/
./Backup_Script.sh
I get the message: zsh: ./Backup_Script.sh: bad interpreter: bin/sh/ no such file or directory

@Cicatr1x
Copy link

you can try /bin/bash but i tought FreeBSD has only shell.

@Naldjer
Copy link

Naldjer commented Dec 13, 2024

Now i get the message: zsh: ./Backup_Script.sh: bad interpreter: /bin/bash/ no such file or directory.

I dont get it. when i try to cd to /bin/sh or /bin/bash they do exist, yet i get the message that they dont when i try to run the script.

@Naldjer
Copy link

Naldjer commented Dec 13, 2024

Okay, think i came some further.

So ive changed the scriptpath to /root/Script_name.sh. After that, i done:

sudo -i
cd /root/
./Script_name.sh

Now i get another error message: sudo: process 211477 unexpected status 0x57f
zsh: killed Script_name.sh

@bdorr1105
Copy link

Thanks for putting this thing together, I was sad to see this capability did not exist in technetium. Anyways, I think you could approach it a bit differently. If you put the NS and A record of the the secondary DNS server you can get it to sync the records without the script. That makes this a whole lot less complex. I went and looked at the available API options and focused it enabling and disabling DHCP based on whether the primary server is up.

I modified what you already put together and added some labeling in the script. I also opted to have it run a check every 5 minutes, but may bump that to an hour or so. This makes it pretty HA to me as the secondary automatically sync outside the script. I also went with the docker version as that is way more resilient to Operating System patches and updates and it can be ported to any VM or server

#!/bin/bash

# Author: Besmir Zanaj, 2024
# Modifed By: Brian Dorr
# This script checks the status of the primary Technitium server and enables or disables DHCP 
# on the secondary server based on the primary server's availability.
#
# Fill out the vars below.
# Create a cronjob with this script on the secondary host.
# eg:
# */5 * * * * /root/technitium-dhcp-failover.sh

set -euo pipefail

src_dns_server='primary.dnsserver.ip.address' # Primary server's IP address
dst_dns_server='secondary.dnsserver.ip.address' # Secondary server's IP address
src_dns_token='primay-dns-server-API-token' # Primary server's API
dst_dns_token='primay-dns-server-API-token' # Secondary server's API

# DHCP scopes to manage - puth the name of each scope you have
dhcp_scopes=("scope1" "scope2")  #Use this array for one or many scopes

echo "Checking primary Technitium server status"
status_code=$(curl --write-out '%{http_code}' --silent --output /dev/null http://$src_dns_server:5380)

if [[ "$status_code" -ne 200 ]]; then
  echo "Primary DNS/DHCP server is not available. Enabling DHCP on the secondary server."
  action="enable"
else
  echo "Primary DNS/DHCP server is available. Disabling DHCP on the secondary server."
  action="disable"
fi

for scope in "${dhcp_scopes[@]}"; do
  echo "Executing API call to $action DHCP scope: $scope"
  response=$(curl -X POST "http://$dst_dns_server:5380/api/dhcp/scopes/$action?token=$dst_dns_token&name=$scope" \
    --silent --write-out "%{http_code}" -o response_body.txt)

  echo "HTTP response code: $response"
  if [[ "$response" == "200" ]]; then
    echo "Successfully $action DHCP for scope: $scope"
  else
    echo "Failed to $action DHCP for scope: $scope. Check the response body for details."
  fi

  echo "Response body:"
  cat response_body.txt
done

@besmirzanaj
Copy link
Author

I Like this solution, just need to make sure that the primary DNS server is hosting the zone(s) as "Primary" and the secondary server is configured to host the zone(s) as Secondary. This will need further explanation or tutorials from my original blog post, or re-write it (Which I think would be best)

Docs: https://blog.technitium.com/2022/06/how-to-self-host-your-own-domain-name.html

I tried the Primary, Secondary guide from the article above and it worked flawlessly (I assume it worked for you as well since you proposed the solution :) )

Will have to find the time to properly write up this. Thank you for the contribution

@ShauneBoy
Copy link

ShauneBoy commented Jan 14, 2025

Just what I've been looking for (having just moved from a combination of pi-hole and the DNS server running on my NAS - mainly forced to move as NAS died so thought I would "do it properly") - DNS Syncing is working a treat (having read the above) - setup as Master and Secondary zones but have a couple of questions...

Can you (easily) just replicate the DHCP settings? As think this would complete the setup... The original sync script sync'd everything - whilst the new "swap DHCP server" script just monitors the DHCP - but I have some reservations defined and would like not have to remember to update both DHCP servers ;)... Also, guess it would keep the stand-by DHCP server updated with the current IP addresses in use? [as when I swapped from Pi-Hole, I noted that the clients picked up totally new IP addresses and didn't keep what they had been previously assigned - so would that be the case again if the DHCP server swapped?]

But also wondering if it would be enough to leave the DHCP server enabled on the secondary machine but set the OFFER time to something like 4 seconds? [so the Primary one would normally answer?] - although that doesn't solve the replication of the reservations/current leases :)...

Sorry if any of this seems obvious!

@besmirzanaj
Copy link
Author

Following the updated script, to optimize the DHCP leases, the solution is really simple.

On the Secondary technitium server DHCP settings, configure the "Offer Delay Time" to something higher than 1s (1000 ms), and leave the default 100ms on the Primary one. That means that the Primary Server will always be the first to offer the IP assignment and if it is not available, the second DNS server will take over.

iScreen Shoter - Google Chrome - 250114180425

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment