Showing posts with label ssh. Show all posts
Showing posts with label ssh. Show all posts

02 August 2026

Sending ZFS backups to another server

After migrating back to my Lenovo server, it didn't have enough drive bays for my backup drives. So I needed to use the Dell as the backup destination.

Two servers:

  • Primary: this is the main server where the primary copy of the data exists (Lenovo)
  • Secondary: this is the backup server where we will send the data to a read-only copy (Dell)


Setup SSH

On the primary server, create ssh key pair (if you already have a key pair feel free to use that):

  • ssh-keygen -t ed25519 -C "root@primary"
    • -C is just a comment to allow for easier identification, so feel free to change, but make sure you also change where it is used later
  • cat id_ed25519.pub
    • We will need this output for saving the key


On the secondary server, create our backup user and give it the public key:

  • Install sudo
    • apt install sudo -y
  • Add the pveadmin user
    • adduser pveadmin
    • usermod -aG sudo pveadmin
    • # Allow pveadmin to run certain commands without prompting for password so use visudo to add the line
    • visudo
      • pveadmin ALL=(ALL) NOPASSWD: /usr/sbin/zfs list *, /usr/sbin/zfs receive *, /usr/sbin/zpool scrub *
  • Switch to the pveadmin user and add our ssh key
    • su - pveadmin
    • mkdir ~/.ssh
    • chmod 700 ~/.ssh
    • # Change the below to your key
    • echo "ssh-ed25519 ABCD1234... root@primary" >> ~/.ssh/authorized_keys
    • chmod 600 ~/.ssh/authorized_keys
    • exit
  • Test the ssh from primary to secondary and save the fingerprint
    • ssh pveadmin@secondary


Send the backups

  • Send over our initial data (use disown to prevent user disconnections from stopping job)
    • zfs send -R --raw rpool/data@backup-20260323 | ssh pveadmin@secondary "sudo zfs receive -o readonly=on backup-pool/data" 2>errors.log & disown
  • Send over incremental data
    • zfs send -R --raw -I rpool/data@backup-20260323 rpool/data@backup-20260401 | ssh pveadmin@secondary "sudo zfs receive backup-pool/data" 2>errors.log & disown


Automate the backups

  • Added a remote argument to my backup.sh script


Scripts

remote-backup.sh

#!/bin/sh

if [ $# -ne 3 ] ; then
  echo "Requires 3 args source, destination, and user@host"
  exit 1
fi

src=$1
dest=$2
remote=$3

snaplist=$(zfs list -t snapshot $src)
if [ $? -ne 0 ] ; then
  echo "No snapshots found for $src"
  exit 1
fi

bkpsnap=$(ssh ${remote} "sudo zfs list -t snapshot $dest 2>&1")
incremental=$?

if [ $incremental -eq 0 ] ; then
  incsnap=$(echo "$bkpsnap" | tail -n 1 | cut -f1 -d" " | cut -f2 -d"@")
  snap=$(echo "$snaplist" | grep "$incsnap" -A 1 | tail -n 1 | cut -f1 -d" " | cut -f2 -d"@")

  if [ "$incsnap" = "$snap" ] ; then
    echo "Backup is already up to date"
    exit 1
  fi

  echo "Sending incremental snap $src@$snap on top of $dest@$incsnap"
  echo "zfs send -R --raw -I $src@$incsnap $src@$snap | ssh ${remote} \"sudo zfs receive $dest\""
  zfs send -R --raw -I $src@$incsnap $src@$snap | ssh ${remote} "sudo zfs receive $dest"
else
  snap=$(echo "$snaplist" | tail -n +2 | head -n 1 | cut -f1 -d" " | cut -f2 -d"@")
  echo "Sending initial snap $src@$snap to $dest"
  echo "zfs send -R --raw $src@$snap | ssh ${remote} \"sudo zfs receive -o readonly=on $dest\""
  zfs send -R --raw $src@$snap | ssh ${remote} "sudo zfs receive -o readonly=on $dest"
fi

echo "Complete\n"


Appendix

Sources