Menu

ZFS, Syncoid, and Sanoid

Very much a rough draft.

Introduction #

My Backup System is a hot mess of bash scripts. After a friend lost a long essay draft, I realized I should run my backup script. It turned out that I hadn’t run the script since March 2025. Over a year ago! Oh no!

ZFS is a powerful filesystem. It can create snapshots of your data that make it really easy to restore. However, how you manage those snapsots is up to you. If one wants to use ZFS snapshots as a backup mechanism then one need tools to automate making and synching the snapshots.

I’m going to play with some tools for managing the snapshots used for backups: sanoid produces snapshots and syncoid synchronizes them between backups.

This note is about my working through this guide:

The official documentation is here:

The relevant parts of my computer setup are a desktop officebox and an computer in my basement called oldbox. They’re connected via a Tailscale VPN. When officebox will access oldbox via tailscale, it will use the hostname ts-oldbox.

First, we install sanoid.

 $ sudo apt-get install sanoid

Testing Environment #

We setup a little testing environment.

 $ truncate -s 2G /tmp/sanoid-backup.img
 $ sudo zpool create sanoid-backup /tmp/sanoid-backup.img
 $ sudo chown -R pgadey:pgadey /sanoid-backup

We check the status of the backup zpool.

 $ sudo zpool status sanoid-backup


 pool: sanoid-backup
 state: ONLINE
 config:
 
         NAME                      STATE     READ WRITE CKSUM
         sanoid-backup             ONLINE       0     0     0
           /tmp/sanoid-backup.img  ONLINE       0     0     0

It is helpful to be able to add random data to this zpool for testing purposes.

Configuring Sanoid #

After that’s all setup, we configure sanoid. A barebones /etc/sanoid/sanoid.conf looks like this:

 # Reusable templates, define retention counts here
 
 [template_critical]
     frequently = 4
     hourly = 48
     daily = 60
     weekly = 12
     monthly = 12
     autosnap = yes
     autoprune = yes
 
 # Datasets, point sanoid at your ZFS datasets
 
 [sanoid-backup]
     use_template = critical
     recursive = yes

And now we can check that sanoid knows what to do.

 $ sudo sanoid

 INFO: cache expired - updating from zfs list.
 INFO: taking snapshots...
 taking snapshot sanoid-backup@autosnap_2026-09-20_15:56:44_monthly
 taking snapshot sanoid-backup@autosnap_2026-09-20_15:56:44_weekly
 taking snapshot sanoid-backup@autosnap_2026-09-20_15:56:44_daily
 taking snapshot sanoid-backup@autosnap_2026-09-20_15:56:44_hourly
 taking snapshot sanoid-backup@autosnap_2026-09-20_15:56:44_frequently
 INFO: cache expired - updating from zfs list.
 INFO: pruning snapshots...

And so it looks like sanoid is taking a healthy number of snapshots. We want to ensure that it keeps taking snapshots, so we run:

 sudo systemctl enable --now sanoid.timer

This will enable a systemd timer to run sanoid.

sudo systemctl status sanoid.timer

 ● sanoid.timer - Run Sanoid Every 15 Minutes
      Loaded: loaded (/usr/lib/systemd/system/sanoid.timer; enabled; preset: enabled)
      Active: active (waiting) since Sun 2026-09-20 07:39:32 EDT; 8h ago
      Trigger: Sun 2026-09-20 16:15:00 EDT; 10min left
      Triggers: ● sanoid.service
 
 Sep 20 07:39:32 zenbook systemd[1]: Started sanoid.timer - Run Sanoid Every 15 Minutes.

To play around with syncoid we also need a backup target. That will be a zpool sanoid-remote on a machine called ts-oldbox. Inside that zpool we will use syncoid to create a dataset: sanoid-remote/backup. It is worth noting that syncoid DOES NOT want this dataset to exist before the initial transfer. You don’t need to set it up.

Ideally, I want this to work:

syncoid --recursive sanoid-backup ts-oldbox:sanoid-remote/backup

However, it doesn’t quite work because I need a sudo access on the remote machine. This is a big headache involved with ZFS on Linux which doesn’t handle root access delegation well. And this is the bit where things get very very iffy. I am not sure of the security implications of this. Proceed with caution!

Sudo #

To get the command above to work as a regular user, one can add their usual user to the visudo list for zfs. On the remote machine:

$ sudo visudo

Add the usual user to passwordless for root access to zfs.

pgadey ALL=(root) NOPASSWD: /usr/sbin/zfs

If something like the following

ssh ts-oldbox 'sudo -n zfs list'

works, then you should be able to do the remote syncoid synchronization. The zfs send | zfs receive pipeline needs similar sudo permissions on both sides. So, it is worth setting this up on either end.

After setting up the permissions, it just works as my regular user.

  $ syncoid --recursive sanoid-backup ts-oldbox:sanoid-remote/backup

  WARN: ZFS resume feature not available on target machine - sync will continue
  without resume support.
  INFO: Sending oldest full snapshot
  sanoid-backup@autosnap_2026-09-20_15:56:44_monthly (~ 12 KB) to new target
  filesystem:
  44.2KiB 0:00:00 [1.12MiB/s] [================================================] 350%
  INFO: Updating new target filesystem with incremental
  sanoid-backup@autosnap_2026-09-20_15:56:44_monthly ...
  syncoid_zenbook_2026-09-20:16:52:18-GMT-04:00 (~ 10 KB):
  11.3KiB 0:00:00 [ 116KiB/s] [================================================] 108%

It would be nice to make a little bash alias of just this command. A quick one-line sanoid-now command or something.

Automating Syncoid #

We then setup some systemd timers to automate backup process: /etc/systemd/system/syncoid.service.

 [Unit]
 
 Description=ZFS replication via syncoid
 After=network-online.target zfs.target
 Wants=network-online.target
 
 [Service]
 Type=oneshot
 Environment=HOME=/root
 ExecStart=/usr/sbin/syncoid \
   --recursive \
   --no-privilege-elevation \
   --sshconfig=/home/pgadey/.ssh/config \
   --sshkey=/root/.ssh/id_ed25519 \
   sanoid-backup \
   ts-oldbox:sanoid-remote/backup 
 StandardOutput=journal
 StandardError=journal

The Environment=HOME=/root is so that mbuffer doesn’t panic about not having a $HOME. The sshconfig option is needed to make sure that systemd (which runs as root) accesses the host ts-oldbox via the correct IP, which is setup by tailscale. One thing that tripped me up in my initial setup: the address ts-oldbox:sanoid-remote/backup looks weird. The format here is HOST:zpool/dataset. A lot of tinkering revealed that I had an extra initial slash that threw everything off.

The following is very helpful for tinkering around with the configs.

 sudo vim /etc/systemd/system/syncoid.service
 sudo systemctl daemon-reload; sudo systemctl restart syncoid.service; sudo systemctl status syncoid.service

Once that serive is working, we make a timer to run syncoid on schedule: /etc/systemd/system/syncoid.timer

 [Unit]
 Description=Run syncoid every 20 minutes on the clock.
 
 [Timer]
 OnCalendar=*:0/20
 Persistent=true
 
 [Install]
 WantedBy=timers.target

The ssh access permissions are still a bit messed up. When systemd runs syncoid, it runs it as root. This means that the ssh connection is really going root to root. And so, we need to make ssh keys for root. I created a ssh key pair for root on zenbook and gave put it in the /root/.ssh/authorized_keys on ts-oldbox. This is purpose of the line --sshkey=/root/.ssh/id_ed25519. Again, the security implications of this are not clear to me. Proceed with caution!

Random Data #

The following truly horrible hack was helpful for producing a steady stream of random testing data. It dumps 100Mb of random data in to $TARGET every ten minutes.

  #!/bin/bash
  TARGET="/sanoid-backup/"
  
  # loop forever
  while true;
  do
  	for i in {1..10};
  	       do
  		       FILE="$TARGET/random-$(date --iso-8601=seconds | tr : c).$i"
  		       echo -e "\nnew file: $FILE"
  		       dd if=/dev/urandom of=$FILE bs=1M count=10;
  		done
  	# wait ten minutes
  	sleep 600;
  done

Recommend Reads #



Published: Sep 20, 2026 @ 15:45.

Tags

#hack #linux

Navigation Menu

Home / Now / Archive / Office Camera / Bookmarks / Tags / Feeds / Top of Page

Thanks for reading! If you have any comments or questions about the content, please let me know. Anyone can contact me by email.