HxHippy

ZFS Send/Receive for Backups

Using ZFS send and receive for efficient backups and replication.

Last updated: 2025-01-15

Overview

ZFS send/receive allows you to:

  • Create full or incremental backups
  • Replicate datasets between systems
  • Migrate data between pools
  • Clone environments efficiently

Basic Send/Receive

Full Send

# Send to file
zfs send tank/data@snap1 > /backup/data-snap1.zfs

# Send to another pool (same system)
zfs send tank/data@snap1 | zfs receive backup/data

# Compressed send (saves bandwidth)
zfs send tank/data@snap1 | gzip > /backup/data-snap1.zfs.gz

Receive Options

# Basic receive
zfs receive backup/data < /backup/data-snap1.zfs

# Force receive (destroys existing)
zfs receive -F backup/data

# Don't mount after receive
zfs receive -u backup/data

# Verbose output
zfs receive -v backup/data

Incremental Backups

# First: full backup
zfs snapshot tank/data@monday
zfs send tank/data@monday | zfs receive backup/data

# Later: incremental (only changes)
zfs snapshot tank/data@tuesday
zfs send -i tank/data@monday tank/data@tuesday | zfs receive backup/data

# Incremental from most recent snapshot
zfs send -I tank/data@monday tank/data@friday | zfs receive backup/data
# This sends monday->tuesday->wednesday->thursday->friday

Remote Replication

Via SSH

# Send to remote system
zfs send tank/data@snap1 | ssh backup-server zfs receive backup/data

# Compressed transfer
zfs send tank/data@snap1 | gzip | ssh backup-server "gunzip | zfs receive backup/data"

# Incremental to remote
zfs send -i tank/data@snap1 tank/data@snap2 | ssh backup-server zfs receive backup/data

# Resume interrupted transfer (ZFS 0.7+)
zfs send -t <token> | ssh backup-server zfs receive -s backup/data

With mbuffer (Faster)

# Install mbuffer
pkg install mbuffer

# Use with send/receive
zfs send tank/data@snap1 | mbuffer -s 128k -m 1G | ssh backup-server "mbuffer -s 128k -m 1G | zfs receive backup/data"

Raw Send (Encrypted)

# Send encrypted dataset (keeps encryption)
zfs send -w tank/encrypted@snap1 | zfs receive backup/encrypted

# Raw send preserves:
# - Encryption
# - Compression (already compressed blocks)
# - Dedup references

Replication Properties

# Send with properties
zfs send -p tank/data@snap1 | zfs receive backup/data

# Send recursively (all children)
zfs send -R tank/data@snap1 | zfs receive backup/data

# Exclude properties on receive
zfs receive -x mountpoint -x quota backup/data

Automated Replication Script

#!/bin/sh
# /usr/local/bin/zfs-replicate.sh

SRC_DATASET="tank/data"
DST_HOST="backup-server"
DST_DATASET="backup/data"
SNAP_PREFIX="auto"

# Get latest snapshots
LAST_SRC=$(zfs list -H -o name -t snapshot -S creation "${SRC_DATASET}" | grep "@${SNAP_PREFIX}" | head -1)
LAST_DST=$(ssh ${DST_HOST} "zfs list -H -o name -t snapshot -S creation ${DST_DATASET} 2>/dev/null | grep @${SNAP_PREFIX} | head -1")

# Create new snapshot
NEW_SNAP="${SRC_DATASET}@${SNAP_PREFIX}-$(date +%Y%m%d-%H%M)"
zfs snapshot "${NEW_SNAP}"

if [ -z "${LAST_DST}" ]; then
    # Full send
    zfs send "${NEW_SNAP}" | ssh ${DST_HOST} "zfs receive -F ${DST_DATASET}"
else
    # Incremental send
    LAST_DST_NAME=$(echo ${LAST_DST} | cut -d@ -f2)
    zfs send -i "@${LAST_DST_NAME}" "${NEW_SNAP}" | ssh ${DST_HOST} "zfs receive ${DST_DATASET}"
fi

echo "Replication complete: ${NEW_SNAP}"

Troubleshooting

# Check receive token (for resume)
zfs get receive_resume_token backup/data

# Destroy partial receive
zfs receive -A backup/data

# View estimated send size
zfs send -nv tank/data@snap1
zfs send -nvi tank/data@snap1 tank/data@snap2  # Incremental

Tools

  • zrepl - ZFS replication daemon
  • znapzend - ZFS backup with retention
  • sanoid/syncoid - Policy-driven snapshots and replication
advanced ZFS Updated 2025-01-15
  • zfs
  • backup
  • replication
  • send
  • receive
  • freebsd