The Handbook

    Theme
    • Guides
        • Check a system before installing FreeBSD
        • Orient a Linux administrator on FreeBSD
        • Supported FreeBSD releases
        • Choose a FreeBSD documentation and support channel
        • Move files safely without GNU mv -t
        • Update, upgrade, or update packages?
        • Upgrade 14.4 or 15.0 to FreeBSD 15.1
        • Choose packages, ports, or poudriere
        • Choose the quarterly or latest package branch
        • Choose a FreeBSD download or package mirror
        • Make a system setting persistent
        • Configure locale, keyboard, and time zone
        • Configure a serial console for recovery
        • Choose a custom kernel, module, or loader setting
        • Run a Linux binary with the compatibility layer
        • Check desktop and laptop hardware before installation
        • Choose and check a graphics driver
        • Choose Xorg or Wayland
        • Build a maintainable desktop baseline
        • Install and check a desktop browser
        • Check laptop Wi-Fi, power, and suspend
        • Check multimedia readiness
        • Choose and configure a printing stack
        • Run Windows applications with Wine
        • Snapshot and replicate a ZFS dataset
        • Choose a filesystem and storage layout
        • Operate ZFS without losing the recovery path
        • Restore files from a ZFS snapshot
        • Read ZFS pool health and run a scrub
        • Replace a failed device in a ZFS mirror
        • Replicate a ZFS dataset over SSH
        • Change PF safely on a remote host
        • Configure a narrow WireGuard tunnel
        • Plan a FreeBSD home server
        • Choose a mail server or an outgoing relay
        • Publish a network service safely
        • Choose a jail network model
        • Choose native jails or a jail manager
        • Choose a jail or a bhyve virtual machine
        • Establish a FreeBSD security baseline
        • Choose a MAC policy
        • Audit security-relevant activity
        • Start a DTrace performance investigation
    • Integrations
        • Create a first jail with Bastille
        • Compare jail managers and OCI tooling
        • Publish a Bastille service through PF
        • Mount a ZFS dataset in a Bastille jail
        • Update and upgrade Bastille jails
        • Back up and restore a Bastille jail
        • Prepare bhyve and vm-bhyve
        • Choose NFS or Samba for file sharing
        • Choose ZFS backup automation
        • Operate a signed poudriere repository
        • Manage FreeBSD configuration with Ansible or Salt
        • Run Motion with webcamd on FreeBSD
        • Design a reverse proxy, certificates, and monitoring
    • FAQ
      • Troubleshooting
          • Recover an interrupted freebsd-update run
          • Resolve a package repository or ABI mismatch
          • Diagnose the FreeBSD boot path
          • Recover with a ZFS boot environment
          • Diagnose DNS, routing, and firewall paths
          • Diagnose network mbuf exhaustion
          • Bind a service to a low port without running it as root
          • Diagnose audio output or input
          • Diagnose webcamd, cuse, and a webcam
          • Fix USB device permissions without opening every device
      • About this handbook
      • Synopsis
      • Establish the local replication chain first
      • Create a dedicated destination boundary
      • Exclude encrypted and raw streams from this procedure
      • Estimate and send the first stream
      • Send an incremental snapshot
      • Handle interruption without guessing
      • Verify recovery, not only transfer
      • Primary references

      Replicate a ZFS dataset over SSH

      Last reviewed
      13 August 2026
      Applies to
      15.1-RELEASE, 15.0-RELEASE, 14.4-RELEASE

      Synopsis #

      ZFS send streams can cross an SSH connection without storing an intermediate archive. The difficult part is authorization: the source needs permission to send the intended snapshot, while the destination needs narrowly scoped permission to receive into one dataset. Granting unrestricted remote root access merely to simplify a backup expands the failure boundary.

      This guide defines the design and verification sequence for an unencrypted dataset. It intentionally does not supply a universal authorized_keys command wrapper because dataset names, retention, direction, and the permitted receive forms determine which operations are safe.

      Establish the local replication chain first #

      Complete one local full receive and one incremental receive using Snapshot and replicate a ZFS dataset . Remote transport should not be introduced until snapshot naming, destination layout, and restore verification work locally.

      Record:

      • source and destination datasets;
      • snapshot naming and retention policy;
      • whether child datasets are included;
      • whether encryption must remain raw;
      • the oldest common snapshot required for incrementals;
      • behavior after an interrupted transfer.

      Create a dedicated destination boundary #

      Use a dedicated account and an existing parent dataset for the sender. Do not reuse an administrator’s interactive key. ZFS delegation with zfs allow can grant operations on that parent and its descendants, but each permission should be derived from the exact receive workflow and tested against a disposable dataset.

      For example, create a parent that contains received children but does not itself hold replicated data:

      # zfs create -o canmount=off -o mountpoint=none backup/replica
      $ zfs allow backup/replica
      

      The first full stream in this guide creates backup/replica/data. That child must not exist before the full receive. Keeping the delegated parent separate from the received child makes the creation boundary explicit and avoids trying to receive a full stream into the already existing delegation root.

      On the destination, keep the received dataset unmounted by default or assign a non-conflicting mount point. A replica should not shadow production data merely because it arrived successfully.

      Inspect existing delegation before adding any:

      $ zfs allow backup/replica
      

      The destination account may need receive-related permissions and the ability to create descendants under the selected parent. It should not receive pool-wide administration or ZFS access to unrelated datasets.

      The examples below use an ordinary SSH login and shell so that the supplied zfs receive command can run. Dataset delegation limits what ZFS operations that account can perform, but it does not restrict the SSH key to one remote command. A compromised source can invoke any command available to the destination account. A forced-command wrapper or an sshd_config restriction is a separate control that must be implemented and tested before describing this path as command-restricted.

      Exclude encrypted and raw streams from this procedure #

      Confirm the source dataset’s encryption state:

      $ zfs get encryption,encryptionroot tank/data
      

      Proceed with these command examples only when encryption is off. For an encrypted dataset, zfs send -w creates a raw stream that preserves the encrypted on-disk representation and encryption parameters. A raw stream must be received as-is: the receive cannot decrypt, re-encrypt, or recompress it. Mixing raw and non-raw receive history also affects whether later raw incrementals can be accepted.

      Raw replication therefore needs its own reviewed full-and-incremental chain, destination key-recovery test, and consistent use of -w. Adding -w to only one command in this procedure is not a safe adaptation.

      Estimate and send the first stream #

      On the source, estimate the stream:

      # zfs send -nPv tank/data@replica-1
      

      Then send it to the reviewed remote receive command:

      # zfs send -v tank/data@replica-1 | ssh backup@backup.example.org zfs receive -u -s backup/replica/data
      

      -u leaves the received dataset unmounted. -s preserves resumable receive state if the stream is interrupted. The SSH host key must be verified through an independent channel before automating the connection.

      Confirm the received snapshot on the destination:

      $ zfs list -t snapshot backup/replica/data@replica-1
      

      Send an incremental snapshot #

      After creating the next source snapshot, estimate the incremental difference:

      # zfs send -nPv -i tank/data@replica-1 tank/data@replica-2
      

      Send it only after confirming that replica-1 remains on both sides:

      # zfs send -v -i tank/data@replica-1 tank/data@replica-2 | ssh backup@backup.example.org zfs receive -u -s backup/replica/data
      

      Destroying the last common snapshot before a successful receive breaks the incremental path. Retention should remove snapshots only after destination verification.

      Handle interruption without guessing #

      When a receive started with -s is interrupted, the destination dataset can expose a receive_resume_token:

      $ zfs get receive_resume_token backup/replica/data
      

      The corresponding resume workflow uses zfs send -t with that token. Treat the token as state from the destination and use the exact current manual-page procedure. Do not begin a new full receive over a partial destination without deciding whether to resume or abort the saved state.

      Verify recovery, not only transfer #

      At intervals, receive or clone the replicated snapshot into an isolated recovery namespace and validate representative data and application consistency. Monitor:

      • last successful snapshot on both sides;
      • destination capacity and snapshot growth;
      • SSH and receive failures;
      • pool health at source and destination;
      • retention execution;
      • time since the last restore test.

      Primary references #

      • FreeBSD Handbook: ZFS replication
      • zfs-send(8)
      • zfs-receive(8)
      • zfs-allow(8)
      • ssh(1)
      • sshd(8)

      Independent documentation. Not affiliated with or endorsed by the FreeBSD Project or the FreeBSD Foundation.

      Report a bug
      • Synopsis
      • Establish the local replication chain first
      • Create a dedicated destination boundary
      • Exclude encrypted and raw streams from this procedure
      • Estimate and send the first stream
      • Send an incremental snapshot
      • Handle interruption without guessing
      • Verify recovery, not only transfer
      • Primary references