TL;DR

First recv with -o encryption=on (no -w). Destination dataset name is required.

Same bytes, new key. Source stays plaintext; destination is born encrypted. OpenZFS allows this only on the initial receive (dataset that does not exist yet).

Source thread: r/zfs.

Command

1
2
3
4
5
6
zfs snapshot tank/test@snap1
zfs send tank/test@snap1 \
  | zfs recv -o encryption=on \
      -o keyformat=passphrase \
      -o keylocation=file:///path/to/keyfile \
      tank/encrypted

Things that silently break the send:

  1. Do not use -w. Raw send replicates the source encryption state. Plaintext in → plaintext out, and -o encryption=… is ignored or errors.
  2. Name the destination dataset (tank/encrypted). A recv with no target does not create it.
  3. Passphrase vs file: keylocation=prompt is a bad fit across a non-interactive pipe. Use a keyfile.
  4. Later incrementals (-i) inherit encryption. Do not pass -o encryption=on again.

Check

1
2
zfs get encryption,keystatus,keyformat tank/encrypted
zfs load-key tank/encrypted   # if keystatus=unavailable

The other direction

Encrypted → encrypted with the same wrapping key: zfs send -w. Encrypted → new wrapping key: send without -w (ZFS decrypts on send; recv encrypts). That needs load-key on the source and is slower.

See also: native encryption.