Very much a rough draft.
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
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.
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!
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.
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!
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
Published: Sep 20, 2026 @ 15:45.
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.