ZFS / syncoid CLI gotchas (trixie, zfs 2.4.4, sanoid v2.3.0)
Last updated 05/11/2026 · migrated-from-outline, tools, zfs
.
Reference for the non-obvious behaviours of zfs and syncoid as shipped in our Docker images (zfsutils-linux 2.4.4 from trixie backports + sanoid/syncoid v2.3.0 Perl scripts). Found while building weboid’s ZFS backup path (see that session log). These bite anyone scripting against these tools.
syncoid (sanoid v2.3.0)
- No
**-s**short flag. It is ambiguous (sendoptions | skip-parent | source-bwlimit | sshcipher | ...) and the command aborts with “Option s is ambiguous”. Always use long options. - Remote-target form mis-parses a snapshotted SOURCE.
syncoid ds@snap [user@]host:targettreatsds@snapasuser=ds, host=snap→ “Could not resolve hostname”. To push, target the dataset ( syncoid ds user@host:target) and let syncoid manage its own snapshot. It snapshots locally, transfers the delta since the last run, and (by default) destroys its working snapshot after. - Refuses to receive into a pre-existing empty dataset. If the target dataset exists but has no matching snapshots: “Target … exists but has no snapshots matching … Cowardly refusing to destroy your existing target.” → create only the parent of the destination, let syncoid create the final dataset.
- Needs its own SSH credentials to push. It dials the server itself, so pass
--sshkey=<priv>and an ssh_config (via--sshconfig=) with host-key trust (StrictHostKeyChecking noor a pinned key), else “Host key verification failed”.
zfs CLI (2.4.x)
**zfs get <prop> <ds>**parses**<ds>**as a property, not a dataset. It fails for both filesystems and volumes with “bad property list: invalid property ''”. Don’t use zfs getto test dataset existence.- Prefer
**zfs list -H -o name <ds>**+ exit code for an existence check that works for both filesystems and volumes: rc 0 = exists, rc 1 with “dataset does not exist” = missing. (zfs getand the combined-Hnoflag both fail.) - The combined
**-Hno**flag is invalid. Write-H -o name, not-Hno. (Affects any script that shortcuts the flags.) - Listing snapshots:
zfs list -t snapshot -r <ds>works; a literal@*glob as an argument does not (“invalid character ’*’ in name”) — rely on-t snapshotinstead of shell-globbing names.
sshd + Docker (receiving syncoid pushes)
- StrictModes rejects a bind-mounted
**authorized_keys**unless owned by root (UID 0) and mode 600. A Docker bind mount preserves the host file’s ownership, sochown root:root+chmod 600the host copy before mounting. Symptom insshd -ddd: “Authentication refused: bad ownership or modes for file /root/.ssh/authorized_keys”. - A server that receives syncoid must run as root to
zfs createreceive datasets and bind :22; an unprivileged user gets “permission denied” on zfs admin.
Quick existence-check idiom (Go)
cmd := exec.CommandContext(ctx, "zfs", "list", "-H", "-o", "name", ds)
out, err := cmd.CombinedOutput()
if err == nil { return nil } // exists
var ee *exec.ExitError
if !errors.As(err, &ee) || ee.ExitCode() != 1 || !strings.Contains(string(out), "does not exist") {
return fmt.Errorf("check dataset %s: %w", ds, err) // real error
}
// ... create parent with: zfs create -p -o canmount=off <parent> Source: Wiki.js