Ah, ok!
However, the behavior seems to change when:
- The dataset I’m replicating has child datasets
- Sanoid takes snapshots using
recursive = zfs
.
- And, the datasets are using native zfs encryption (not sure this should matter?)
Modifying my first example:
[tank/ds1]
+ recursive = zfs
use_template = keepShort
[backup/ds1]
use_template = keepShort
Assume a dataset structure like:
tank
tank/ds1
tank/ds1/childA
tank/ds1/childB
When running:
syncoid --sendoptions="Rw" --no-sync-snap tank/ds1 backup/ds1
Any snapshots, newer or older, on backup
but not on tank
are destroyed.
If I add --no-rollback
:
syncoid --sendoptions="Rw" --no-sync-snap --no-rollback tank/ds1 backup/ds1
Those older snapshots are not destroyed on backup
.
From OpenZFS:
When a snapshot replication package stream that is generated by using the zfs
send
-R
command is received, any snapshots that do not exist on the sending location are destroyed by using the zfs
destroy
-d
command.
And under the comments of the -F
flag:
If receiving an incremental replication stream (for example, one generated by zfs
send
-R
[-i
|-I
]), destroy snapshots and file systems that do not exist on the sending side.
To ensure snapshots are not destroyed on backup
, I’m only adding --no-rollback
to the syncoid command. I notice if I add --debug
that the zfs receive
command has only -s
and not -F
.
Is there some other magic happening when I add --no-rollback
? Or is the -F
flag behaving one way for my first example (single dataset-to-dataset replication) and a different way for my second example (zfs recursive snapshots, child datasets, encryption)?
UPDATE: Figured a test case might help, as well. Here’s what I’m doing:
Using this sanoid.conf (I kept backup
out of the config for now, to ensure no pruning from sanoid was in the mix):
[tank/ds1]
recursive = zfs
use_template = keepShort
# Create the dataset
zfs create -o encryption=aes-256-gcm -o keylocation=file:///root/key.file -o keyformat=passphrase tank/ds1
zfs create tank/ds1/childA
zfs create tank/ds1/childB
# Create some initial snapshots
systemctl start sanoid.service
Now I have…
tank/ds1
tank/ds1@autosnap_2025-01-08_02:12:06_monthly
tank/ds1@autosnap_2025-01-08_02:12:06_weekly
tank/ds1@autosnap_2025-01-08_02:12:06_daily
tank/ds1@autosnap_2025-01-08_02:12:06_hourly
tank/ds1/childA
tank/ds1/childA@autosnap_2025-01-08_02:12:06_monthly
tank/ds1/childA@autosnap_2025-01-08_02:12:06_weekly
tank/ds1/childA@autosnap_2025-01-08_02:12:06_daily
tank/ds1/childA@autosnap_2025-01-08_02:12:06_hourly
tank/ds1/childB
tank/ds1/childB@autosnap_2025-01-08_02:12:06_monthly
tank/ds1/childB@autosnap_2025-01-08_02:12:06_weekly
tank/ds1/childB@autosnap_2025-01-08_02:12:06_daily
tank/ds1/childB@autosnap_2025-01-08_02:12:06_hourly
# Sync initially to backup
syncoid --sendoptions="Rw" --no-sync-snap tank/ds1 backup/ds1
# Destroy something on the source dataset to simulate pruning
zfs destroy -r tank/ds1@autosnap_2025-01-08_02:12:06_monthly
# Create a new snapshot on source
zfs snapshot -r tank/ds1@test
Now to compare behaviors.
syncoid --sendoptions="Rw" --no-sync-snap tank/ds1 backup/ds1
Results in:
backup/ds1
backup/ds1@autosnap_2025-01-08_02:12:06_weekly
backup/ds1@autosnap_2025-01-08_02:12:06_daily
backup/ds1@autosnap_2025-01-08_02:12:06_hourly
backup/ds1@test
backup/ds1/childA
backup/ds1/childA@autosnap_2025-01-08_02:12:06_weekly
backup/ds1/childA@autosnap_2025-01-08_02:12:06_daily
backup/ds1/childA@autosnap_2025-01-08_02:12:06_hourly
backup/ds1/childA@test
backup/ds1/childB
backup/ds1/childB@autosnap_2025-01-08_02:12:06_weekly
backup/ds1/childB@autosnap_2025-01-08_02:12:06_daily
backup/ds1/childB@autosnap_2025-01-08_02:12:06_hourly
backup/ds1/childB@test
I gained the @test
snapshot, but I lost @autosnap_2025-01-08_02:12:06_monthly
the one I deleted on the source (tank
).
# Add a source snapshot
zfs snapshot -r tank/ds1@test-again
# Delete a source snapshot
zfs destroy -r tank/ds1@autosnap_2025-01-08_02:12:06_weekly
# And sync again
syncoid --no-rollback --sendoptions="Rw" --no-sync-snap tank/ds1 backup/ds1
Results in:
backup/ds1
backup/ds1@autosnap_2025-01-08_02:12:06_weekly
backup/ds1@autosnap_2025-01-08_02:12:06_daily
backup/ds1@autosnap_2025-01-08_02:12:06_hourly
backup/ds1@test
backup/ds1@test-again
backup/ds1/childA
backup/ds1/childA@autosnap_2025-01-08_02:12:06_weekly
backup/ds1/childA@autosnap_2025-01-08_02:12:06_daily
backup/ds1/childA@autosnap_2025-01-08_02:12:06_hourly
backup/ds1/childA@test
backup/ds1/childA@test-again
backup/ds1/childB
backup/ds1/childB@autosnap_2025-01-08_02:12:06_weekly
backup/ds1/childB@autosnap_2025-01-08_02:12:06_daily
backup/ds1/childB@autosnap_2025-01-08_02:12:06_hourly
backup/ds1/childB@test
backup/ds1/childB@test-again
The new @test-again
snapshot appears. The @autosnap_2025-01-08_02:12:06_weekly
which was destroyed on the source has not been destroyed on the target.