Page MenuHomeFreeBSD

D58175.id181823.diff
No OneTemporary

D58175.id181823.diff

diff --git a/documentation/content/en/books/handbook/zfs/_index.adoc b/documentation/content/en/books/handbook/zfs/_index.adoc
--- a/documentation/content/en/books/handbook/zfs/_index.adoc
+++ b/documentation/content/en/books/handbook/zfs/_index.adoc
@@ -50,17 +50,15 @@
ZFS is an advanced file system designed to solve major problems found in previous storage subsystem software.
-Originally developed at Sun(TM), ongoing open source ZFS development has moved to the http://open-zfs.org[OpenZFS Project].
+Originally developed at Sun Microsystems(TM), ongoing open source ZFS development has moved to the https://openzfs.org[OpenZFS Project].
ZFS has three major design goals:
* Data integrity: All data includes a crossref:zfs[zfs-term-checksum,checksum] of the data. ZFS calculates checksums and writes them along with the data. When reading that data later, ZFS recalculates the checksums. If the checksums do not match, meaning detecting one or more data errors, ZFS will attempt to automatically correct errors when ditto-, mirror-, or parity-blocks are available.
* Pooled storage: adding physical storage devices to a pool, and allocating storage space from that shared pool. Space is available to all file systems and volumes, and increases by adding new storage devices to the pool.
* Performance: caching mechanisms provide increased performance.
- crossref:zfs[zfs-term-arc,ARC] is an advanced memory-based read cache. ZFS
- provides a second level disk-based read cache with
- crossref:zfs[zfs-term-l2arc,L2ARC], and a disk-based synchronous write cache
- named crossref:zfs[zfs-term-zil,ZIL].
+crossref:zfs[zfs-term-arc,ARC] is an advanced memory-based read cache.
+ZFS provides a second level disk-based read cache with crossref:zfs[zfs-term-l2arc,L2ARC], and a disk-based synchronous write cache named crossref:zfs[zfs-term-zil,ZIL].
A complete list of features and terminology is in crossref:zfs[zfs-term, ZFS Features and Terminology].
@@ -70,32 +68,32 @@
More than a file system, ZFS is fundamentally different from traditional file systems.
Combining the traditionally separate roles of volume manager and file system provides ZFS with unique advantages.
The file system is now aware of the underlying structure of the disks.
-Traditional file systems could exist on a single disk alone at a time.
+Traditional file systems could exist on a single disk at a time.
If there were two disks then creating two separate file systems was necessary.
A traditional hardware RAID configuration avoided this problem by presenting the operating system with a single logical disk made up of the space provided by physical disks on top of which the operating system placed a file system.
-Even with software RAID solutions like those provided by GEOM, the UFS file system living on top of the RAID believes it's dealing with a single device.
+Even with software RAID solutions like those provided by GEOM, the UFS file system living on top of the RAID believes it is dealing with a single device.
ZFS' combination of the volume manager and the file system solves this and allows the creation of file systems that all share a pool of available storage.
One big advantage of ZFS' awareness of the physical disk layout is that existing file systems grow automatically when adding extra disks to the pool.
This new space then becomes available to the file systems.
-ZFS can also apply different properties to each file system. This makes it useful to create separate file systems and datasets instead of a single monolithic file system.
+ZFS can also apply different properties to each file system.
+This makes it useful to create separate file systems and datasets instead of a single monolithic file system.
[[zfs-quickstart]]
== Quick Start Guide
+The FreeBSD installer can install the system directly onto a ZFS pool, a configuration known as Root-on-ZFS; see crossref:bsdinstall[bsdinstall-part-zfs,Guided Root-on-ZFS].
+This section shows how to create and manage additional ZFS pools and datasets on a running system.
+
FreeBSD can mount ZFS pools and datasets during system initialization.
To enable it, run:
[source,shell]
....
-# service zfs enable
+# sysrc zfs_enable="YES"
....
-or add this line to [.filename]#/etc/rc.conf#:
-
-[.programlisting]
-....
-zfs_enable="YES"
-....
+This adds `zfs_enable="YES"` to [.filename]#/etc/rc.conf#.
+Running `service zfs enable` makes the same change.
Then start the service:
@@ -104,32 +102,32 @@
# service zfs start
....
-The examples in this section assume three SCSI disks with the device names [.filename]#da0#, [.filename]#da1#, and [.filename]#da2#.
-Users of SATA hardware should instead use [.filename]#ada# device names.
+The examples in this section assume three SATA disks with the device names [.filename]#ada0#, [.filename]#ada1#, and [.filename]#ada2#.
+Users of SCSI/SAS hardware should instead use [.filename]#da# device names.
[[zfs-quickstart-single-disk-pool]]
=== Single Disk Pool
-To create a simple, non-redundant pool using a single disk device:
+Create a GPT partition on an empty disk first.
+Then add a partition of type `freebsd-zfs` from which a single, non-redundant pool is created:
[source,shell]
....
-# zpool create example /dev/da0
+# gpart create -s gpt ada1
+# gpart add -t freebsd-zfs ada1
+# zpool create example /dev/ada1p1
....
To view the new pool, review the output of `df`:
[source,shell]
....
-# df
-Filesystem 1K-blocks Used Avail Capacity Mounted on
-/dev/ad0s1a 2026030 235230 1628718 13% /
-devfs 1 1 0 100% /dev
-/dev/ad0s1d 54098308 1032846 48737598 2% /usr
-example 17547136 0 17547136 0% /example
+# df /example
+Filesystem 1K-blocks Used Avail Capacity Mounted on
+example 17547136 0 17547136 0% /example
....
-This output shows creating and mounting of the `example` pool, and that is now accessible as a file system.
+This output shows creating and mounting of the `example` pool, and that it is now accessible as a file system.
Create files for users to browse:
[source,shell]
@@ -169,11 +167,8 @@
....
# zfs umount example/compressed
# df
-Filesystem 1K-blocks Used Avail Capacity Mounted on
-/dev/ad0s1a 2026030 235232 1628716 13% /
-devfs 1 1 0 100% /dev
-/dev/ad0s1d 54098308 1032864 48737580 2% /usr
-example 17547008 0 17547008 0% /example
+Filesystem 1K-blocks Used Avail Capacity Mounted on
+example 17547008 0 17547008 0% /example
....
To re-mount the file system to make it accessible again, use `zfs mount` and verify with `df`:
@@ -183,9 +178,6 @@
# zfs mount example/compressed
# df
Filesystem 1K-blocks Used Avail Capacity Mounted on
-/dev/ad0s1a 2026030 235234 1628714 13% /
-devfs 1 1 0 100% /dev
-/dev/ad0s1d 54098308 1032864 48737580 2% /usr
example 17547008 0 17547008 0% /example
example/compressed 17547008 0 17547008 0% /example/compressed
....
@@ -195,9 +187,9 @@
[source,shell]
....
# mount
-/dev/ad0s1a on / (ufs, local)
+/dev/ada0p1 on / (ufs, local)
devfs on /dev (devfs, local)
-/dev/ad0s1d on /usr (ufs, local, soft-updates)
+/dev/ada0p2 on /usr (ufs, local, soft-updates)
example on /example (zfs, local)
example/compressed on /example/compressed (zfs, local)
....
@@ -218,13 +210,13 @@
[source,shell]
....
# df
-Filesystem 1K-blocks Used Avail Capacity Mounted on
-/dev/ad0s1a 2026030 235234 1628714 13% /
-devfs 1 1 0 100% /dev
-/dev/ad0s1d 54098308 1032864 48737580 2% /usr
-example 17547008 0 17547008 0% /example
-example/compressed 17547008 0 17547008 0% /example/compressed
-example/data 17547008 0 17547008 0% /example/data
+Filesystem 1K-blocks Used Avail Capacity Mounted on
+/dev/ada0p1 2026030 235234 1628714 13% /
+devfs 1 1 0 100% /dev
+/dev/ada0p2 54098308 1032864 48737580 2% /usr
+example 17547008 0 17547008 0% /example
+example/compressed 17547008 0 17547008 0% /example/compressed
+example/data 17547008 0 17547008 0% /example/data
....
Notice that all file systems in the pool have the same available space.
@@ -248,16 +240,23 @@
ZFS supports this feature in its pool design.
RAID-Z pools require three or more disks but provide more usable space than mirrored pools.
-This example creates a RAID-Z pool, specifying the disks to add to the pool:
+This example creates a RAID-Z pool.
+First the GPT partitions are created on each disk, then specify these GPT partitions to add to the pool using the `raidz` keyword:
[source,shell]
....
-# zpool create storage raidz da0 da1 da2
+# gpart create -s gpt ada0
+# gpart add -t freebsd-zfs ada0
+# gpart create -s gpt ada1
+# gpart add -t freebsd-zfs ada1
+# gpart create -s gpt ada2
+# gpart add -t freebsd-zfs ada2
+# zpool create storage raidz ada0p1 ada1p1 ada2p1
....
[NOTE]
====
-Sun(TM) recommends that the number of devices used in a RAID-Z configuration be between three and nine.
+Keeping the number of devices used in a RAID-Z configuration between three and nine is recommended.
For environments requiring a single pool consisting of 10 disks or more, consider breaking it up into smaller RAID-Z groups.
If two disks are available, ZFS mirroring provides redundancy if required.
Refer to man:zpool[8] for more details.
@@ -289,24 +288,24 @@
# ln -s /storage/home /usr/home
....
-Users data is now stored on the freshly-created [.filename]#/storage/home#.
+User data is now stored on the freshly-created [.filename]#/storage/home#.
Test by adding a new user and logging in as that user.
Create a file system snapshot to roll back to later:
[source,shell]
....
-# zfs snapshot storage/home@08-30-08
+# zfs snapshot storage/home@2026-07-11
....
ZFS creates snapshots of a dataset, not a single directory or file.
-The `@` character is a delimiter between the file system name or the volume name.
+The `@` character is a delimiter between the file system or volume name and the snapshot name.
Before deleting an important directory, back up the file system, then roll back to an earlier snapshot in which the directory still exists:
[source,shell]
....
-# zfs rollback storage/home@08-30-08
+# zfs rollback storage/home@2026-07-11
....
To list all available snapshots, run `ls` in the file system's [.filename]#.zfs/snapshot# directory.
@@ -323,7 +322,7 @@
[source,shell]
....
-# zfs destroy storage/home@08-30-08
+# zfs destroy storage/home@2026-07-11
....
After testing, make [.filename]#/storage/home# the real
@@ -339,16 +338,16 @@
[source,shell]
....
# mount
-/dev/ad0s1a on / (ufs, local)
+/dev/ada0p1 on / (ufs, local)
devfs on /dev (devfs, local)
-/dev/ad0s1d on /usr (ufs, local, soft-updates)
+/dev/ada0p2 on /usr (ufs, local, soft-updates)
storage on /storage (zfs, local)
storage/home on /home (zfs, local)
# df
Filesystem 1K-blocks Used Avail Capacity Mounted on
-/dev/ad0s1a 2026030 235240 1628708 13% /
+/dev/ada0p1 2026030 235240 1628708 13% /
devfs 1 1 0 100% /dev
-/dev/ad0s1d 54098308 1032826 48737618 2% /usr
+/dev/ada0p2 54098308 1032826 48737618 2% /usr
storage 26320512 0 26320512 0% /storage
storage/home 26320512 0 26320512 0% /home
....
@@ -361,6 +360,8 @@
daily_status_zfs_enable="YES"
....
+man:periodic[8] can also run scheduled scrubs of the pool; see crossref:zfs[zfs-zpool-scrub,Scrubbing a Pool].
+
[[zfs-quickstart-recovering-raid-z]]
=== Recovering RAID-Z
@@ -387,36 +388,37 @@
pool: storage
state: DEGRADED
status: One or more devices has been taken offline by the administrator.
- Sufficient replicas exist for the pool to continue functioning in a
- degraded state.
+ Sufficient replicas exist for the pool to continue functioning in a
+ degraded state.
action: Online the device using 'zpool online' or replace the device with
- 'zpool replace'.
- scrub: none requested
+ 'zpool replace'.
+ scan: none requested
config:
- NAME STATE READ WRITE CKSUM
- storage DEGRADED 0 0 0
- raidz1 DEGRADED 0 0 0
- da0 ONLINE 0 0 0
- da1 OFFLINE 0 0 0
- da2 ONLINE 0 0 0
+ NAME STATE READ WRITE CKSUM
+ storage DEGRADED 0 0 0
+ raidz1-0 DEGRADED 0 0 0
+ ada0p1 ONLINE 0 0 0
+ ada1p1 OFFLINE 0 0 0
+ ada2p1 ONLINE 0 0 0
errors: No known data errors
....
-"OFFLINE" shows the administrator took [.filename]#da1# offline using:
+"OFFLINE" shows the administrator took [.filename]#ada1p1# offline using:
[source,shell]
....
-# zpool offline storage da1
+# zpool offline storage ada1p1
....
-Power down the computer now and replace [.filename]#da1#.
-Power up the computer and return [.filename]#da1# to the pool:
+Power down the computer now and replace [.filename]#ada1p1#.
+Recreate the GPT partition of type `freebsd-zfs` on the new disk.
+Power up the computer and return [.filename]#ada1p1# to the pool:
[source,shell]
....
-# zpool replace storage da1
+# zpool replace storage ada1p1
....
Next, check the status again, this time without `-x` to display all pools:
@@ -424,17 +426,17 @@
[source,shell]
....
# zpool status storage
- pool: storage
+ pool: storage
state: ONLINE
- scrub: resilver completed with 0 errors on Sat Aug 30 19:44:11 2008
+ scan: resilvered 3.21G in 00:04:36 with 0 errors on Sat Jul 11 10:32:19 2026
config:
- NAME STATE READ WRITE CKSUM
- storage ONLINE 0 0 0
- raidz1 ONLINE 0 0 0
- da0 ONLINE 0 0 0
- da1 ONLINE 0 0 0
- da2 ONLINE 0 0 0
+ NAME STATE READ WRITE CKSUM
+ storage ONLINE 0 0 0
+ raidz1-0 ONLINE 0 0 0
+ ada0p1 ONLINE 0 0 0
+ ada1p1 ONLINE 0 0 0
+ ada2p1 ONLINE 0 0 0
errors: No known data errors
....
@@ -464,23 +466,23 @@
The duration of a scrub depends on the amount of data stored.
Larger amounts of data will take proportionally longer to verify.
-Since scrubbing is I/O intensive, ZFS allows a single scrub to run at a time.
+Since scrubbing is I/O intensive, ZFS allows a single scrub to run on each pool at a time.
After scrubbing completes, view the status with `zpool status`:
[source,shell]
....
# zpool status storage
- pool: storage
+ pool: storage
state: ONLINE
- scrub: scrub completed with 0 errors on Sat Jan 26 19:57:37 2013
+ scan: scrub repaired 0B in 00:19:16 with 0 errors on Sat Jul 11 10:32:19 2026
config:
- NAME STATE READ WRITE CKSUM
- storage ONLINE 0 0 0
- raidz1 ONLINE 0 0 0
- da0 ONLINE 0 0 0
- da1 ONLINE 0 0 0
- da2 ONLINE 0 0 0
+ NAME STATE READ WRITE CKSUM
+ storage ONLINE 0 0 0
+ raidz1-0 ONLINE 0 0 0
+ ada0p1 ONLINE 0 0 0
+ ada1p1 ONLINE 0 0 0
+ ada2p1 ONLINE 0 0 0
errors: No known data errors
....
@@ -502,30 +504,30 @@
[[zfs-zpool-create]]
=== Creating and Destroying Storage Pools
-Creating a ZFS storage pool requires permanent decisions, as the pool structure cannot change after creation.
-The most important decision is which types of vdevs to group the physical disks into.
+The most important decision when creating a ZFS storage pool is which types of vdevs to group the physical disks into.
See the list of crossref:zfs[zfs-term-vdev,vdev types] for details about the possible options.
-After creating the pool, most vdev types do not allow adding disks to the vdev.
-The exceptions are mirrors, which allow adding new disks to the vdev, and stripes, which upgrade to mirrors by attaching a new disk to the vdev.
-Although adding new vdevs expands a pool, the pool layout cannot change after pool creation.
-Instead, back up the data, destroy the pool, and recreate it.
+The vdev types determine the redundancy, capacity, and performance characteristics of a pool.
+A pool's layout is not fixed forever at creation time.
+Mirrors allow adding new disks to the vdev, and stripes upgrade to mirrors by attaching a new disk to the vdev.
+RAID-Z vdevs grow one disk at a time with crossref:zfs[zfs-zpool-raidz-expansion,RAID-Z expansion], which requires OpenZFS 2.3 or later, first available in FreeBSD 15.0.
+Adding new vdevs expands a pool at any time, and crossref:zfs[zfs-zpool-attach,removing top-level vdevs] is possible within limits.
+Reshaping a live pool takes time and I/O bandwidth, so choosing suitable vdev types up front remains important.
-Create a simple mirror pool:
+Create a simple mirror pool on disks containing GPT partitions of type `freebsd-zfs`:
[source,shell]
....
-# zpool create mypool mirror /dev/ada1 /dev/ada2
+# zpool create mypool mirror /dev/ada1p1 /dev/ada2p1
# zpool status
pool: mypool
state: ONLINE
- scan: none requested
config:
NAME STATE READ WRITE CKSUM
mypool ONLINE 0 0 0
mirror-0 ONLINE 0 0 0
- ada1 ONLINE 0 0 0
- ada2 ONLINE 0 0 0
+ ada1p1 ONLINE 0 0 0
+ ada2p1 ONLINE 0 0 0
errors: No known data errors
....
@@ -534,21 +536,20 @@
[source,shell]
....
-# zpool create mypool mirror /dev/ada1 /dev/ada2 mirror /dev/ada3 /dev/ada4
+# zpool create mypool mirror /dev/ada1p1 /dev/ada2p1 mirror /dev/ada3p1 /dev/ada4p1
# zpool status
pool: mypool
state: ONLINE
- scan: none requested
config:
NAME STATE READ WRITE CKSUM
mypool ONLINE 0 0 0
mirror-0 ONLINE 0 0 0
- ada1 ONLINE 0 0 0
- ada2 ONLINE 0 0 0
+ ada1p1 ONLINE 0 0 0
+ ada2p1 ONLINE 0 0 0
mirror-1 ONLINE 0 0 0
- ada3 ONLINE 0 0 0
- ada4 ONLINE 0 0 0
+ ada3p1 ONLINE 0 0 0
+ ada4p1 ONLINE 0 0 0
errors: No known data errors
....
@@ -565,37 +566,80 @@
[source,shell]
....
-# zpool create mypool raidz2 /dev/ada0p3 /dev/ada1p3 /dev/ada2p3 /dev/ada3p3 /dev/ada4p3 /dev/ada5p3
+# zpool create mypool raidz2 /dev/ada0p1 /dev/ada1p1 /dev/ada2p1 /dev/ada3p1 /dev/ada4p1 /dev/ada5p1
# zpool status
pool: mypool
state: ONLINE
- scan: none requested
config:
NAME STATE READ WRITE CKSUM
mypool ONLINE 0 0 0
raidz2-0 ONLINE 0 0 0
- ada0p3 ONLINE 0 0 0
- ada1p3 ONLINE 0 0 0
- ada2p3 ONLINE 0 0 0
- ada3p3 ONLINE 0 0 0
- ada4p3 ONLINE 0 0 0
- ada5p3 ONLINE 0 0 0
+ ada0p1 ONLINE 0 0 0
+ ada1p1 ONLINE 0 0 0
+ ada2p1 ONLINE 0 0 0
+ ada3p1 ONLINE 0 0 0
+ ada4p1 ONLINE 0 0 0
+ ada5p1 ONLINE 0 0 0
errors: No known data errors
....
+ZFS aligns and sizes its smallest writes to each vdev based on the vdev's `ashift` value, the base 2 logarithm of the sector size.
+OpenZFS detects the sector size the disks report when creating a vdev and chooses `ashift` accordingly.
+Some drives report 512-byte sectors for compatibility while using 4096-byte sectors internally.
+Create pools on such drives with an explicit `-o ashift=12` to force 4096-byte alignment:
+
+[source,shell]
+....
+# zpool create -o ashift=12 mypool mirror /dev/ada1p1 /dev/ada2p1
+....
+
+Setting the man:sysctl[8] variable `vfs.zfs.vdev.min_auto_ashift` to `12` gives the same result for pool creation and for later `zpool add` and `zpool attach` operations.
+`ashift` is a property of each vdev, not of the pool as a whole.
+It is fixed when creating the vdev and cannot change afterwards, so verify it before committing data to a pool.
+
Destroy a pool that is no longer needed to reuse the disks.
Destroying a pool requires unmounting the file systems in that pool first.
If any dataset is in use, the unmount operation fails without destroying the pool.
Force the pool destruction with `-f`.
This can cause undefined behavior in applications which had open files on those datasets.
+[[zfs-zpool-props]]
+=== Pool Properties
+
+Like datasets, pools have properties that report status and control behavior.
+Display every property of a pool with `zpool get all _mypool_`, or name specific properties:
+
+[source,shell]
+....
+# zpool get health,capacity mypool
+NAME PROPERTY VALUE SOURCE
+mypool health ONLINE -
+mypool capacity 27% -
+....
+
+Change a writable property with `zpool set`:
+
+[source,shell]
+....
+# zpool set comment="Backup pool" mypool
+# zpool get comment mypool
+NAME PROPERTY VALUE SOURCE
+mypool comment Backup pool local
+....
+
+Properties like `size`, `capacity`, `fragmentation`, and `health` are read-only status values.
+Others change how the pool behaves; this chapter uses `autoexpand` to grow a pool automatically (crossref:zfs[zfs-zpool-online,Growing a Pool]), `autoreplace` with hot spares (crossref:zfs[zfs-zpool-spares,Hot Spares and Automatic Replacement with zfsd]), `autotrim` on SSD pools (crossref:zfs[zfs-zpool-trim,TRIM and Initialization]), and `compatibility` for portable pools (crossref:zfs[zfs-zpool-upgrade,Upgrading a Storage Pool]).
+Set properties at pool creation time by passing `-o` to `zpool create`, as shown with `ashift` above.
+man:zpoolprops[7] describes every pool property.
+
[[zfs-zpool-attach]]
=== Adding and Removing Devices
Two ways exist for adding disks to a pool: attaching a disk to an existing vdev with `zpool attach`, or adding vdevs to the pool with `zpool add`.
Some crossref:zfs[zfs-term-vdev,vdev types] allow adding disks to the vdev after creation.
+RAID-Z vdevs accept new disks only through crossref:zfs[zfs-zpool-raidz-expansion,RAID-Z expansion].
A pool created with a single disk lacks redundancy.
It can detect corruption but can not repair it, because there is no other copy of the data.
@@ -613,7 +657,6 @@
# zpool status
pool: mypool
state: ONLINE
- scan: none requested
config:
NAME STATE READ WRITE CKSUM
@@ -622,14 +665,9 @@
errors: No known data errors
# zpool attach mypool ada0p3 ada1p3
-Make sure to wait until resilvering finishes before rebooting.
-
-If you boot from pool 'mypool', you may need to update boot code on newly attached disk _ada1p3_.
-
-Assuming you use GPT partitioning and _da0_ is your new boot disk you may use the following command:
-
- gpart bootcode -b /boot/pmbr -p /boot/gptzfsboot -i 1 da0
+Make sure to wait until resilver is done before rebooting.
# gpart bootcode -b /boot/pmbr -p /boot/gptzfsboot -i 1 ada1
+partcode written to ada1p1
bootcode written to ada1
# zpool status
pool: mypool
@@ -637,9 +675,9 @@
status: One or more devices is currently being resilvered. The pool will
continue to function, possibly in a degraded state.
action: Wait for the resilver to complete.
- scan: resilver in progress since Fri May 30 08:19:19 2014
- 527M scanned out of 781M at 47.9M/s, 0h0m to go
- 527M resilvered, 67.53% done
+ scan: resilver in progress since Sat Jul 11 11:15:25 2026
+ 738M scanned at 105M/s, 522M issued at 74.6M/s, 781M total
+ 522M resilvered, 66.84% done, 00:00:03 to go
config:
NAME STATE READ WRITE CKSUM
@@ -652,7 +690,7 @@
# zpool status
pool: mypool
state: ONLINE
- scan: resilvered 781M in 0h0m with 0 errors on Fri May 30 08:15:58 2014
+ scan: resilvered 781M in 00:00:16 with 0 errors on Sat Jul 11 11:18:36 2026
config:
NAME STATE READ WRITE CKSUM
@@ -664,7 +702,7 @@
errors: No known data errors
....
-When adding disks to the existing vdev is not an option, as for RAID-Z, an alternative method is to add another vdev to the pool.
+When adding disks to the existing vdev is not an option, an alternative method is to add another vdev to the pool.
Adding vdevs provides higher performance by distributing writes across the vdevs.
Each vdev provides its own redundancy.
Mixing vdev types like `mirror` and `RAID-Z` is possible but discouraged.
@@ -685,7 +723,7 @@
# zpool status
pool: mypool
state: ONLINE
- scan: resilvered 781M in 0h0m with 0 errors on Fri May 30 08:19:35 2014
+ scan: resilvered 781M in 00:00:16 with 0 errors on Sat Jul 11 12:11:56 2026
config:
NAME STATE READ WRITE CKSUM
@@ -697,13 +735,46 @@
errors: No known data errors
# zpool add mypool mirror ada2p3 ada3p3
# gpart bootcode -b /boot/pmbr -p /boot/gptzfsboot -i 1 ada2
+partcode written to ada2p1
bootcode written to ada2
# gpart bootcode -b /boot/pmbr -p /boot/gptzfsboot -i 1 ada3
+partcode written to ada3p1
bootcode written to ada3
# zpool status
pool: mypool
state: ONLINE
- scan: scrub repaired 0 in 0h0m with 0 errors on Fri May 30 08:29:51 2014
+ scan: resilvered 781M in 00:00:16 with 0 errors on Sat Jul 11 09:58:28 2026
+config:
+
+ NAME STATE READ WRITE CKSUM
+ mypool ONLINE 0 0 0
+ mirror-0 ONLINE 0 0 0
+ ada0p3 ONLINE 0 0 0
+ ada1p3 ONLINE 0 0 0
+ mirror-1 ONLINE 0 0 0
+ ada2p3 ONLINE 0 0 0
+ ada3p3 ONLINE 0 0 0
+
+errors: No known data errors
+....
+
+`zpool add` also attaches dedicated log and cache devices to an existing pool.
+A crossref:zfs[zfs-term-vdev-log,log] vdev stores the ZFS intent log on separate low-latency storage, accelerating synchronous writes such as those issued by databases and NFS.
+Mirror log devices, because losing an unmirrored log device together with a system crash costs the pool the last few seconds of synchronous writes; see crossref:zfs[zfs-advanced-zil-slog,"Synchronous Writes, the ZIL, and SLOG"] for sizing and tuning guidance.
+A crossref:zfs[zfs-term-vdev-cache,cache] vdev extends the crossref:zfs[zfs-term-arc,ARC] with a second level of read cache on fast storage.
+Cache devices need no redundancy, as ZFS reads any block that fails to read from the cache from the original pool disks instead.
+The contents of the cache, the crossref:zfs[zfs-term-l2arc,L2ARC], survive reboots by default.
+
+Add a mirrored log vdev and an NVMe cache device to an existing pool:
+
+[source,shell]
+....
+# zpool add mypool log mirror ada4p2 ada5p2
+# zpool add mypool cache nda0p2
+# zpool status mypool
+ pool: mypool
+ state: ONLINE
+ scan: resilvered 781M in 00:00:16 with 0 errors on Sat Jul 11 09:58:28 2026
config:
NAME STATE READ WRITE CKSUM
@@ -714,11 +785,17 @@
mirror-1 ONLINE 0 0 0
ada2p3 ONLINE 0 0 0
ada3p3 ONLINE 0 0 0
+ logs
+ mirror-2 ONLINE 0 0 0
+ ada4p2 ONLINE 0 0 0
+ ada5p2 ONLINE 0 0 0
+ cache
+ nda0p2 ONLINE 0 0 0
errors: No known data errors
....
-Removing vdevs from a pool is impossible and removal of disks from a mirror is exclusive if there is enough remaining redundancy.
+`zpool detach` removes single disks from a mirror vdev when enough redundancy remains.
If a single disk remains in a mirror group, that group ceases to be a mirror and becomes a stripe, risking the entire pool if that remaining disk fails.
Remove a disk from a three-way mirror group:
@@ -728,7 +805,7 @@
# zpool status
pool: mypool
state: ONLINE
- scan: scrub repaired 0 in 0h0m with 0 errors on Fri May 30 08:29:51 2014
+ scan: scrub repaired 0B in 00:01:11 with 0 errors on Sat Jul 11 03:14:02 2026
config:
NAME STATE READ WRITE CKSUM
@@ -743,7 +820,7 @@
# zpool status
pool: mypool
state: ONLINE
- scan: scrub repaired 0 in 0h0m with 0 errors on Fri May 30 08:29:51 2014
+ scan: scrub repaired 0B in 00:01:11 with 0 errors on Sat Jul 11 03:14:02 2026
config:
NAME STATE READ WRITE CKSUM
@@ -755,6 +832,191 @@
errors: No known data errors
....
+`zpool remove` removes entire top-level vdevs from a pool.
+ZFS evacuates the vdev by copying all of its allocated data to the other vdevs in the pool, then detaches the disks.
+Removal works for hot spares, cache, log, special, and dedup devices, and for data vdevs that are single disks or mirrors.
+Data vdevs are not removable from pools that contain a top-level RAID-Z vdev, and all top-level vdevs in the pool must use the same `ashift`.
+The evacuation runs in the background and the pool stays online throughout; monitor progress with `zpool status`.
+After the removal completes, ZFS keeps an in-memory table mapping the blocks of the removed vdev to their new locations.
+The table is small but permanent; `zpool remove -n` estimates its memory use before starting a removal.
+
+Remove one of the two mirror vdevs from a pool:
+
+[source,shell]
+....
+# zpool remove mypool mirror-1
+# zpool status mypool
+ pool: mypool
+ state: ONLINE
+ scan: scrub repaired 0B in 00:01:11 with 0 errors on Sat Jul 11 03:14:02 2026
+remove: Evacuation of mirror in progress since Sat Jul 11 10:15:31 2026
+ 1.71G copied out of 2.32G at 111M/s, 73.71% done, 0h0m to go
+config:
+
+ NAME STATE READ WRITE CKSUM
+ mypool ONLINE 0 0 0
+ mirror-0 ONLINE 0 0 0
+ ada0p3 ONLINE 0 0 0
+ ada1p3 ONLINE 0 0 0
+ mirror-1 ONLINE 0 0 0
+ ada2p3 ONLINE 0 0 0
+ ada3p3 ONLINE 0 0 0
+
+errors: No known data errors
+# zpool status mypool
+ pool: mypool
+ state: ONLINE
+ scan: scrub repaired 0B in 00:01:11 with 0 errors on Sat Jul 11 03:14:02 2026
+remove: Removal of vdev 1 copied 2.32G in 0h0m, completed on Sat Jul 11 10:15:53 2026
+ 10.9K memory used for removed device mappings
+config:
+
+ NAME STATE READ WRITE CKSUM
+ mypool ONLINE 0 0 0
+ mirror-0 ONLINE 0 0 0
+ ada0p3 ONLINE 0 0 0
+ ada1p3 ONLINE 0 0 0
+ indirect-1 ONLINE 0 0 0
+
+errors: No known data errors
+....
+
+The `indirect-1` entry is a placeholder for the removed vdev's remapped blocks and contains no disks.
+Cancel an in-progress removal with `zpool remove -s`.
+
+[[zfs-zpool-raidz-expansion]]
+=== RAID-Z Expansion
+
+RAID-Z expansion grows an existing RAID-Z vdev by one disk at a time, without changing its parity level.
+
+[NOTE]
+====
+RAID-Z expansion requires OpenZFS 2.3 or later, first available in FreeBSD 15.0.
+Pools created on earlier releases need the `raidz_expansion` feature enabled with `zpool upgrade` before expanding.
+====
+
+To expand a RAID-Z vdev, run `zpool attach` with the name of the RAID-Z vdev as shown by `zpool status` and the new disk:
+
+[source,shell]
+....
+# zpool attach mypool raidz2-0 ada6p3
+....
+
+The expansion reflows the existing data across the enlarged set of disks in the background while the pool remains online and in use.
+Add `-w` to make `zpool attach` wait until the expansion completes.
+`zpool status` reports progress on the `expand:` line:
+
+[source,shell]
+....
+# zpool status mypool
+ pool: mypool
+ state: ONLINE
+ scan: scrub repaired 0B in 06:14:36 with 0 errors on Sat Jul 11 04:31:19 2026
+expand: expansion of raidz2-0 in progress since Sat Jul 11 09:42:12 2026
+ 1.83T / 5.36T copied at 186M/s, 34.14% done, 05:31:43 to go
+config:
+
+ NAME STATE READ WRITE CKSUM
+ mypool ONLINE 0 0 0
+ raidz2-0 ONLINE 0 0 0
+ ada0p3 ONLINE 0 0 0
+ ada1p3 ONLINE 0 0 0
+ ada2p3 ONLINE 0 0 0
+ ada3p3 ONLINE 0 0 0
+ ada4p3 ONLINE 0 0 0
+ ada5p3 ONLINE 0 0 0
+ ada6p3 ONLINE 0 0 0
+
+errors: No known data errors
+....
+
+When the expansion finishes, the capacity of the new disk becomes available:
+
+[source,shell]
+....
+# zpool status mypool | grep expand:
+expand: expanded raidz2-0 copied 5.36T in 08:23:35, on Sat Jul 11 18:05:47 2026
+....
+
+Blocks written before the expansion keep their old data-to-parity ratio and continue to occupy space accordingly.
+The full capacity gain from the new disk therefore applies to data written after the expansion, while old data benefits only when rewritten, for example with crossref:zfs[zfs-zfs-rewrite,`zfs rewrite`].
+Repeat the procedure to grow a vdev by several disks, attaching one at a time.
+
+[[zfs-zpool-draid]]
+=== dRAID Pools
+
+dRAID is a variant of RAID-Z that distributes hot spare capacity across all disks in the vdev.
+A crossref:zfs[zfs-term-vdev-draid,dRAID] vdev is built from internal RAID-Z groups plus optional _distributed spares_, all spread evenly over every disk.
+When a disk fails, ZFS rebuilds into the distributed spare by reading from and writing to all remaining disks in parallel, completing in a fraction of the time a resilver onto a single spare disk takes.
+Replacing the failed disk then rebuilds it from the distributed spare, restoring full protection.
+dRAID targets pools with many disks, where RAID-Z rebuild times grow dangerously long.
+
+The vdev type spells out the complete layout, per man:zpoolconcepts[7]:
+
+[.programlisting]
+....
+draid[parity][:datad][:childrenc][:sparess]
+....
+
+_parity_ is the parity level of the internal groups, 1 to 3, defaulting to 1.
+_data_ is the number of data devices per redundancy group, defaulting to 8.
+_children_ is the total number of disks, serving as a cross-check when listing many devices.
+_spares_ is the number of distributed hot spares, defaulting to 0.
+
+Create a pool from a 24-disk dRAID vdev with double parity, 8 data disks per redundancy group, and 2 distributed spares:
+
+[source,shell]
+....
+# zpool create mypool draid2:8d:24c:2s ada0 ada1 ada2 ada3 ada4 ada5 ada6 ada7 ada8 ada9 ada10 ada11 ada12 ada13 ada14 ada15 ada16 ada17 ada18 ada19 ada20 ada21 ada22 ada23
+....
+
+The distributed spares appear under `spares` in `zpool status`, with names like `draid2-0-0` for the first spare of the first vdev.
+
+dRAID trades space efficiency for rebuild speed.
+Unlike RAID-Z, dRAID uses a fixed stripe width, padding smaller writes with zeros.
+With the default of 8 data disks and 4 KB sectors, the minimum allocation is 32 KB, which reduces usable capacity and compression effectiveness for datasets dominated by small blocks.
+The capacity of the distributed spares is also committed up front, whether or not a disk ever fails.
+Pairing a dRAID pool with a crossref:zfs[zfs-zpool-special,special vdev] keeps metadata and small blocks off the wide stripes and recovers much of the lost efficiency.
+For pools with a handful of disks, RAID-Z remains the better choice.
+
+[[zfs-zpool-special]]
+=== Special Allocation Classes
+
+Special allocation classes dedicate vdevs to specific types of pool data.
+A crossref:zfs[zfs-term-vdev-special,special] vdev stores pool metadata such as indirect blocks and dnodes, and optionally the data blocks of small files.
+A `dedup` vdev stores the crossref:zfs[zfs-term-deduplication,deduplication] tables.
+Placing this metadata on fast devices such as NVMe mirrors speeds up metadata-heavy operations like directory traversal, `zfs list`, and deduplication lookups on pools of otherwise slower disks.
+
+Add a mirrored special vdev to an existing pool:
+
+[source,shell]
+....
+# zpool add mypool special mirror nda0 nda1
+....
+
+New metadata allocations then go to the special vdev; existing metadata stays where it is until rewritten.
+
+Setting the `special_small_blocks` dataset property makes the special vdev store data blocks up to the given size as well:
+
+[source,shell]
+....
+# zfs set special_small_blocks=16K mypool/projects
+....
+
+Valid values are zero, which disables storing data blocks in the special class, or a power of two from 512 bytes up to 1 MB on FreeBSD 14.x; FreeBSD 15.0 accepts values up to the maximum block size of 16 MB.
+Setting the property to the dataset's `recordsize` sends all data blocks of that dataset to the special class.
+When a special vdev fills up, new allocations spill back to the normal class instead of failing.
+
+[WARNING]
+====
+A special vdev is not a cache.
+It holds the only copy of the pool metadata allocated to it, and losing it destroys the pool.
+Match the redundancy of special and dedup vdevs to the redundancy of the data vdevs, for example by using a mirror.
+====
+
+Removing a special or dedup vdev with `zpool remove` is possible, subject to the restrictions described in crossref:zfs[zfs-zpool-attach,Adding and Removing Devices].
+In practice, the matching `ashift` requirement often prevents removal when the special vdev uses devices with a different sector size than the data vdevs.
+
[[zfs-zpool-status]]
=== Checking the Status of a Pool
@@ -768,7 +1030,7 @@
# zpool status
pool: mypool
state: ONLINE
- scan: scrub repaired 0 in 2h25m with 0 errors on Sat Sep 14 04:25:50 2013
+ scan: scrub repaired 0B in 02:25:36 with 0 errors on Sat Jul 11 06:51:26 2026
config:
NAME STATE READ WRITE CKSUM
@@ -784,6 +1046,17 @@
errors: No known data errors
....
+Options adjust the output to the task at hand:
+
+* `-v` displays verbose data error information, printing a complete list of the errors found since the last complete pool scrub, including the names of affected files.
+* `-x` displays the status of pools with errors or pools that are otherwise unavailable, hiding healthy pools.
+* `-s` displays the number of slow I/O operations on each leaf vdev, meaning operations that did not complete within 30 seconds.
+* `-e` displays unhealthy vdevs only, keeping the output short for pools with many devices; this flag requires FreeBSD 14.1 or later.
+* `-p` displays numbers as exact, parseable values instead of rounded human-readable ones.
+* `-t` displays the crossref:zfs[zfs-zpool-trim,TRIM] status of each vdev.
+
+Refer to man:zpool-status[8] for the complete list of options.
+
[[zfs-zpool-clear]]
=== Clearing Errors
@@ -810,7 +1083,6 @@
# zpool status
pool: mypool
state: ONLINE
- scan: none requested
config:
NAME STATE READ WRITE CKSUM
@@ -823,11 +1095,11 @@
# zpool replace mypool ada1p3 ada2p3
Make sure to wait until resilvering finishes before rebooting.
-When booting from the pool 'zroot', update the boot code on the newly attached disk 'ada2p3'.
+When booting from the pool 'mypool', update the boot code on the newly attached disk 'ada2p3'.
-Assuming GPT partitioning is used and [.filename]#da0# is the new boot disk, use the following command:
+Assuming GPT partitioning is used and ada0 is the new boot disk, use the following command:
- gpart bootcode -b /boot/pmbr -p /boot/gptzfsboot -i 1 da0
+ gpart bootcode -b /boot/pmbr -p /boot/gptzfsboot -i 1 ada0
# gpart bootcode -b /boot/pmbr -p /boot/gptzfsboot -i 1 ada2
# zpool status
pool: mypool
@@ -835,9 +1107,9 @@
status: One or more devices is currently being resilvered. The pool will
continue to function, possibly in a degraded state.
action: Wait for the resilver to complete.
- scan: resilver in progress since Mon Jun 2 14:21:35 2014
- 604M scanned out of 781M at 46.5M/s, 0h0m to go
- 604M resilvered, 77.39% done
+ scan: resilver in progress since Sat Jul 11 14:21:35 2026
+ 781M / 781M scanned, 604M / 781M issued at 101M/s
+ 604M resilvered, 77.39% done, 00:00:01 to go
config:
NAME STATE READ WRITE CKSUM
@@ -852,7 +1124,7 @@
# zpool status
pool: mypool
state: ONLINE
- scan: resilvered 781M in 0h0m with 0 errors on Mon Jun 2 14:21:52 2014
+ scan: resilvered 781M in 00:00:17 with 0 errors on Sat Jul 11 14:21:52 2026
config:
NAME STATE READ WRITE CKSUM
@@ -864,21 +1136,24 @@
errors: No known data errors
....
+On mirrored vdevs, adding `-s` to `zpool replace` or `zpool attach` performs a _sequential resilver_ instead of a healing resilver.
+A sequential resilver copies the data in disk order without verifying each block checksum, restoring redundancy much sooner.
+Since the copy skips checksum verification, ZFS starts a crossref:zfs[zfs-zpool-scrub,scrub] of the pool automatically after the sequential resilver completes.
+Sequential resilvering works on mirror and crossref:zfs[zfs-zpool-draid,dRAID] vdevs, not on RAID-Z.
+Restart an in-progress resilver from the beginning with `zpool resilver _mypool_`.
+
[[zfs-zpool-resilver]]
=== Dealing with Failed Devices
-When a disk in a pool fails, the vdev to which the disk belongs enters the
-crossref:zfs[zfs-term-degraded,degraded] state.
+When a disk in a pool fails, the vdev to which the disk belongs enters the crossref:zfs[zfs-term-degraded,degraded] state.
The data is still available, but with reduced performance because ZFS computes missing data from the available redundancy.
To restore the vdev to a fully functional state, replace the failed physical device.
ZFS is then instructed to begin the crossref:zfs[zfs-term-resilver,resilver] operation.
ZFS recomputes data on the failed device from available redundancy and writes it to the replacement device.
After completion, the vdev returns to crossref:zfs[zfs-term-online,online] status.
-If the vdev does not have any redundancy, or if devices have failed and there is
-not enough redundancy to compensate, the pool enters the
-crossref:zfs[zfs-term-faulted,faulted] state.
-Unless enough devices can reconnect the pool becomes inoperative requiring a data restore from backups.
+If the vdev does not have any redundancy, or if devices have failed and there is not enough redundancy to compensate, the pool enters the crossref:zfs[zfs-term-faulted,faulted] state.
+Unless enough devices can reconnect, the pool becomes inoperative, requiring a data restore from backups.
When replacing a failed disk, the name of the failed disk changes to the GUID of the new disk.
A new device name parameter for `zpool replace` is not required if the replacement device has the same device name.
@@ -893,8 +1168,7 @@
status: One or more devices could not be opened. Sufficient replicas exist for
the pool to continue functioning in a degraded state.
action: Attach the missing device and online it using 'zpool online'.
- see: http://illumos.org/msg/ZFS-8000-2Q
- scan: none requested
+ see: https://openzfs.github.io/openzfs-docs/msg/ZFS-8000-2Q
config:
NAME STATE READ WRITE CKSUM
@@ -911,9 +1185,9 @@
status: One or more devices is currently being resilvered. The pool will
continue to function, possibly in a degraded state.
action: Wait for the resilver to complete.
- scan: resilver in progress since Mon Jun 2 14:52:21 2014
- 641M scanned out of 781M at 49.3M/s, 0h0m to go
- 640M resilvered, 82.04% done
+ scan: resilver in progress since Sat Jul 11 14:52:21 2026
+ 781M / 781M scanned, 641M / 781M issued at 128M/s
+ 640M resilvered, 82.04% done, 00:00:01 to go
config:
NAME STATE READ WRITE CKSUM
@@ -928,7 +1202,7 @@
# zpool status
pool: mypool
state: ONLINE
- scan: resilvered 781M in 0h0m with 0 errors on Mon Jun 2 14:52:38 2014
+ scan: resilvered 781M in 00:00:17 with 0 errors on Sat Jul 11 14:52:38 2026
config:
NAME STATE READ WRITE CKSUM
@@ -940,13 +1214,109 @@
errors: No known data errors
....
+[[zfs-zpool-spares]]
+=== Hot Spares and Automatic Replacement with zfsd
+
+Pools with redundant vdevs can register crossref:zfs[zfs-term-vdev-spare,hot spare] disks that stand by to replace a failed device.
+A spare must be at least as large as the device it replaces.
+Specify spares at pool creation time by listing them after the `spare` keyword, as in `zpool create _mypool_ mirror _ada0p3_ _ada1p3_ spare _ada3p3_`, or add them to an existing pool with `zpool add`:
+
+[source,shell]
+....
+# zpool add mypool spare ada3p3
+# zpool status
+ pool: mypool
+ state: ONLINE
+config:
+
+ NAME STATE READ WRITE CKSUM
+ mypool ONLINE 0 0 0
+ mirror-0 ONLINE 0 0 0
+ ada0p3 ONLINE 0 0 0
+ ada1p3 ONLINE 0 0 0
+ spares
+ ada3p3 AVAIL
+
+errors: No known data errors
+....
+
+A spare stays idle, shown as `AVAIL`, until a device fails.
+Activate a spare by hand with `zpool replace`, naming the failed device and the spare:
+
+[source,shell]
+....
+# zpool replace mypool ada1p3 ada3p3
+# zpool status
+ pool: mypool
+ state: DEGRADED
+status: One or more devices could not be opened. Sufficient replicas exist for
+ the pool to continue functioning in a degraded state.
+action: Attach the missing device and online it using 'zpool online'.
+ see: https://openzfs.github.io/openzfs-docs/msg/ZFS-8000-2Q
+ scan: resilvered 781M in 00:00:21 with 0 errors on Sat Jul 11 09:14:33 2026
+config:
+
+ NAME STATE READ WRITE CKSUM
+ mypool DEGRADED 0 0 0
+ mirror-0 DEGRADED 0 0 0
+ ada0p3 ONLINE 0 0 0
+ spare-1 DEGRADED 0 0 0
+ ada1p3 UNAVAIL 0 0 0 cannot open
+ ada3p3 ONLINE 0 0 0
+ spares
+ ada3p3 INUSE currently in use
+
+errors: No known data errors
+....
+
+The spare replacement is temporary by design, and the pool stays in this state until an administrator resolves it.
+After physically replacing the failed disk and resilvering the new device with crossref:zfs[zfs-zpool-replace,`zpool replace`], return the spare to standby with `zpool detach _mypool_ _ada3p3_`.
+To promote the spare to a permanent member of the vdev instead, detach the failed device with `zpool detach _mypool_ _ada1p3_`.
+
+The `autoreplace` pool property, off by default, controls what happens when a new disk appears at the same physical location as a device that previously belonged to the pool.
+With `autoreplace` set to `on`, ZFS replaces the old device with the new disk automatically, without requiring a `zpool replace` command:
+
+[source,shell]
+....
+# zpool set autoreplace=on mypool
+....
+
+On FreeBSD, automatic reactions to failures, including honoring `autoreplace`, are performed by man:zfsd[8], the ZFS fault management daemon in the base system.
+The daemon is not enabled by default.
+Enable and start it with:
+
+[source,shell]
+....
+# sysrc zfsd_enable="YES"
+# service zfsd start
+....
+
+man:zfsd[8] listens for pool events and:
+
+* Activates a hot spare when a device disappears from a redundant vdev or when a vdev becomes degraded or faulted.
+* Marks a vdev as faulted when it produces more than 50 I/O errors or more than 8 delayed I/O operations in a 60 second period, then activates a hot spare.
+* Marks a vdev as degraded when it produces more than 50 checksum errors in a 60 second period, then activates a hot spare.
+* Brings a device back online when it reappears, triggering a resilver of the data written while it was missing.
+* Replaces a missing device with a new disk that appears at the same physical location when the pool has `autoreplace` set to `on`.
+* Returns hot spares to standby once the resilver of a permanent replacement completes.
+
+[NOTE]
+====
+FreeBSD uses man:zfsd[8] for fault management.
+The ZFS Event Daemon (ZED) found on Linux OpenZFS platforms is not part of FreeBSD.
+====
+
+Inspect the event stream that drives these decisions with `zpool events`.
+Adding `-v` shows the full payload of each event, and `-f` keeps the command running, printing new events as they arrive.
+ZFS also delivers these events to man:devd[8].
+The rules in [.filename]#/etc/devd/zfs.conf# log them to the system log and serve as a template for custom actions, such as sending mail when a pool becomes degraded.
+
[[zfs-zpool-scrub]]
=== Scrubbing a Pool
Routinely crossref:zfs[zfs-term-scrub,scrub] pools, ideally at least once every month.
The `scrub` operation is disk-intensive and will reduce performance while running.
-Avoid high-demand periods when scheduling `scrub` or use
-crossref:zfs[zfs-advanced-tuning-scrub_delay,`vfs.zfs.scrub_delay`] to adjust the relative priority of the `scrub` to keep it from slowing down other workloads.
+Avoid high-demand periods when scheduling `scrub`, or lower its impact with the I/O scheduler tunables described in crossref:zfs[zfs-advanced-tuning,Tuning].
[source,shell]
....
@@ -954,9 +1324,9 @@
# zpool status
pool: mypool
state: ONLINE
- scan: scrub in progress since Wed Feb 19 20:52:54 2014
- 116G scanned out of 8.60T at 649M/s, 3h48m to go
- 0 repaired, 1.32% done
+ scan: scrub in progress since Sat Jul 11 20:52:54 2026
+ 130G / 8.60T scanned at 649M/s, 116G / 8.60T issued at 580M/s
+ 0B repaired, 1.32% done, 04:15:41 to go
config:
NAME STATE READ WRITE CKSUM
@@ -972,7 +1342,23 @@
errors: No known data errors
....
-To cancel a scrub operation if needed, run `zpool scrub -s _mypool_`.
+Pause a running scrub with `zpool scrub -p _mypool_` when it interferes with other work; running `zpool scrub _mypool_` again resumes it from where it stopped.
+To cancel a scrub operation instead, run `zpool scrub -s _mypool_`.
+`zpool scrub -w _mypool_` waits until the scrub completes before returning, which is useful in scripts.
+After a scrub reports errors, `zpool scrub -e _mypool_` performs an _error scrub_ that re-verifies only the blocks with known errors, finishing much faster than a full scrub.
+The error scrub requires OpenZFS 2.2, first shipped in FreeBSD 14.0.
+
+man:periodic[8] can run scrubs automatically.
+To scrub every pool whose last scrub is more than 35 days old, add this line to [.filename]#/etc/periodic.conf#:
+
+[.programlisting]
+....
+daily_scrub_zfs_enable="YES"
+....
+
+Set `daily_scrub_zfs_default_threshold="_days_"` to change the number of days between scrubs.
+man:periodic.conf[5] also describes per-pool thresholds.
+Setting `daily_status_zfs_enable="YES"` adds a pool health check to the daily man:periodic[8] report.
[[zfs-zpool-selfheal]]
=== Self-Healing
@@ -987,27 +1373,26 @@
If that disk can provide the correct data, ZFS will give that to the application and correct the data on the disk with the wrong checksum.
This happens without any interaction from a system administrator during normal pool operation.
-The next example shows this self-healing behavior by creating a mirrored pool of disks [.filename]#/dev/ada0# and [.filename]#/dev/ada1#.
+The next example shows this self-healing behavior by creating a mirrored pool of partitions [.filename]#/dev/ada0p1# and [.filename]#/dev/ada1p1#.
[source,shell]
....
-# zpool create healer mirror /dev/ada0 /dev/ada1
+# zpool create healer mirror /dev/ada0p1 /dev/ada1p1
# zpool status healer
pool: healer
state: ONLINE
- scan: none requested
config:
NAME STATE READ WRITE CKSUM
healer ONLINE 0 0 0
mirror-0 ONLINE 0 0 0
- ada0 ONLINE 0 0 0
- ada1 ONLINE 0 0 0
+ ada0p1 ONLINE 0 0 0
+ ada1p1 ONLINE 0 0 0
errors: No known data errors
# zpool list
-NAME SIZE ALLOC FREE CKPOINT EXPANDSZ FRAG CAP DEDUP HEALTH ALTROOT
-healer 960M 92.5K 960M - - 0% 0% 1.00x ONLINE -
+NAME SIZE ALLOC FREE CKPOINT EXPANDSZ FRAG CAP DEDUP HEALTH ALTROOT
+healer 960M 111K 960M - - 0% 0% 1.00x ONLINE -
....
Copy some important data to the pool to protect from data errors using the self-healing feature and create a checksum of the pool for later comparison.
@@ -1015,9 +1400,9 @@
[source,shell]
....
# cp /some/important/data /healer
-# zfs list
-NAME SIZE ALLOC FREE CAP DEDUP HEALTH ALTROOT
-healer 960M 67.7M 892M 7% 1.00x ONLINE -
+# zpool list
+NAME SIZE ALLOC FREE CKPOINT EXPANDSZ FRAG CAP DEDUP HEALTH ALTROOT
+healer 960M 67.7M 892M - - 0% 7% 1.00x ONLINE -
# sha1 /healer > checksum.txt
# cat checksum.txt
SHA1 (/healer) = 2753eff56d77d9a536ece6694bf0a82740344d1f
@@ -1038,7 +1423,7 @@
[source,shell]
....
# zpool export healer
-# dd if=/dev/random of=/dev/ada1 bs=1m count=200
+# dd if=/dev/random of=/dev/ada1p1 bs=1m count=200
200+0 records in
200+0 records out
209715200 bytes transferred in 62.992162 secs (3329227 bytes/sec)
@@ -1047,32 +1432,31 @@
The pool status shows that one device has experienced an error.
Note that applications reading data from the pool did not receive any incorrect data.
-ZFS provided data from the [.filename]#ada0# device with the correct checksums.
+ZFS provided data from the [.filename]#ada0p1# device with the correct checksums.
To find the device with the wrong checksum, look for one whose `CKSUM` column contains a nonzero value.
[source,shell]
....
# zpool status healer
- pool: healer
- state: ONLINE
- status: One or more devices has experienced an unrecoverable error. An
- attempt was made to correct the error. Applications are unaffected.
- action: Determine if the device needs to be replaced, and clear the errors
- using 'zpool clear' or replace the device with 'zpool replace'.
- see: http://illumos.org/msg/ZFS-8000-4J
- scan: none requested
- config:
-
- NAME STATE READ WRITE CKSUM
- healer ONLINE 0 0 0
- mirror-0 ONLINE 0 0 0
- ada0 ONLINE 0 0 0
- ada1 ONLINE 0 0 1
+ pool: healer
+ state: ONLINE
+status: One or more devices has experienced an unrecoverable error. An
+ attempt was made to correct the error. Applications are unaffected.
+action: Determine if the device needs to be replaced, and clear the errors
+ using 'zpool clear' or replace the device with 'zpool replace'.
+ see: https://openzfs.github.io/openzfs-docs/msg/ZFS-8000-9P
+config:
+
+ NAME STATE READ WRITE CKSUM
+ healer ONLINE 0 0 0
+ mirror-0 ONLINE 0 0 0
+ ada0p1 ONLINE 0 0 0
+ ada1p1 ONLINE 0 0 1
errors: No known data errors
....
-ZFS detected the error and handled it by using the redundancy present in the unaffected [.filename]#ada0# mirror disk.
+ZFS detected the error and handled it by using the redundancy present in the unaffected [.filename]#ada0p1# mirror device.
A checksum comparison with the original one will reveal whether the pool is consistent again.
[source,shell]
@@ -1089,7 +1473,7 @@
A pool consisting of a single device has no self-healing capabilities.
That is also the reason why checksums are so important in ZFS; do not disable them for any reason.
ZFS requires no man:fsck[8] or similar file system consistency check program to detect and correct this, and keeps the pool available while there is a problem.
-A scrub operation is now required to overwrite the corrupted data on [.filename]#ada1#.
+A scrub operation is now required to overwrite the corrupted data on [.filename]#ada1p1#.
[source,shell]
....
@@ -1098,25 +1482,25 @@
pool: healer
state: ONLINE
status: One or more devices has experienced an unrecoverable error. An
- attempt was made to correct the error. Applications are unaffected.
+ attempt was made to correct the error. Applications are unaffected.
action: Determine if the device needs to be replaced, and clear the errors
- using 'zpool clear' or replace the device with 'zpool replace'.
- see: http://illumos.org/msg/ZFS-8000-4J
- scan: scrub in progress since Mon Dec 10 12:23:30 2012
- 10.4M scanned out of 67.0M at 267K/s, 0h3m to go
- 9.63M repaired, 15.56% done
+ using 'zpool clear' or replace the device with 'zpool replace'.
+ see: https://openzfs.github.io/openzfs-docs/msg/ZFS-8000-9P
+ scan: scrub in progress since Sat Jul 11 10:34:02 2026
+ 40.8M scanned at 20.4M/s, 24.9M issued at 12.4M/s, 67.0M total
+ 9.63M repaired, 37.16% done, 00:00:03 to go
config:
NAME STATE READ WRITE CKSUM
healer ONLINE 0 0 0
mirror-0 ONLINE 0 0 0
- ada0 ONLINE 0 0 0
- ada1 ONLINE 0 0 627 (repairing)
+ ada0p1 ONLINE 0 0 0
+ ada1p1 ONLINE 0 0 627 (repairing)
errors: No known data errors
....
-The scrub operation reads data from [.filename]#ada0# and rewrites any data with a wrong checksum on [.filename]#ada1#, shown by the `(repairing)` output from `zpool status`.
+The scrub operation reads data from [.filename]#ada0p1# and rewrites any data with a wrong checksum on [.filename]#ada1p1#, shown by the `(repairing)` output from `zpool status`.
After the operation is complete, the pool status changes to:
[source,shell]
@@ -1127,22 +1511,22 @@
status: One or more devices has experienced an unrecoverable error. An
attempt was made to correct the error. Applications are unaffected.
action: Determine if the device needs to be replaced, and clear the errors
- using 'zpool clear' or replace the device with 'zpool replace'.
- see: http://illumos.org/msg/ZFS-8000-4J
- scan: scrub repaired 66.5M in 0h2m with 0 errors on Mon Dec 10 12:26:25 2012
+ using 'zpool clear' or replace the device with 'zpool replace'.
+ see: https://openzfs.github.io/openzfs-docs/msg/ZFS-8000-9P
+ scan: scrub repaired 66.5M in 00:00:06 with 0 errors on Sat Jul 11 10:34:08 2026
config:
NAME STATE READ WRITE CKSUM
healer ONLINE 0 0 0
mirror-0 ONLINE 0 0 0
- ada0 ONLINE 0 0 0
- ada1 ONLINE 0 0 2.72K
+ ada0p1 ONLINE 0 0 0
+ ada1p1 ONLINE 0 0 2.72K
errors: No known data errors
....
After the scrubbing operation completes with all the data synchronized from
-[.filename]#ada0# to [.filename]#ada1#, crossref:zfs[zfs-zpool-clear,clear] the error messages from the pool status by running `zpool clear`.
+[.filename]#ada0p1# to [.filename]#ada1p1#, crossref:zfs[zfs-zpool-clear,clear] the error messages from the pool status by running `zpool clear`.
[source,shell]
....
@@ -1150,20 +1534,124 @@
# zpool status healer
pool: healer
state: ONLINE
- scan: scrub repaired 66.5M in 0h2m with 0 errors on Mon Dec 10 12:26:25 2012
+ scan: scrub repaired 66.5M in 00:00:06 with 0 errors on Sat Jul 11 10:34:08 2026
config:
NAME STATE READ WRITE CKSUM
healer ONLINE 0 0 0
mirror-0 ONLINE 0 0 0
- ada0 ONLINE 0 0 0
- ada1 ONLINE 0 0 0
+ ada0p1 ONLINE 0 0 0
+ ada1p1 ONLINE 0 0 0
errors: No known data errors
....
The pool is now back to a fully working state, with all error counts now zero.
+[[zfs-zpool-trim]]
+=== TRIM and Initialization
+
+Solid state drives and thinly provisioned storage perform best when ZFS tells them which blocks are no longer in use.
+`zpool trim` passes that information to every device in the pool that supports the TRIM or UNMAP commands:
+
+[source,shell]
+....
+# zpool trim mypool
+....
+
+Trim a single device by naming it after the pool, as in `zpool trim mypool nda0p3`.
+`zpool trim -w` waits until trimming completes, `-c` cancels a running trim, and `-s` suspends it; running `zpool trim` again resumes a suspended trim.
+On devices that support it, `zpool trim --secure` requests a secure TRIM, where the device guarantees erasing the data stored on the trimmed blocks.
+
+`zpool status -t` shows the trim progress for each device:
+
+[source,shell]
+....
+# zpool status -t mypool
+ pool: mypool
+ state: ONLINE
+ scan: scrub repaired 0B in 00:12:33 with 0 errors on Sat Jul 11 03:31:11 2026
+config:
+
+ NAME STATE READ WRITE CKSUM
+ mypool ONLINE 0 0 0
+ mirror-0 ONLINE 0 0 0
+ nda0p3 ONLINE 0 0 0 (23% trimmed, started at Sat Jul 11 09:15:04 2026)
+ nda1p3 ONLINE 0 0 0 (22% trimmed, started at Sat Jul 11 09:15:04 2026)
+
+errors: No known data errors
+....
+
+Setting the `autotrim` pool property to `on` makes ZFS issue small TRIM commands continuously as the pool frees space:
+
+[source,shell]
+....
+# zpool set autotrim=on mypool
+....
+
+Automatic trimming skips small freed regions and puts a constant extra load on the devices.
+For most systems, leaving `autotrim` off and running a full `zpool trim` on a schedule gives better results.
+On FreeBSD 14.1 and later, man:periodic[8] runs such a trim daily when [.filename]#/etc/periodic.conf# contains `daily_trim_zfs_enable="YES"`.
+
+`zpool initialize` is the counterpart of trimming: it writes a pattern to all unallocated space on the specified devices, or on every eligible device in the pool when given no device names.
+This forces the backing storage to allocate the space up front, avoiding first-write latency on thinly provisioned virtual disks, and exercises new devices before trusting them with data.
+
+[source,shell]
+....
+# zpool initialize mypool
+....
+
+As with trimming, `-c` cancels an initialization, `-s` suspends it, and `-w` waits for it to finish; `-u` removes the initialization state from the devices.
+See man:zpool-trim[8] and man:zpool-initialize[8] for details.
+
+[[zfs-zpool-checkpoint]]
+=== Pool Checkpoints
+
+`zpool checkpoint` saves the state of the entire pool, including every dataset and the pool configuration, so that a later rewind returns the pool to the exact state it had when creating the checkpoint.
+Create a checkpoint before risky administrative operations such as upgrading the operating system, enabling new pool features, or reorganizing datasets:
+
+[source,shell]
+....
+# zpool checkpoint mypool
+....
+
+A pool holds at most one checkpoint at a time.
+The `CKPOINT` column of `zpool list` shows how much space the checkpoint consumes as the pool diverges from the saved state:
+
+[source,shell]
+....
+# zpool list mypool
+NAME SIZE ALLOC FREE CKPOINT EXPANDSZ FRAG CAP DEDUP HEALTH ALTROOT
+mypool 4.50T 1.42T 3.08T 221M - 4% 31% 1.00x ONLINE -
+....
+
+To rewind, export the pool and import it again with `--rewind-to-checkpoint`:
+
+[source,shell]
+....
+# zpool export mypool
+# zpool import --rewind-to-checkpoint mypool
+....
+
+Rewinding discards every change made to the pool after creating the checkpoint, including newer snapshots and property changes.
+Discard a checkpoint that is no longer needed to release the space it holds; `-w` waits until the discard completes:
+
+[source,shell]
+....
+# zpool checkpoint -d -w mypool
+....
+
+While a checkpoint exists, ZFS refuses operations that change the pool structure: `zpool remove`, `zpool attach`, `zpool detach`, `zpool split`, and `zpool reguid`.
+Space freed after creating the checkpoint is not reclaimed until the checkpoint is discarded, so a long-lived checkpoint on a busy pool can fill it up.
+
+[NOTE]
+====
+A checkpoint is not a replacement for crossref:zfs[zfs-zfs-snapshot,snapshots] or backups.
+It protects against administrative mistakes for a short period, lives on the same disks as the pool, and rewinding discards everything written after its creation.
+====
+
+See man:zpool-checkpoint[8] for details.
+
[[zfs-zpool-online]]
=== Growing a Pool
@@ -1176,9 +1664,19 @@
When replacing the 1 TB drive with another 2 TB drive, the resilvering process copies the existing data onto the new drive.
As both of the devices now have 2 TB capacity, the mirror's available space grows to 2 TB.
-Start expansion by using `zpool online -e` on each device.
+The pool does not use the new capacity automatically by default.
+Set the `autoexpand` pool property to `on` before replacing the devices and the pool grows as soon as the last smaller device leaves the vdev:
+
+[source,shell]
+....
+# zpool set autoexpand=on mypool
+....
+
+Otherwise, start expansion by using `zpool online -e` on each device.
After expanding all devices, the extra space becomes available to the pool.
+To grow a RAID-Z vdev by adding a disk instead of replacing the existing ones, see crossref:zfs[zfs-zpool-raidz-expansion,RAID-Z Expansion].
+
[[zfs-zpool-import]]
=== Importing and Exporting Pools
@@ -1199,10 +1697,16 @@
Importing a pool automatically mounts the datasets.
If this is undesired behavior, use `zpool import -N` to prevent it.
`zpool import -o` sets temporary properties for this specific import.
-`zpool import altroot=` allows importing a pool with a base mount point instead of the root of the file system.
+`zpool import -o altroot=` allows importing a pool with a base mount point instead of the root of the file system.
If the pool was last used on a different system and was not properly exported, force the import using `zpool import -f`.
`zpool import -a` imports all pools that do not appear to be in use by another system.
+man:zpool-import[8] also provides options for recovering damaged pools.
+`zpool import -F` attempts to rewind the pool to an earlier transaction group when the most recent ones are damaged, discarding the last few seconds of writes.
+`zpool import -m` allows importing a pool whose dedicated log device is missing.
+`zpool import -o readonly=on` imports the pool read-only, preventing all writes, which is useful for forensics or when rescuing data from a failing pool.
+`zpool import -R /mnt` combines `altroot=/mnt` with `cachefile=none`, keeping a rescue import from disturbing the mount paths and the pool cache of the running system.
+
List all available pools for import:
[source,shell]
@@ -1214,8 +1718,8 @@
action: The pool can be imported using its name or numeric identifier.
config:
- mypool ONLINE
- ada2p3 ONLINE
+ mypool ONLINE
+ ada0p1 ONLINE
....
Import the pool with an alternative root directory:
@@ -1224,125 +1728,108 @@
....
# zpool import -o altroot=/mnt mypool
# zfs list
-zfs list
NAME USED AVAIL REFER MOUNTPOINT
mypool 110K 47.0G 31K /mnt/mypool
....
+At boot, the ZFS startup scripts enabled by `zfs_enable="YES"` in [.filename]#/etc/rc.conf# import every pool recorded in [.filename]#/etc/zfs/zpool.cache#.
+Importing a pool adds it to that cache file automatically; the `cachefile` pool property controls this behavior.
+Pools imported with `cachefile=none` do not come back automatically after a reboot.
+
[[zfs-zpool-upgrade]]
=== Upgrading a Storage Pool
-After upgrading FreeBSD, or if importing a pool from a system using an older version, manually upgrade the pool to the latest ZFS version to support newer features.
+After upgrading FreeBSD, or if importing a pool from a system using an older version, manually upgrade the pool to make new on-disk features available.
Consider whether the pool may ever need importing on an older system before upgrading.
Upgrading is a one-way process.
-Upgrade older pools is possible, but downgrading pools with newer features is not.
+Upgrading older pools is possible, but downgrading pools with newer features is not.
+
+Pools do not carry a single version number anymore.
+Instead, each on-disk format change is an individual _feature flag_, described in man:zpool-features[7].
+A feature is _enabled_ when the pool allows its use and becomes _active_ once the pool stores data that depends on it.
+An enabled but inactive feature does not affect compatibility.
+Once a feature is active, software without support for it can no longer import the pool, although pools whose active features are all "read-only compatible" still allow read-only imports.
-Upgrade a v28 pool to support `Feature Flags`:
+`zpool status` reports when supported features are not enabled on a pool:
[source,shell]
....
-# zpool status
+# zpool status mypool
pool: mypool
state: ONLINE
-status: The pool is formatted using a legacy on-disk format. The pool can
- still be used, but some features are unavailable.
-action: Upgrade the pool using 'zpool upgrade'. Once this is done, the
- pool will no longer be accessible on software that does not support feat
- flags.
- scan: none requested
+status: Some supported and requested features are not enabled on the pool.
+ The pool can still be used, but some features are unavailable.
+action: Enable all features using 'zpool upgrade'. Once this is done,
+ the pool may no longer be accessible by software that does not support
+ the features. See zpool-features(7) for details.
+ scan: scrub repaired 0B in 00:11:03 with 0 errors on Sat Jul 11 03:15:41 2026
config:
NAME STATE READ WRITE CKSUM
mypool ONLINE 0 0 0
mirror-0 ONLINE 0 0 0
- ada0 ONLINE 0 0 0
- ada1 ONLINE 0 0 0
+ ada0p1 ONLINE 0 0 0
+ ada1p1 ONLINE 0 0 0
errors: No known data errors
-# zpool upgrade
-This system supports ZFS pool feature flags.
-
-The following pools are formatted with legacy version numbers and are upgraded to use feature flags.
-After being upgraded, these pools will no longer be accessible by software that does not support feature flags.
-
-VER POOL
---- ------------
-28 mypool
-
-Use 'zpool upgrade -v' for a list of available legacy versions.
-Every feature flags pool has all supported features enabled.
-# zpool upgrade mypool
-This system supports ZFS pool feature flags.
-
-Successfully upgraded 'mypool' from version 28 to feature flags.
-Enabled the following features on 'mypool':
- async_destroy
- empty_bpobj
- lz4_compress
- multi_vdev_crash_dump
....
-The newer features of ZFS will not be available until `zpool upgrade` has completed.
-Use `zpool upgrade -v` to see what new features the upgrade provides, as well as which features are already supported.
-
-Upgrade a pool to support new feature flags:
+Running `zpool upgrade` without arguments lists every pool with disabled features:
[source,shell]
....
-# zpool status
- pool: mypool
- state: ONLINE
-status: Some supported features are not enabled on the pool. The pool can
- still be used, but some features are unavailable.
-action: Enable all features using 'zpool upgrade'. Once this is done,
- the pool may no longer be accessible by software that does not support
- the features. See zpool-features(7) for details.
- scan: none requested
-config:
-
- NAME STATE READ WRITE CKSUM
- mypool ONLINE 0 0 0
- mirror-0 ONLINE 0 0 0
- ada0 ONLINE 0 0 0
- ada1 ONLINE 0 0 0
-
-errors: No known data errors
# zpool upgrade
This system supports ZFS pool feature flags.
-All pools are formatted using feature flags.
-
Some supported features are not enabled on the following pools. Once a
feature is enabled the pool may become incompatible with software
that does not support the feature. See zpool-features(7) for details.
+Note that the pool 'compatibility' feature can be used to inhibit
+feature upgrades.
+
POOL FEATURE
---------------
-zstore
- multi_vdev_crash_dump
- spacemap_histogram
- enabled_txg
- hole_birth
- extensible_dataset
- bookmarks
- filesystem_limits
+mypool
+ zilsaxattr
+ head_errlog
+ blake3
+ block_cloning
+ vdev_zaps_v2
+....
+
+Use `zpool upgrade -v` to list every feature the running system supports, together with the legacy version numbers.
+Enable all supported features on a pool:
+
+[source,shell]
+....
# zpool upgrade mypool
This system supports ZFS pool feature flags.
Enabled the following features on 'mypool':
- spacemap_histogram
- enabled_txg
- hole_birth
- extensible_dataset
- bookmarks
- filesystem_limits
+ zilsaxattr
+ head_errlog
+ blake3
+ block_cloning
+ vdev_zaps_v2
....
+Pools that must stay importable on systems running older ZFS software can restrict which features `zpool create` and `zpool upgrade` enable through the `compatibility` pool property:
+
+[source,shell]
+....
+# zpool create -o compatibility=openzfs-2.1-freebsd mypool mirror /dev/ada0p1 /dev/ada1p1
+....
+
+Setting the property on an existing pool with `zpool set` works as well.
+Each named feature set corresponds to a file in [.filename]#/usr/share/zfs/compatibility.d#, and the default value `off` allows every feature.
+Setting the property does not disable features that are already enabled.
+
[WARNING]
====
-Update the boot code on systems that boot from a pool to support the new pool version.
+Update the boot code on systems that boot from a pool to support the new pool features.
Use `gpart bootcode` on the partition that contains the boot code.
-Two types of bootcode are available, depending on way the system boots: GPT (the most common option) and EFI (for more modern systems).
+Two types of bootcode are available, depending on the way the system boots: GPT (the most common option) and EFI (for more modern systems).
For legacy boot using GPT, use the following command:
@@ -1380,10 +1867,10 @@
....
# zpool history
History for 'tank':
-2013-02-26.23:02:35 zpool create tank mirror /dev/ada0 /dev/ada1
-2013-02-27.18:50:58 zfs set atime=off tank
-2013-02-27.18:51:09 zfs set checksum=fletcher4 tank
-2013-02-27.18:51:18 zfs create tank/backup
+2026-07-11.10:24:05 zpool create tank mirror /dev/ada0p1 /dev/ada1p1
+2026-07-11.18:50:58 zfs set atime=off tank
+2026-07-11.18:51:09 zfs set checksum=fletcher4 tank
+2026-07-11.18:51:18 zfs create tank/backup
....
The output shows `zpool` and `zfs` commands altering the pool in some way along with a timestamp.
@@ -1397,13 +1884,13 @@
....
# zpool history -i
History for 'tank':
-2013-02-26.23:02:35 [internal pool create txg:5] pool spa 28; zfs spa 28; zpl 5;uts 9.1-RELEASE 901000 amd64
-2013-02-27.18:50:53 [internal property set txg:50] atime=0 dataset = 21
-2013-02-27.18:50:58 zfs set atime=off tank
-2013-02-27.18:51:04 [internal property set txg:53] checksum=7 dataset = 21
-2013-02-27.18:51:09 zfs set checksum=fletcher4 tank
-2013-02-27.18:51:13 [internal create txg:55] dataset = 39
-2013-02-27.18:51:18 zfs create tank/backup
+2026-07-11.10:24:05 [txg:5] create pool version 5000; software version zfs-2.2.7-FreeBSD_gd75f9ee8b; uts myzfsbox 14.3-RELEASE 1403000 amd64
+2026-07-11.18:50:53 [txg:50] set tank (21) atime=0
+2026-07-11.18:50:58 zfs set atime=off tank
+2026-07-11.18:51:04 [txg:53] set tank (21) checksum=7
+2026-07-11.18:51:09 zfs set checksum=fletcher4 tank
+2026-07-11.18:51:13 [txg:55] create tank/backup (39)
+2026-07-11.18:51:18 zfs create tank/backup
....
Show more details by adding `-l`.
@@ -1413,14 +1900,14 @@
....
# zpool history -l
History for 'tank':
-2013-02-26.23:02:35 zpool create tank mirror /dev/ada0 /dev/ada1 [user 0 (root) on :global]
-2013-02-27.18:50:58 zfs set atime=off tank [user 0 (root) on myzfsbox:global]
-2013-02-27.18:51:09 zfs set checksum=fletcher4 tank [user 0 (root) on myzfsbox:global]
-2013-02-27.18:51:18 zfs create tank/backup [user 0 (root) on myzfsbox:global]
+2026-07-11.10:24:05 zpool create tank mirror /dev/ada0p1 /dev/ada1p1 [user 0 (root) on myzfsbox:global]
+2026-07-11.18:50:58 zfs set atime=off tank [user 0 (root) on myzfsbox:global]
+2026-07-11.18:51:09 zfs set checksum=fletcher4 tank [user 0 (root) on myzfsbox:global]
+2026-07-11.18:51:18 zfs create tank/backup [user 0 (root) on myzfsbox:global]
....
-The output shows that the `root` user created the mirrored pool with disks [.filename]#/dev/ada0# and [.filename]#/dev/ada1#.
-The hostname `myzfsbox` is also shown in the commands after the pool's creation.
+The output shows that the `root` user created the mirrored pool with disks [.filename]#/dev/ada0p1# and [.filename]#/dev/ada1p1#.
+The hostname `myzfsbox` is also shown with each command.
The hostname display becomes important when exporting the pool from one system and importing on another.
It's possible to distinguish the commands issued on the other system by the hostname recorded for each command.
@@ -1463,11 +1950,51 @@
----------------------- ----- ----- ----- ----- ----- -----
data 288G 1.53T 2 12 9.23K 61.5K
mirror 288G 1.53T 2 12 9.23K 61.5K
- ada1 - - 0 4 5.61K 61.7K
- ada2 - - 1 4 5.04K 61.7K
+ ada1p1 - - 0 4 5.61K 61.7K
+ ada2p1 - - 1 4 5.04K 61.7K
----------------------- ----- ----- ----- ----- ----- -----
....
+`zpool iostat` offers several deeper views of the I/O pipeline.
+`-l` adds average latency columns, splitting the total wait time into disk time and time spent in the various I/O queues.
+`-q` shows the number of pending and active operations in each per-vdev queue.
+`-w` prints full latency histograms, and `-r` prints request size histograms for each leaf vdev.
+
+The latency columns of `-l` make it easy to spot a single slow disk dragging down a whole vdev:
+
+[source,shell]
+....
+# zpool iostat -lv data
+ capacity operations bandwidth total_wait disk_wait syncq_wait asyncq_wait scrub trim rebuild
+pool alloc free read write read write read write read write read write read write wait wait wait
+---------- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- -------
+data 288G 1.53T 156 212 19.4M 26.6M 14ms 186ms 5ms 184ms 1us 2us 8ms 2ms - - -
+ mirror-0 288G 1.53T 156 212 19.4M 26.6M 14ms 186ms 5ms 184ms 1us 2us 8ms 2ms - - -
+ ada1p1 - - 79 106 9.7M 13.3M 3ms 5ms 3ms 4ms 1us 2us 1ms 1ms - - -
+ ada2p1 - - 77 106 9.7M 13.3M 212ms 187ms 208ms 183ms 1us 2us 15ms 3ms - - -
+---------- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- -------
+....
+
+In this example, [.filename]#ada2p1# needs hundreds of milliseconds of disk time for I/O that its mirror partner [.filename]#ada1p1# completes in single-digit milliseconds, while both handle a similar number of operations.
+A write to the mirror only completes when the slowest member finishes, so the write latency of the whole vdev tracks [.filename]#ada2p1#.
+High latency at normal operation counts, without read, write, or checksum errors in `zpool status`, often indicates a disk that retries internally and is a candidate for replacement.
+
+Scripts that must wait for background pool activity to finish can block on `zpool wait`:
+
+[source,shell]
+....
+# zpool wait -t scrub,resilver mypool
+....
+
+The command returns once none of the named activities, such as `scrub`, `resilver`, `trim`, or `remove`, are in progress.
+See man:zpool-wait[8] for the full list of activities.
+
+[NOTE]
+====
+`zpool status`, `zpool list`, `zpool get`, and `zfs list` accept `-j` to produce JSON output, which is more robust for scripts to parse than the human-readable tables.
+JSON output requires OpenZFS 2.3 or later, first available in FreeBSD 15.0.
+====
+
[[zfs-zpool-split]]
=== Splitting a Storage Pool
@@ -1575,10 +2102,11 @@
....
In modern versions of ZFS, `zfs destroy` is asynchronous, and the free space might take minutes to appear in the pool.
-Use `zpool get freeing _poolname_` to see the `freeing` property, that shows which datasets are having their blocks freed in the background.
+Use `zpool get freeing _poolname_` to see the `freeing` property, which shows how much space remains to reclaim in the background.
+Use `zpool wait -t free _poolname_` to wait until the background freeing completes.
If there are child datasets, like crossref:zfs[zfs-term-snapshot,snapshots] or other datasets, destroying the parent is impossible.
To destroy a dataset and its children, use `-r` to recursively destroy the dataset and its children.
-Use `-n -v` to list datasets and snapshots destroyed by this operation, without actually destroy anything.
+Use `-n -v` to list datasets and snapshots destroyed by this operation, without actually destroying anything.
Space reclaimed by destroying snapshots is also shown.
[[zfs-zfs-volume]]
@@ -1608,6 +2136,33 @@
/dev/zvol/tank/fat32 on /mnt (msdosfs, local)
....
+Creating a volume reserves enough space in the pool to hold the full volume size.
+To skip that reservation, create a _sparse volume_, also known as a thin provisioned volume, with `-s`:
+
+[source,shell]
+....
+# zfs create -s -V 250g tank/bigdisk
+....
+
+Sparse volumes allow committing more space than the pool actually has.
+Writes to a sparse volume can fail with `ENOSPC` when the pool runs low on space, so use sparse volumes only where the consumer of the volume tolerates write errors.
+
+The `volmode` property controls how ZFS exposes a volume to the operating system.
+Setting it to `full` exposes the volume as a GEOM provider with maximal functionality, and `geom` is an alias for `full`.
+Setting it to `dev` exposes the volume as a plain device node, hiding any partitions it contains.
+Volumes with `volmode` set to `none` are not exposed outside ZFS at all, but still support snapshots, clones, and replication, making them useful as replication targets.
+The property defaults to `default`, which defers to the system-wide `zvol_volmode` tunable described in man:zfs[4].
+
+Volumes work well as virtual disks for man:bhyve[8] virtual machines and as backing storage for iSCSI extents: declare the volume device as a LUN in man:ctl.conf[5] and serve it with man:ctld[8].
+
+[WARNING]
+====
+Do not place swap space on ZFS volumes.
+When the system is low on memory, ZFS itself needs memory to complete the write that would release memory, and the system can deadlock.
+See the upstream OpenZFS issue link:https://github.com/openzfs/zfs/issues/7734[openzfs/zfs#7734] for details.
+Use a dedicated swap partition instead.
+====
+
Destroying a volume is much the same as destroying a regular file system dataset.
The operation is nearly instantaneous, but it may take minutes to reclaim the free space in the background.
@@ -1712,7 +2267,18 @@
Two commonly used and useful dataset properties are the NFS and SMB share options.
Setting these defines if and how ZFS shares datasets on the network.
-At present, FreeBSD supports setting NFS sharing alone.
+On FreeBSD, ZFS integrates with the NFS server in the base system: setting the `sharenfs` property exports the dataset through man:mountd[8].
+ZFS writes the export entry to [.filename]#/etc/zfs/exports#, which man:mountd[8] reads in addition to [.filename]#/etc/exports#.
+Sharing datasets requires the NFS server enabled in [.filename]#/etc/rc.conf#:
+
+[.programlisting]
+....
+zfs_enable="YES"
+nfs_server_enable="YES"
+....
+
+The value of `sharenfs` is either `on`, `off`, or a list of man:exports[5] options to apply to the export.
+
To get the current status of a share, enter:
[source,shell]
@@ -1720,9 +2286,6 @@
# zfs get sharenfs mypool/usr/home
NAME PROPERTY VALUE SOURCE
mypool/usr/home sharenfs on local
-# zfs get sharesmb mypool/usr/home
-NAME PROPERTY VALUE SOURCE
-mypool/usr/home sharesmb off local
....
To enable sharing of a dataset, enter:
@@ -1740,6 +2303,44 @@
# zfs set sharenfs="-alldirs,-maproot=root,-network=192.168.1.0/24" mypool/usr/home
....
+The `sharesmb` property is not functional on FreeBSD, as the sharing library implements NFS only.
+To serve a dataset over SMB, install a package such as package:net/samba422[] and export the path of the mounted dataset in its configuration.
+
+[[zfs-zfs-acl]]
+=== NFSv4 ACLs
+
+ZFS on FreeBSD stores NFSv4-style ACLs natively; the `acltype` dataset property defaults to `nfsv4`.
+Every file and directory can carry an access control list alongside the traditional permission bits, giving finer-grained control than the owner/group/other model.
+Display and edit these lists with man:getfacl[1] and man:setfacl[1].
+
+Display the ACL of a file:
+
+[source,shell]
+....
+% getfacl /usr/home/alice/notes.txt
+# file: /usr/home/alice/notes.txt
+# owner: alice
+# group: alice
+ owner@:rw-p--aARWcCos:-------:allow
+ group@:r-----a-R-c--s:-------:allow
+ everyone@:r-----a-R-c--s:-------:allow
+....
+
+Grant an additional user write access by adding an `allow` entry:
+
+[source,shell]
+....
+% setfacl -m u:bob:rwp::allow /usr/home/alice/notes.txt
+....
+
+Two dataset properties control how ACLs interact with traditional permission handling.
+The `aclmode` property (`discard`, `groupmask`, `passthrough`, or `restricted`) governs what happens to the ACL when man:chmod[1] changes the permission bits of a file.
+With the default, `discard`, chmod removes all ACL entries that do not represent the new file mode.
+The `aclinherit` property (`discard`, `noallow`, `restricted`, `passthrough`, or `passthrough-x`) governs which ACL entries new files and directories inherit from their parent directory.
+With the default, `restricted`, inherited entries lose the `write_acl` and `write_owner` permissions.
+
+See man:setfacl[1] and man:zfsprops[7] for the full entry syntax and property descriptions.
+
[[zfs-zfs-snapshot]]
=== Managing Snapshots
@@ -1828,8 +2429,7 @@
mypool/usr/home@my_recursive_snapshot 0 - 184K -
....
-Displaying both the dataset and the snapshot together reveals how snapshots work
-in crossref:zfs[zfs-term-cow,COW] fashion.
+Displaying both the dataset and the snapshot together reveals how snapshots work in crossref:zfs[zfs-term-cow,COW] fashion.
They save the changes (_delta_) made and not the complete file system contents all over again.
This means that snapshots take little space when making changes.
Observe space usage even more by copying a file to the dataset, then creating a second snapshot:
@@ -1889,8 +2489,7 @@
|Renaming the path or file.
|===
-Comparing the output with the table, it becomes clear that ZFS added [.filename]#passwd#
-after creating the snapshot `_mypool/var/tmp@my_recursive_snapshot_`.
+Comparing the output with the table, it becomes clear that ZFS added [.filename]#passwd# after creating the snapshot `_mypool/var/tmp@my_recursive_snapshot_`.
This also resulted in a modification to the parent directory mounted at `_/var/tmp_`.
Comparing two snapshots is helpful when using the ZFS replication feature to transfer a dataset to a different host for backup purposes.
@@ -1963,7 +2562,7 @@
[source,shell]
....
# zfs list -rt snapshot mypool/var/tmp
-AME USED AVAIL REFER MOUNTPOINT
+NAME USED AVAIL REFER MOUNTPOINT
mypool/var/tmp@my_recursive_snapshot 88K - 152K -
mypool/var/tmp@after_cp 53.5K - 118K -
mypool/var/tmp@diff_snapshot 0 - 120K -
@@ -1995,7 +2594,7 @@
[[zfs-zfs-snapshot-snapdir]]
==== Restoring Individual Files from Snapshots
-Snapshots live in a hidden directory under the parent dataset: [.filename]#.zfs/snapshots/snapshotname#.
+Snapshots live in a hidden directory under the parent dataset: [.filename]#.zfs/snapshot/snapshotname#.
By default, these directories will not show even when executing a standard `ls -a` .
Although the directory doesn't show, access it like any normal directory.
The property named `snapdir` controls whether these hidden directories show up in a directory listing.
@@ -2045,12 +2644,94 @@
Copying files into and removing them from snapshot directories are both disallowed because that would change the state of the dataset they represent.
Snapshots consume space based on how much the parent file system has changed since the time of the snapshot.
-The `written` property of a snapshot tracks the space the snapshot uses.
+The `used` property of a snapshot tracks the space the snapshot uses; its `written` property shows how much data was written between the previous snapshot and this one.
To destroy snapshots and reclaim the space, use `zfs destroy _dataset_@_snapshot_`.
Adding `-r` recursively removes all snapshots with the same name under the parent dataset.
Adding `-n -v` to the command displays a list of the snapshots to be deleted and an estimate of the space it would reclaim without performing the actual destroy operation.
+[[zfs-zfs-snapshot-hold]]
+==== Snapshot Holds
+
+A crossref:zfs[zfs-term-hold,hold] places a user-defined tag on a snapshot and prevents its destruction.
+Attempts to destroy a held snapshot with `zfs destroy` fail with `EBUSY`.
+A snapshot can carry any number of holds, each identified by a unique tag name.
+Holds protect snapshots that other processes still depend on, such as the most recent snapshot shared by both sides of a replication chain (see crossref:zfs[zfs-zfs-send,Replication]).
+Destroying that common snapshot by accident forces the next replication to start over with a full stream.
+
+Place a hold with `zfs hold _tag_ _snapshot_`.
+Adding `-r` also holds the snapshots with the same name on all child datasets.
+
+[source,shell]
+....
+# zfs hold keepme mypool/var/tmp@my_recursive_snapshot
+# zfs destroy mypool/var/tmp@my_recursive_snapshot
+cannot destroy snapshot mypool/var/tmp@my_recursive_snapshot: dataset is busy
+....
+
+`zfs holds` lists the holds on a snapshot, showing the tag name and the time of placing each hold:
+
+[source,shell]
+....
+# zfs holds mypool/var/tmp@my_recursive_snapshot
+NAME TAG TIMESTAMP
+mypool/var/tmp@my_recursive_snapshot keepme Sat Jul 11 09:41 2026
+....
+
+Remove a hold with `zfs release`.
+Destroying the snapshot becomes possible again once the last hold on it is released:
+
+[source,shell]
+....
+# zfs release keepme mypool/var/tmp@my_recursive_snapshot
+# zfs destroy mypool/var/tmp@my_recursive_snapshot
+....
+
+See man:zfs-hold[8] for more information.
+
+[[zfs-zfs-bookmark]]
+=== Bookmarks
+
+A crossref:zfs[zfs-term-bookmark,bookmark] records the point in time at which a snapshot was created, without keeping any of the snapshot data.
+Bookmarks consume almost no space, no matter how much the dataset changes afterwards.
+Like a snapshot, a bookmark can serve as the source of an incremental `zfs send`.
+Unlike a snapshot, it does not prevent ZFS from freeing the old data.
+This combination makes bookmarks useful for incremental replication: after sending a snapshot to another pool, bookmark it and destroy it on the sending side, reclaiming the space while the receiving side keeps its copy of the snapshot.
+See crossref:zfs[zfs-send-incremental,Incremental Backups] for the replication workflow itself.
+
+Create a bookmark from a snapshot with `zfs bookmark`.
+Bookmark names use `#` as the separator between the dataset name and the bookmark name, in the same way snapshot names use `@`.
+
+[source,shell]
+....
+# zfs snapshot mypool/data@snap1
+# zfs send mypool/data@snap1 | zfs receive backup/data
+# zfs bookmark mypool/data@snap1 mypool/data#snap1bm
+# zfs destroy mypool/data@snap1
+....
+
+List bookmarks with `zfs list -t bookmark`:
+
+[source,shell]
+....
+# zfs list -t bookmark mypool/data
+NAME USED AVAIL REFER MOUNTPOINT
+mypool/data#snap1bm - - 184K -
+....
+
+When the time comes for the next incremental backup, take a new snapshot and use the bookmark as the incremental source:
+
+[source,shell]
+....
+# zfs snapshot mypool/data@snap2
+# zfs send -i mypool/data#snap1bm mypool/data@snap2 | zfs receive backup/data
+....
+
+This works because the receiving pool still has the snapshot _backup/data@snap1_ matching the bookmark.
+Bookmarks serve as incremental sources only.
+Mounting a bookmark, rolling back to it, or restoring files from it is impossible, as it contains no data.
+See man:zfs-bookmark[8] for more information.
+
[[zfs-zfs-clones]]
=== Managing Clones
@@ -2136,6 +2817,47 @@
Once satisfied with the changes, promote the clones to full datasets and provide them to end users to work with like they would with a real dataset.
This saves time and administrative overhead when providing these jails.
+[[zfs-zfs-block-cloning]]
+=== Block Cloning
+
+crossref:zfs[zfs-term-block-cloning,Block cloning] brings copy-on-write to individual file copies.
+When enabled, copying a file with man:cp[1] does not duplicate the file data.
+Instead, the new file refers to the same blocks as the original, and ZFS writes new blocks only when either file changes afterwards.
+man:cp[1] performs copies with man:copy_file_range[2], which ZFS turns into block clone operations, making copies within a pool almost instantaneous and consuming no additional space at first.
+
+Unlike crossref:zfs[zfs-zfs-deduplication,deduplication], block cloning keeps no memory-hungry table of checksums.
+Sharing happens at the moment of the copy only, and writing identical data in separate operations still stores it twice.
+Unlike crossref:zfs[zfs-zfs-clones,clones], block cloning requires no snapshot, works on individual files rather than whole datasets, and leaves no `origin` relationship behind.
+
+Block cloning requires the `block_cloning` pool feature from OpenZFS 2.2, first shipped in FreeBSD 14.0 (see man:zpool-features[7]).
+The man:sysctl[8] `vfs.zfs.bclone_enabled` controls whether ZFS actually creates block clones.
+It defaults to 0 on FreeBSD 14.x and to 1 on FreeBSD 15.0.
+While the sysctl is 0, `copy_file_range()` falls back to an ordinary copy.
+To enable block cloning on FreeBSD 14.x:
+
+[source,shell]
+....
+# sysctl vfs.zfs.bclone_enabled=1
+vfs.zfs.bclone_enabled: 0 -> 1
+....
+
+To keep the setting across reboots, add this line to [.filename]#/etc/sysctl.conf#:
+
+[.programlisting]
+....
+vfs.zfs.bclone_enabled=1
+....
+
+The pool properties `bcloneused`, `bclonesaved`, and `bcloneratio` show the space used by cloned blocks, the space saved by cloning, and the resulting savings ratio:
+
+[source,shell]
+....
+# cp /var/tmp/database.dump /var/tmp/database.copy
+# zpool list -o name,bcloneused,bclonesaved,bcloneratio mypool
+NAME BCLONE_USED BCLONE_SAVED BCLONE_RATIO
+mypool 1.7G 1.7G 2.00x
+....
+
[[zfs-zfs-send]]
=== Replication
@@ -2208,9 +2930,11 @@
....
# zfs snapshot mypool@replica1
# zfs send -v mypool@replica1 | zfs receive backup/mypool
-send from @ to mypool@replica1 estimated size is 50.1M
+full send of mypool@replica1 estimated size is 50.1M
total estimated size is 50.1M
-TIME SENT SNAPSHOT
+TIME SENT SNAPSHOT mypool@replica1
+10:22:01 9.75M mypool@replica1
+10:22:02 25.4M mypool@replica1
# zpool list
NAME SIZE ALLOC FREE CKPOINT EXPANDSZ FRAG CAP DEDUP HEALTH ALTROOT
@@ -2245,10 +2969,10 @@
[source,shell]
....
-# zfs send -v -i mypool@replica1 mypool@replica2 | zfs receive /backup/mypool
+# zfs send -v -i mypool@replica1 mypool@replica2 | zfs receive backup/mypool
send from @replica1 to mypool@replica2 estimated size is 5.02M
total estimated size is 5.02M
-TIME SENT SNAPSHOT
+TIME SENT SNAPSHOT mypool@replica2
# zpool list
NAME SIZE ALLOC FREE CKPOINT EXPANDSZ FRAG CAP DEDUP HEALTH ALTROOT
@@ -2277,6 +3001,63 @@
Specifying `-p` copies the dataset properties including compression settings, quotas, and mount points.
Specifying `-R` copies all child datasets of the dataset along with their properties.
Automate sending and receiving to create regular backups on the second pool.
+The crossref:zfs[zfs-send-options,Send Stream Options] section describes these and other useful options in more detail.
+
+[[zfs-send-options]]
+==== Send Stream Options
+
+`zfs send` provides options that control what a stream includes and how compact it is.
+Specifying `-p` includes the dataset properties, such as compression settings, quotas, and mount points, in the stream.
+Specifying `-R` generates a replication stream package, which includes the dataset, all child datasets, snapshots, clones, and properties up to the named snapshot, and implies `-p`.
+The example in crossref:zfs[zfs-send-ssh,Sending Encrypted Backups over SSH] uses `-R` to replicate a complete dataset tree.
+Specifying `-v` prints information about the generated stream, including a per-second progress report.
+
+Other options change the format of the data blocks in the stream:
+
+* `-L` permits blocks larger than 128 KB in the stream, preserving the on-disk block size of datasets with a `recordsize` above 128 KB instead of splitting those blocks.
+* `-e` generates a more compact stream by using `WRITE_EMBEDDED` records for tiny blocks that the `embedded_data` pool feature stores directly in the block pointer.
+* `-c` sends blocks that are compressed on disk in their compressed form rather than decompressing them first, saving CPU time on both systems and reducing the amount of data transferred for compressed datasets.
+* `-w` sends an encrypted dataset in raw form, exactly as stored on disk and without loading its encryption key, as described in crossref:zfs[zfs-encrypted-send,Sending Encrypted Datasets]. For unencrypted datasets, `-w` is equivalent to `-Lec`.
+
+Combining these options as `zfs send -Lec` is a good default on modern pools.
+The receiving pool must support every feature the stream uses, and `zfs receive` rejects a stream that requires a feature the pool lacks.
+See man:zfs-send[8] and man:zpool-features[7] for the full list of options and the pool features they depend on.
+
+[[zfs-send-resume]]
+==== Resumable Transfers
+
+A dropped network connection or a reboot during a large transfer normally means starting over from the beginning.
+Using `zfs receive -s` keeps the partially received state on the receiving dataset instead of deleting it when the stream ends prematurely.
+
+[source,shell]
+....
+# zfs send -v mypool@replica1 | zfs receive -s backup/mypool
+full send of mypool@replica1 estimated size is 50.1M
+total estimated size is 50.1M
+TIME SENT SNAPSHOT mypool@replica1
+10:31:01 9.75M mypool@replica1
+^C
+....
+
+After the interruption, the receiving dataset stores an opaque resume token in its `receive_resume_token` property.
+The token records which snapshot the interrupted stream contained and how much of it arrived.
+Passing the token to `zfs send -t` generates a new stream that continues where the transfer stopped:
+
+[source,shell]
+....
+# zfs get -H -o value receive_resume_token backup/mypool
+1-1211c4f4a-f8-789c636064000310a501c49c50360710a715e5e7a69766a63040c1eabb735735ce8f8d5420c0e5c9e8d4d3d28a5388e4d3d200
+# zfs send -t $(zfs get -H -o value receive_resume_token backup/mypool) | zfs receive -s backup/mypool
+....
+
+The resumed transfer completes normally and removes the saved partial state.
+Until resumed or aborted, the partial state consumes space on the receiving pool and blocks other receives into the same dataset.
+To abandon an interrupted transfer instead of resuming it, use `zfs receive -A` to delete the saved partial state:
+
+[source,shell]
+....
+# zfs receive -A backup/mypool
+....
[[zfs-send-ssh]]
==== Sending Encrypted Backups over SSH
@@ -2286,7 +3067,7 @@
This is undesirable when sending the streams over the internet to a remote host.
Use SSH to securely encrypt data sent over a network connection.
Since ZFS requires redirecting the stream from standard output, piping it through SSH is easy.
-To keep the contents of the file system encrypted in transit and on the remote system, consider using https://wiki.freebsd.org/PEFS[PEFS].
+To keep the contents of the file system encrypted on the remote system as well, use crossref:zfs[zfs-native-encryption,native encryption] and send the dataset as a raw stream, as described in crossref:zfs[zfs-encrypted-send,Sending Encrypted Datasets].
Change some settings and take security precautions first.
This describes the necessary steps required for the `zfs send` operation; for more information on SSH, see crossref:security[openssh,"OpenSSH"].
@@ -2331,17 +3112,68 @@
Pipe the output through SSH to the waiting `zfs receive` on the remote host _backuphost_.
Using an IP address or fully qualified domain name is good practice.
The receiving machine writes the data to the _backup_ dataset on the _recvpool_ pool.
-Adding `-d` to `zfs recv` overwrites the name of the pool on the receiving side with the name of the snapshot.
+Adding `-d` to `zfs recv` discards the first element of the sent snapshot's path, usually the pool name, and grafts the remaining path onto the receiving dataset, creating any required intermediate file systems.
`-u` causes the file systems to not mount on the receiving side.
Using `-v` shows more details about the transfer, including the elapsed time and the amount of data transferred.
+[[zfs-recv-corrective]]
+==== Corrective Receive
+
+A scrub detects corrupted data, but on a pool without enough redundancy it cannot repair the damage and reports permanent errors instead.
+When an intact copy of the affected snapshot exists on another pool, a corrective receive with `zfs receive -c` heals the damaged blocks in place, using the send stream as a source of healthy data.
+The dataset stays where it is, without any rollback or rename.
+
+[NOTE]
+====
+Corrective receive requires OpenZFS 2.2, first shipped in FreeBSD 14.0.
+====
+
+In this example, a scrub of the single-disk pool _mypool_ found unrecoverable damage:
+
+[source,shell]
+....
+# zpool status -v mypool
+ pool: mypool
+ state: ONLINE
+status: One or more devices has experienced an error resulting in data
+ corruption. Applications may be affected.
+action: Restore the file in question if possible. Otherwise restore the
+ entire pool from backup.
+ see: https://openzfs.github.io/openzfs-docs/msg/ZFS-8000-8A
+ scan: scrub repaired 0B in 00:01:22 with 1 errors on Sat Jul 11 09:14:37 2026
+config:
+
+ NAME STATE READ WRITE CKSUM
+ mypool ONLINE 0 0 0
+ ada0 ONLINE 0 0 2
+
+errors: Permanent errors have been detected in the following files:
+
+ /mypool/reports/summary-2026.db
+....
+
+The snapshot _replica2_ still references the damaged blocks, and the _backup_ pool holds an intact copy of that snapshot.
+Send the snapshot from the backup pool and receive it correctively into the matching snapshot on the damaged pool, then scrub to confirm the repair:
+
+[source,shell]
+....
+# zfs send backup/mypool@replica2 | zfs receive -c mypool@replica2
+# zpool scrub -w mypool
+# zpool status mypool | grep errors:
+errors: No known data errors
+....
+
+The stream must contain the same snapshot that exists on the damaged dataset.
+Corrective receive heals only the data blocks present in the stream; it cannot repair metadata or data written after the snapshot was taken.
+Always run a crossref:zfs[zfs-zpool-scrub,scrub] afterwards to verify that the pool no longer contains damaged data.
+
[[zfs-zfs-quota]]
=== Dataset, User, and Group Quotas
Use crossref:zfs[zfs-term-quota,Dataset quotas] to restrict the amount of space consumed by a particular dataset.
crossref:zfs[zfs-term-refquota,Reference Quotas] work in much the same way, but count the space used by the dataset itself, excluding snapshots and child datasets.
-Similarly, use crossref:zfs[zfs-term-userquota,user] and
-crossref:zfs[zfs-term-groupquota,group] quotas to prevent users or groups from using up all the space in the pool or dataset.
+Similarly, use crossref:zfs[zfs-term-userquota,user] and crossref:zfs[zfs-term-groupquota,group] quotas to prevent users or groups from using up all the space in the pool or dataset.
+Project quotas, described below, restrict the space consumed by an arbitrary directory tree instead.
The following examples assume that the users already exist in the system.
Before adding a user to the system, make sure to create their home dataset first and set the `mountpoint` to `/home/_bob_`.
@@ -2380,14 +3212,14 @@
[source,shell]
....
-# zfs set userquota@joe=50G
+# zfs set userquota@joe=50G storage/home/joe
....
To remove any quota:
[source,shell]
....
-# zfs set userquota@joe=none
+# zfs set userquota@joe=none storage/home/joe
....
[NOTE]
@@ -2403,14 +3235,14 @@
[source,shell]
....
-# zfs set groupquota@firstgroup=50G
+# zfs set groupquota@firstgroup=50G storage/home
....
To remove the quota for the group _firstgroup_, or to make sure that one is not set, instead use:
[source,shell]
....
-# zfs set groupquota@firstgroup=none
+# zfs set groupquota@firstgroup=none storage/home
....
As with the user quota property, non-`root` users can see the quotas associated with the groups to which they belong.
@@ -2418,7 +3250,7 @@
To display the amount of space used by each user on a file system or snapshot along with any quotas, use `zfs userspace`.
For group information, use `zfs groupspace`.
-For more information about supported options or how to display specific options alone, refer to man:zfs[1].
+For more information about supported options or how to display specific options alone, refer to man:zfs-userspace[8].
Privileged users and `root` can list the quota for [.filename]#storage/home/bob# using:
@@ -2427,6 +3259,42 @@
# zfs get quota storage/home/bob
....
+[[zfs-zfs-quota-project]]
+==== Project Quotas
+
+Dataset and user quotas do not fit every layout.
+When several directory trees belonging to different projects share a single dataset, project quotas restrict the space consumed by each tree instead.
+A project is an arbitrary numeric identifier tagged onto files and directories.
+
+`zfs project` manages the project ID of files and directories.
+Setting the project inherit flag on a directory makes new files and subdirectories created inside it inherit its project ID.
+To assign project ID _42_ to an existing tree, set the flag and the ID recursively:
+
+[source,shell]
+....
+# zfs project -s -p 42 -r /storage/proj
+....
+
+Enforce a limit of 100 GB on all files belonging to project _42_ with the `projectquota` property:
+
+[source,shell]
+....
+# zfs set projectquota@42=100G storage
+....
+
+`zfs projectspace` displays the space consumed by each project on a dataset along with any quotas:
+
+[source,shell]
+....
+# zfs projectspace -o name,used,quota storage
+NAME USED QUOTA
+42 1.95G 100G
+....
+
+The `projectobjquota` property limits the number of objects a project may own in the same way.
+Display or clear the project ID of a directory with `zfs project -d` and `zfs project -C`.
+Refer to man:zfs-project[8] and man:zfsprops[7] for details.
+
[[zfs-zfs-reservation]]
=== Reservations
@@ -2448,8 +3316,7 @@
# zfs set reservation=none storage/home/bob
....
-The same principle applies to the `refreservation` property for setting a
-crossref:zfs[zfs-term-refreservation,Reference Reservation], with the general format `refreservation=_size_`.
+The same principle applies to the `refreservation` property for setting a crossref:zfs[zfs-term-refreservation,Reference Reservation], with the general format `refreservation=_size_`.
This command shows any reservations or refreservations that exist on [.filename]#storage/home/bob#:
@@ -2465,34 +3332,33 @@
ZFS provides transparent compression.
Compressing data written at the block level saves space and also increases disk throughput.
If data compresses by 25% the compressed data writes to the disk at the same rate as the uncompressed version, resulting in an effective write speed of 125%.
-Compression can also be a great alternative to
-crossref:zfs[zfs-zfs-deduplication,Deduplication] because it does not require extra memory.
+Compression can also be a great alternative to crossref:zfs[zfs-zfs-deduplication,Deduplication] because it does not require extra memory.
ZFS offers different compression algorithms, each with different trade-offs.
-The introduction of LZ4 compression in ZFS v5000 enables compressing the entire pool without the large performance trade-off of other algorithms.
+On OpenZFS 2.2 and later, newly created pools and datasets default to `compression=on`, which selects LZ4.
+LZ4 compresses the entire pool without the large performance trade-off of other algorithms.
The biggest advantage to LZ4 is the _early abort_ feature.
If LZ4 does not achieve at least 12.5% compression in the header part of the data, ZFS writes the block uncompressed to avoid wasting CPU cycles trying to compress data that is either already compressed or uncompressible.
-For details about the different compression algorithms available in ZFS, see the
-crossref:zfs[zfs-term-compression,Compression] entry in the terminology section.
+Changing the `compression` property affects new writes only; existing data keeps its old encoding until rewritten (see crossref:zfs[zfs-zfs-rewrite,Rewriting Existing Data]).
+For details about the different compression algorithms available in ZFS, see the crossref:zfs[zfs-term-compression,Compression] entry in the terminology section.
The administrator can see the effectiveness of compression using dataset properties.
[source,shell]
....
# zfs get used,compressratio,compression,logicalused mypool/compressed_dataset
-NAME PROPERTY VALUE SOURCE
-mypool/compressed_dataset used 449G -
-mypool/compressed_dataset compressratio 1.11x -
-mypool/compressed_dataset compression lz4 local
-mypool/compressed_dataset logicalused 496G -
+NAME PROPERTY VALUE SOURCE
+mypool/compressed_dataset used 449G -
+mypool/compressed_dataset compressratio 1.11x -
+mypool/compressed_dataset compression lz4 local
+mypool/compressed_dataset logicalused 496G -
....
The dataset is using 449 GB of space (the used property).
Without compression, it would have taken 496 GB of space (the `logicalused` property).
This results in a 1.11:1 compression ratio.
-Compression can have an unexpected side effect when combined with
-crossref:zfs[zfs-term-userquota,User Quotas].
+Compression can have an unexpected side effect when combined with crossref:zfs[zfs-term-userquota,User Quotas].
User quotas restrict how much actual space a user consumes on a dataset _after compression_.
If a user has a quota of 10 GB, and writes 10 GB of compressible data, they will still be able to store more data.
If they later update a file, say a database, with more or less compressible data, the amount of space available to them will change.
@@ -2505,8 +3371,8 @@
[[zfs-zfs-compression-zstd]]
=== Zstandard Compression
-OpenZFS 2.0 added a new compression algorithm.
-Zstandard (Zstd) offers higher compression ratios than the default LZ4 while offering much greater speeds than the alternative, gzip. OpenZFS 2.0 is available starting with FreeBSD 12.1-RELEASE via package:sysutils/openzfs[] and has been the default since FreeBSD 13.0-RELEASE.
+OpenZFS 2.0 added the Zstandard (Zstd) compression algorithm.
+Zstd offers higher compression ratios than the default LZ4 while offering much greater speeds than the alternative, gzip.
Zstd provides a large selection of compression levels, providing fine-grained control over performance versus compression ratio.
One of the main advantages of Zstd is that the decompression speed is independent of the compression level.
@@ -2520,13 +3386,42 @@
ZFS offers 19 levels of Zstd compression, each offering incrementally more space savings in exchange for slower compression.
The default level is `zstd-3` and offers greater compression than LZ4 without being much slower.
Levels above 10 require large amounts of memory to compress each block and systems with less than 16 GB of RAM should not use them.
-ZFS uses a selection of the Zstd_fast_ levels also, which get correspondingly faster but supports lower compression ratios.
+ZFS uses a selection of the Zstd _fast_ levels also, which get correspondingly faster but support lower compression ratios.
ZFS supports `zstd-fast-1` through `zstd-fast-10`, `zstd-fast-20` through `zstd-fast-100` in increments of 10, and `zstd-fast-500` and `zstd-fast-1000` which provide minimal compression, but offer high performance.
If ZFS is not able to get the required memory to compress a block with Zstd, it will fall back to storing the block uncompressed.
This is unlikely to happen except at the highest levels of Zstd on memory constrained systems.
ZFS counts how often this has occurred since loading the ZFS module with `kstat.zfs.misc.zstd.compress_alloc_fail`.
+[[zfs-zfs-rewrite]]
+=== Rewriting Existing Data
+
+[NOTE]
+====
+`zfs rewrite` requires OpenZFS 2.4, first shipped in FreeBSD 15.0.
+====
+
+Properties like `compression`, `checksum`, `dedup`, and `copies` affect newly written data only.
+Changing them leaves existing blocks stored as they were, and applying the new values traditionally required a send/receive cycle or copying every file back and forth.
+`zfs rewrite` instead rewrites the blocks of existing files in place, as if they were atomically read and written back, so the current property values take effect without those workarounds.
+
+The command operates on files and directories rather than dataset names.
+To apply a newly chosen compression algorithm to a whole dataset, recurse from its mount point:
+
+[source,shell]
+....
+# zfs set compression=zstd mypool/archive
+# zfs rewrite -r /mypool/archive
+....
+
+Adding `-v` prints the name of every rewritten file.
+`-x` keeps the recursion from crossing mount points into child datasets, and `-o` and `-l` restrict the rewrite to a byte range within a file.
+Property changes that would alter the logical block size, such as `recordsize`, have no effect on rewritten files.
+
+Rewritten blocks are new blocks: snapshots taken before the rewrite keep referencing the old copies, so space usage can grow until those snapshots are destroyed.
+`zfs rewrite` does not work through snapshots for the same reason.
+Refer to man:zfs-rewrite[8] for details.
+
[[zfs-zfs-deduplication]]
=== Deduplication
@@ -2535,15 +3430,20 @@
Tremendous space savings are possible if the data contains a lot of duplicated files or repeated information.
Warning: deduplication requires a large amount of memory, and enabling compression instead provides most of the space savings without the extra cost.
-To activate deduplication, set the `dedup` property on the target pool:
+`dedup` is a dataset property, not a pool property.
+To activate deduplication, set it on a dataset:
[source,shell]
....
# zfs set dedup=on pool
....
-Deduplicating only affects new data written to the pool.
-Merely activating this option will not deduplicate data already written to the pool.
+This example sets the property on the root dataset of the pool _pool_, so every dataset in the pool inherits it.
+Limiting deduplication to the datasets that actually store duplicate-heavy data keeps the deduplication table smaller.
+
+Deduplicating only affects new data written to the dataset.
+Merely activating this option will not deduplicate data already written.
+On FreeBSD 15.0, crossref:zfs[zfs-zfs-rewrite,`zfs rewrite`] can rewrite existing data so that it passes through deduplication.
A pool with a freshly activated deduplication property will look like this example:
[source,shell]
@@ -2576,6 +3476,9 @@
The `DEDUP` column shows a factor of `3.00x`.
Detecting and deduplicating copies of the data uses a third of the space.
The potential for space savings can be enormous, but comes at the cost of having enough memory to keep track of the deduplicated blocks.
+ZFS stores an entry for every deduplicated block in the deduplication table (crossref:zfs[zfs-term-deduplication,DDT]).
+A general rule of thumb is 5-6 GB of RAM per 1 TB of deduplicated data.
+When the table no longer fits in memory, every write forces reads of table entries from disk, and performance degrades drastically.
Deduplication is not always beneficial when the data in a pool is not redundant.
ZFS can show potential space savings by simulating deduplication on an existing pool:
@@ -2609,9 +3512,17 @@
Activating deduplication on this pool would not save any amount of space, and is not worth the amount of memory required to enable deduplication.
Using the formula _ratio = dedup * compress / copies_, system administrators can plan the storage allocation, deciding whether the workload will contain enough duplicate blocks to justify the memory requirements.
If the data is reasonably compressible, the space savings may be good.
+
+Fast dedup, a reworked implementation that lowers these costs, requires OpenZFS 2.3 or later, first available in FreeBSD 15.0.
+Fast dedup batches deduplication table updates in an on-disk log and flushes them to the table in the background, reducing the random I/O that made classic deduplication slow.
+The `dedup_table_quota` pool property sets a limit on the on-disk size of the table; ZFS stops adding new entries once the pool reaches the limit, and with the default value of `auto` the size of a dedicated dedup vdev serves as the quota.
+The read-only `dedup_table_size` pool property reports the current size of the table.
+`zpool ddtprune -p 30 pool` removes the oldest 30% of the table entries that provide no savings because only a single block references them; `-d _days_` instead prunes single-reference entries older than the given number of days.
+`zpool status -DD pool` displays deduplication table statistics, including a histogram of blocks by reference count.
+Storing the table on a dedicated crossref:zfs[zfs-zpool-special,dedup vdev] made of fast devices keeps lookups quick even when the table outgrows RAM.
+
Good practice is to enable compression first as compression also provides greatly increased performance.
-Enable deduplication in cases where savings are considerable and with enough
-available memory for the crossref:zfs[zfs-term-deduplication,DDT].
+Enable deduplication in cases where savings are considerable and with enough available memory for the crossref:zfs[zfs-term-deduplication,DDT].
[[zfs-zfs-jail]]
=== ZFS and Jails
@@ -2621,6 +3532,44 @@
To control the dataset from within a jail, set the `jailed` property.
ZFS forbids mounting a jailed dataset on the host because it may have mount points that would compromise the security of the host.
+A delegated dataset hands control of a whole subtree to the jail administrator, who can mount it, create child datasets, take snapshots, and change properties, all confined to that subtree.
+For this to work, the jail needs the `allow.mount` and `allow.mount.zfs` parameters enabled and the `enforce_statfs` parameter set to a value lower than 2.
+Refer to man:jail[8] for details on these parameters.
+
+This example creates a dataset, marks it as jailed, and attaches it to the running jail _myjail_:
+
+[source,shell]
+....
+# zfs create mypool/jaildata
+# zfs set jailed=on mypool/jaildata
+# zfs jail myjail mypool/jaildata
+....
+
+The dataset then appears in `zfs list` inside the jail and is manageable there.
+`zfs unjail myjail mypool/jaildata` returns control to the host.
+See man:zfs-jail[8] for more information.
+
+Starting with FreeBSD 15.0, man:jail[8] automates the attachment.
+List the datasets to delegate in the `zfs.dataset` jail parameter, and man:jail[8] attaches them when the jail starts.
+The datasets must already have `jailed=on` set, and the parameter requires `allow.mount.zfs`.
+An [.filename]#/etc/jail.conf# entry using it looks like this:
+
+[.programlisting]
+....
+myjail {
+ path = "/usr/local/jails/myjail";
+ host.hostname = "myjail.example.org";
+ exec.start = "/bin/sh /etc/rc";
+ exec.stop = "/bin/sh /etc/rc.shutdown";
+ allow.mount;
+ allow.mount.zfs;
+ enforce_statfs = 1;
+ zfs.dataset = "mypool/jaildata";
+}
+....
+
+Setting the `zfs.mount_snapshot` jail parameter to 1 additionally lets users inside the jail access the contents of ZFS snapshots under the [.filename]#.zfs# directory of the delegated file systems.
+
[[zfs-zfs-allow]]
== Delegated Administration
@@ -2637,7 +3586,7 @@
`zfs allow _someuser_ create _mydataset_` gives the specified user permission to create child datasets under the selected parent dataset.
A caveat: creating a new dataset involves mounting it.
That requires setting the FreeBSD `vfs.usermount` man:sysctl[8] to `1` to allow non-root users to mount a file system.
-Another restriction aimed at preventing abuse: non-`root` users must own the mountpoint where mounting the file system.
+Another restriction aimed at preventing abuse: non-`root` users must own the mountpoint where the file system is to be mounted.
[[zfs-zfs-allow-allow]]
=== Delegating Permission Delegation
@@ -2654,7 +3603,7 @@
With ZFS native encryption, encrypted datasets can be used on other systems that support this pool feature without relying on third-party components or operating system-native encryption.
Another benefit of ZFS native encryption is that datasets do not need to be decrypted for administrative tasks such as snapshots, replication, or scrub operations.
-ZFS data integrity checks work just as well with natively encrypted datasets, and compression also works encrypted datasets.
+ZFS data integrity checks work just as well with natively encrypted datasets, and compression also works on encrypted datasets.
Different datasets on the same pool can use separate keys independently.
Consider a central file server where different users store their data in encrypted home datasets.
@@ -2694,8 +3643,8 @@
* file holes, and
* deduplication tables
-This unencrypted metadata allows ZFS to perform routine maintenance operations, such as `zfs scrub`, without first decrypting the data.
-As a result, ZFS can detect and repair corrupted encrypted data that is encrypted data without accessing the unencrypted file contents.
+This unencrypted metadata allows ZFS to perform routine maintenance operations, such as `zpool scrub`, without first decrypting the data.
+As a result, ZFS can detect and repair corrupted encrypted data without accessing the unencrypted file contents.
To encrypt existing unencrypted data, copy it to a dataset with encryption enabled.
This allows data to be migrated to a secure location within the pool, provided the passphrase is supplied to authorize access.
@@ -2757,15 +3706,15 @@
[source,shell]
....
# zfs get encryption,keylocation,keyformat zroot/secretdata
-NAME PROPERTY VALUE SOURCE
-mypool/secretdata encryption aes-256-gcm -
-mypool/secretdata keylocation prompt local
-mypool/secretdata keyformat passphrase -
+NAME PROPERTY VALUE SOURCE
+zroot/secretdata encryption aes-256-gcm -
+zroot/secretdata keylocation prompt local
+zroot/secretdata keyformat passphrase -
....
The `encryption` property shows `aes-256-gcm`, which is currently the default encryption method used by ZFS.
As indicated by the `SOURCE` column, neither this property nor the `keyformat` property can be changed using `zfs set` after the dataset has been created.
-See the crossref:zfs-change-encryption-key[zfs-change-encryption-key, changing the encryption key] section for information on how to modify some of these properties.
+See the crossref:zfs[zfs-change-encryption-key,changing the encryption key] section for information on how to modify some of these properties.
The following table lists the encryption-related properties used by ZFS:
[.informaltable]
@@ -2814,7 +3763,7 @@
This distinction is important.
An unmounted dataset is not protected if its encryption key remains loaded in memory.
Always run `zfs unload-key` to protect the data.
-To access the dataset again, load the key and provide the passphrase as described in the crossref:zfs-load-encryption-key[zfs-load-encryption-key, loading the encryption key] section.
+To access the dataset again, load the key and provide the passphrase as described in the crossref:zfs[zfs-load-encryption-key,loading the encryption key] section.
ZFS prevents `zfs unload-key` from running while the dataset is still mounted.
[source,shell]
@@ -2887,10 +3836,10 @@
Re-enter new passphrase for 'zroot/secretdata':
....
-Running `zfs change-key` requires the current key to be loaded. If it is not,
-ZFS still prompts for the new key but then displays a warning that the current key has not been loaded.
+Running `zfs change-key` requires the current key to be loaded.
+If it is not, ZFS still prompts for the new key but then displays a warning that the current key has not been loaded.
-The zfs change-key command can also modify the `keylocation`, `keyformat`, and `pbkdf2iters` properties by specifying them with the `-o` option.
+The `zfs change-key` command can also modify the `keylocation`, `keyformat`, and `pbkdf2iters` properties by specifying them with the `-o` option.
Running `zfs change-key` on an encrypted child dataset makes it an encryption root if it is not one already.
To prevent this behavior and continue inheriting the parent's key, specify the `-i` option.
@@ -2925,7 +3874,7 @@
# zfs snapshot zroot/secretdata@snap1
....
-In addition to crossref:zfs-load-encryption-key[zfs-send-ssh, sending encrypted backups over SSH], using the `-w` (raw) option with `zfs send` transfers the encrypted data blocks to the target pool.
+In addition to crossref:zfs[zfs-send-ssh,sending encrypted backups over SSH], using the `-w` (raw) option with `zfs send` transfers the encrypted data blocks to the target pool.
A raw send provides several advantages:
* the receiving system never sees plaintext data.
@@ -2944,6 +3893,116 @@
The receiving system may then re-encrypt the data using a different key.
However, doing so prevents future incremental raw sends to that destination.
+[[zfs-zfskeys]]
+=== Loading Encryption Keys at Boot
+
+The `zfskeys` man:rc[8] script loads encryption keys at boot for encrypted datasets whose `keylocation` property points to a key file with a `file://` URL.
+The script runs after pool import and before mounting the file systems, so datasets handled this way mount automatically during the normal ZFS startup.
+The script does not handle datasets with `keylocation=prompt`; load their keys manually with `zfs load-key` after the system has booted.
+
+To switch the dataset created earlier from a passphrase to a key file, generate 32 bytes of random data as the new key, restrict access to the key file, and change the encryption key of the dataset:
+
+[source,shell]
+....
+# dd if=/dev/random of=/root/secretdata.key bs=32 count=1
+# chmod 600 /root/secretdata.key
+# zfs change-key -o keyformat=raw -o keylocation=file:///root/secretdata.key zroot/secretdata
+....
+
+Enable the script in [.filename]#/etc/rc.conf#:
+
+[source,shell]
+....
+# sysrc zfskeys_enable="YES"
+....
+
+By default, the script loads the keys of all encrypted datasets with a `file://` key location.
+To restrict key loading to specific datasets, list them in `zfskeys_datasets`, separated by spaces:
+
+[source,shell]
+....
+# sysrc zfskeys_datasets="zroot/secretdata"
+....
+
+[WARNING]
+====
+Anyone able to read the key file can decrypt the dataset.
+Keep the key file owned by `root` with mode `600`, and store it on storage that is itself protected, such as an encrypted root file system or removable media that is only connected during boot.
+====
+
+[[zfs-boot-environments]]
+== Boot Environments
+
+A boot environment is a bootable crossref:zfs[zfs-term-clone,clone] of the dataset tree that contains the operating system.
+On systems installed with the Root-on-ZFS layout, these datasets live under `zroot/ROOT`, with the running system in `zroot/ROOT/default`.
+Creating a boot environment snapshots and clones the root datasets, which completes in seconds and consumes almost no space until the environments start to diverge.
+
+Manage boot environments with man:bectl[8].
+Create a new environment before a major change, such as an operating system or large package upgrade:
+
+[source,shell]
+....
+# bectl create beforeupgrade
+# bectl list
+BE Active Mountpoint Space Created
+default NR / 2.43G 2026-07-11 11:26
+beforeupgrade - - 328K 2026-07-11 11:31
+....
+
+In the `Active` column, `N` marks the environment in use now, and `R` marks the one that becomes active on reboot.
+
+Perform the upgrade with man:freebsd-update[8] or man:pkg[8] as usual.
+The upgrade modifies the active environment, while `beforeupgrade` preserves the system as it was.
+If the upgraded system fails to boot or misbehaves, reboot and select `beforeupgrade` from the `Boot Environments` menu of the FreeBSD loader.
+Selecting an environment in the loader menu affects the current boot only.
+To make the rollback permanent, activate the environment:
+
+[source,shell]
+....
+# bectl activate beforeupgrade
+....
+
+Running `bectl activate -t _beName_` instead activates an environment for the next boot only, which is useful when testing changes on a remote system.
+
+To inspect or repair the contents of an inactive environment without booting it, mount it at a temporary location:
+
+[source,shell]
+....
+# bectl mount beforeupgrade
+/tmp/be_mount.c1Xk
+# bectl umount beforeupgrade
+....
+
+Destroy an environment that is no longer needed to reclaim the space it uses:
+
+[source,shell]
+....
+# bectl destroy beforeupgrade
+....
+
+[TIP]
+====
+Creating a boot environment before every man:freebsd-update[8] upgrade or large man:pkg[8] operation provides an instant way back to a known-good system without restoring from backups.
+====
+
+[[zfs-be-applications]]
+=== Boot Environments and Applications
+
+A boot environment contains only the datasets under `zroot/ROOT`.
+On the default Root-on-ZFS layout that includes the whole root file system with [.filename]#/usr/local# and [.filename]#/var/db#, so installed packages and their metadata roll back together with the operating system.
+Datasets outside `zroot/ROOT`, such as `zroot/home`, `zroot/var/log`, and `zroot/var/mail`, are shared: every environment sees the same files, and activating an older environment does not return them to an earlier state.
+
+Plan the dataset layout around this split before relying on boot environments.
+Data that must survive a rollback, such as the databases of an application installed from packages, belongs on its own dataset outside `zroot/ROOT`:
+
+[source,shell]
+....
+# zfs create -o mountpoint=/var/db/postgres zroot/pgdata
+....
+
+With this layout, rolling back to an older environment returns the database server binaries to their previous version while the database contents remain untouched.
+The reverse also holds: files written to shared datasets while testing a new environment persist after switching back, so boot environments do not replace crossref:zfs[zfs-zfs-snapshot,snapshots] of those datasets.
+
[[zfs-advanced]]
== Advanced Topics
@@ -2951,148 +4010,183 @@
=== Tuning
Adjust tunables to make ZFS perform best for different workloads.
-
-* [[zfs-advanced-tuning-arc_max]] `_vfs.zfs.arc.max_` starting with 13.x
- (`vfs.zfs.arc_max` for 12.x) - Upper size of the
- crossref:zfs[zfs-term-arc,ARC]. The default is all RAM but 1 GB, or 5/8 of all RAM, whichever is more. Use a lower value if the system runs any other daemons or processes that may require memory. Adjust this value at runtime with man:sysctl[8] and set it in [.filename]#/boot/loader.conf# or [.filename]#/etc/sysctl.conf#.
-* [[zfs-advanced-tuning-arc_meta_limit]] `_vfs.zfs.arc.meta_limit_` starting
- with 13.x (`vfs.zfs.arc_meta_limit` for 12.x) - Limit the amount of the
- crossref:zfs[zfs-term-arc,ARC] used to store metadata. The default is one
- fourth of `vfs.zfs.arc.max`. Increasing this value will improve performance if
- the workload involves operations on a large number of files and directories,
- or frequent metadata operations, at the cost of less file data fitting in the
- crossref:zfs[zfs-term-arc,ARC]. Adjust this value at runtime with man:sysctl[8] in [.filename]#/boot/loader.conf# or [.filename]#/etc/sysctl.conf#.
-* [[zfs-advanced-tuning-arc_min]] `_vfs.zfs.arc.min_` starting with 13.x
- (`vfs.zfs.arc_min` for 12.x) - Lower size of the
- crossref:zfs[zfs-term-arc,ARC]. The default is one half of
- `vfs.zfs.arc.meta_limit`. Adjust this value to prevent other applications from
- pressuring out the entire crossref:zfs[zfs-term-arc,ARC]. Adjust this value at runtime with man:sysctl[8] and in [.filename]#/boot/loader.conf# or [.filename]#/etc/sysctl.conf#.
-* [[zfs-advanced-tuning-vdev-cache-size]] `_vfs.zfs.vdev.cache.size_` - A preallocated amount of memory reserved as a cache for each device in the pool. The total amount of memory used will be this value multiplied by the number of devices. Set this value at boot time and in [.filename]#/boot/loader.conf#.
-* [[zfs-advanced-tuning-min-auto-ashift]] `_vfs.zfs.min_auto_ashift_` - Lower `ashift` (sector size) used automatically at pool creation time. The value is a power of two. The default value of `9` represents `2^9 = 512`, a sector size of 512 bytes. To avoid _write amplification_ and get the best performance, set this value to the largest sector size used by a device in the pool.
-+
-Common drives have 4 KB sectors.
-Using the default `ashift` of `9` with these drives results in write amplification on these devices.
-Data contained in a single 4 KB write is instead written in eight 512-byte writes.
-ZFS tries to read the native sector size from all devices when creating a pool, but drives with 4 KB sectors report that their sectors are 512 bytes for compatibility.
-Setting `vfs.zfs.min_auto_ashift` to `12` (`2^12 = 4096`) before creating a pool forces ZFS to use 4 KB blocks for best performance on these drives.
-+
-Forcing 4 KB blocks is also useful on pools with planned disk upgrades.
-Future disks use 4 KB sectors, and `ashift` values cannot change after creating a pool.
+FreeBSD exposes the ZFS kernel tunables as man:sysctl[8] variables under `vfs.zfs`; man:zfs[4] documents every tunable and its default value.
+Set values that must apply from boot in [.filename]#/boot/loader.conf#; adjust runtime-changeable values with man:sysctl[8] and make them permanent in [.filename]#/etc/sysctl.conf#.
+
+* `_vfs.zfs.arc.max_` - Upper size of the crossref:zfs[zfs-term-arc,ARC].
+The default of `0` lets ZFS size the ARC automatically based on the amount of installed memory.
+Use a lower value if the system runs any other daemons or processes that may require memory; see crossref:zfs[zfs-advanced-arc,ARC Sizing and Monitoring] for guidance.
+Adjust this value at runtime with man:sysctl[8] and set it in [.filename]#/boot/loader.conf# or [.filename]#/etc/sysctl.conf#.
+* `_vfs.zfs.arc.min_` - Lower size of the crossref:zfs[zfs-term-arc,ARC].
+The default of `0` lets ZFS choose a small minimum automatically.
+Increase this value to prevent other applications from pressuring out the entire ARC.
+Adjust this value at runtime with man:sysctl[8] and set it in [.filename]#/boot/loader.conf# or [.filename]#/etc/sysctl.conf#.
+* `_vfs.zfs.arc.meta_balance_` - Balance between caching metadata and file data in the crossref:zfs[zfs-term-arc,ARC]; values above `100` increasingly favor metadata, and the default is `500`.
+Increase this value if the workload involves operations on a large number of files and directories, or frequent metadata operations, at the cost of less file data fitting in the ARC.
+This tunable replaces `vfs.zfs.arc.meta_limit`, which OpenZFS 2.2 removed.
+Adjust this value at any time with man:sysctl[8].
+* `_vfs.zfs.vdev.min_auto_ashift_` - Lower `ashift` (sector size) used automatically at pool creation time.
+The value is a power of two.
+The default value of `9` represents `2^9 = 512`, a sector size of 512 bytes.
+To avoid _write amplification_ and get the best performance, set this value to the largest sector size used by a device in the pool.
+
-In some specific cases, the smaller 512-byte block size might be preferable.
-When used with 512-byte disks for databases or as storage for virtual machines, less data transfers during small random reads.
-This can provide better performance when using a smaller ZFS record size.
-* [[zfs-advanced-tuning-prefetch_disable]] `_vfs.zfs.prefetch.disable_` -
- Disable prefetch. A value of `0` enables and `1` disables it. The default is
- `0`, unless the system has less than 4 GB of RAM. Prefetch works by reading
- larger blocks than requested into the crossref:zfs[zfs-term-arc,ARC] in hopes to soon need the data. If the workload has a large number of random reads, disabling prefetch may actually improve performance by reducing unnecessary reads. Adjust this value at any time with man:sysctl[8].
-* [[zfs-advanced-tuning-vdev-trim_on_init]] `_vfs.zfs.vdev.trim_on_init_` - Control whether new devices added to the pool have the `TRIM` command run on them. This ensures the best performance and longevity for SSDs, but takes extra time. If the device has already been secure erased, disabling this setting will make the addition of the new device faster. Adjust this value at any time with man:sysctl[8].
-* [[zfs-advanced-tuning-vdev-max_pending]] `_vfs.zfs.vdev.max_pending_` - Limit the number of pending I/O requests per device. A higher value will keep the device command queue full and may give higher throughput. A lower value will reduce latency. Adjust this value at any time with man:sysctl[8].
-* [[zfs-advanced-tuning-top_maxinflight]] `_vfs.zfs.top_maxinflight_` - Upper
- number of outstanding I/Os per top-level crossref:zfs[zfs-term-vdev,vdev].
- Limits the depth of the command queue to prevent high latency. The limit is
- per top-level vdev, meaning the limit applies to each
- crossref:zfs[zfs-term-vdev-mirror,mirror],
- crossref:zfs[zfs-term-vdev-raidz,RAID-Z], or other vdev independently. Adjust this value at any time with man:sysctl[8].
-* [[zfs-advanced-tuning-l2arc_write_max]] `_vfs.zfs.l2arc_write_max_` - Limit
- the amount of data written to the crossref:zfs[zfs-term-l2arc,L2ARC] per second. This tunable extends the longevity of SSDs by limiting the amount of data written to the device. Adjust this value at any time with man:sysctl[8].
-* [[zfs-advanced-tuning-l2arc_write_boost]] `_vfs.zfs.l2arc_write_boost_` - Adds
- the value of this tunable to
- crossref:zfs[zfs-advanced-tuning-l2arc_write_max,`vfs.zfs.l2arc_write_max`]
- and increases the write speed to the SSD until evicting the first block from
- the crossref:zfs[zfs-term-l2arc,L2ARC]. This "Turbo Warmup Phase" reduces the
- performance loss from an empty crossref:zfs[zfs-term-l2arc,L2ARC] after a reboot. Adjust this value at any time with man:sysctl[8].
-* [[zfs-advanced-tuning-scrub_delay]]`_vfs.zfs.scrub_delay_` - Number of ticks
- to delay between each I/O during a crossref:zfs[zfs-term-scrub,`scrub`]. To
- ensure that a `scrub` does not interfere with the normal operation of the
- pool, if any other I/O is happening the `scrub` will delay between each
- command. This value controls the limit on the total IOPS (I/Os Per Second)
- generated by the `scrub`. The granularity of the setting is determined
- by the value of `kern.hz` which defaults to 1000 ticks per second.
- Changing this setting results in a different effective IOPS limit. The
- default value is `4`, resulting in a limit of: 1000 ticks/sec / 4 = 250
- IOPS. Using a value of _20_ would give a limit of: 1000 ticks/sec / 20 =
- 50 IOPS. Recent activity on the pool limits the speed of `scrub`, as
- determined by
- crossref:zfs[zfs-advanced-tuning-scan_idle,`vfs.zfs.scan_idle`]. Adjust this value at any time with man:sysctl[8].
-* [[zfs-advanced-tuning-resilver_delay]] `_vfs.zfs.resilver_delay_` - Number of
- milliseconds of delay inserted between each I/O during a
- crossref:zfs[zfs-term-resilver,resilver]. To ensure that a resilver does not
- interfere with the normal operation of the pool, if any other I/O is happening
- the resilver will delay between each command. This value controls the limit of
- total IOPS (I/Os Per Second) generated by the resilver. ZFS determins the
- granularity of the setting by the value of `kern.hz` which defaults to 1000
- ticks per second. Changing this setting results in a different effective IOPS
- limit. The default value is 2, resulting in a limit of: 1000 ticks/sec / 2 =
- 500 IOPS. Returning the pool to an crossref:zfs[zfs-term-online,Online] state
- may be more important if another device failing could
- crossref:zfs[zfs-term-faulted,Fault] the pool, causing data loss. A value of 0
- will give the resilver operation the same priority as other operations,
- speeding the healing process. Other recent activity on the pool limits the
- speed of resilver, as determined by
- crossref:zfs[zfs-advanced-tuning-scan_idle,`vfs.zfs.scan_idle`]. Adjust this value at any time with man:sysctl[8].
-* [[zfs-advanced-tuning-scan_idle]] `_vfs.zfs.scan_idle_` - Number of
- milliseconds since the last operation before considering the pool is idle. ZFS
- disables the rate limiting for crossref:zfs[zfs-term-scrub,`scrub`] and
- crossref:zfs[zfs-term-resilver,resilver] when the pool is idle. Adjust this value at any time with man:sysctl[8].
-* [[zfs-advanced-tuning-txg-timeout]] `_vfs.zfs.txg.timeout_` - Upper number of
- seconds between crossref:zfs[zfs-term-txg,transaction group]s. The current transaction group writes to the pool and a fresh transaction group starts if this amount of time elapsed since the previous transaction group. A transaction group may trigger earlier if writing enough data. The default value is 5 seconds. A larger value may improve read performance by delaying asynchronous writes, but this may cause uneven performance when writing the transaction group. Adjust this value at any time with man:sysctl[8].
-
-[[zfs-advanced-i386]]
-=== ZFS on i386
-
-Some of the features provided by ZFS are memory intensive, and may require tuning for upper efficiency on systems with limited RAM.
-
-==== Memory
-
-As a lower value, the total system memory should be at least one gigabyte.
-The amount of recommended RAM depends upon the size of the pool and which features ZFS uses.
-A general rule of thumb is 1 GB of RAM for every 1 TB of storage.
-If using the deduplication feature, a general rule of thumb is 5 GB of RAM per TB of storage to deduplicate.
-While some users use ZFS with less RAM, systems under heavy load may panic due to memory exhaustion.
-ZFS may require further tuning for systems with less than the recommended RAM requirements.
-
-==== Kernel Configuration
-
-Due to the address space limitations of the i386(TM) platform, ZFS users on the i386(TM) architecture must add this option to a custom kernel configuration file, rebuild the kernel, and reboot:
+See crossref:zfs[zfs-zpool-create,Creating and Destroying Storage Pools] for why pools on 4 KB-sector drives should use an `ashift` of 12.
+* `_vfs.zfs.prefetch.disable_` - Disable prefetch.
+A value of `0` enables and `1` disables it.
+The default is `0`.
+Prefetch works by reading larger blocks than requested into the crossref:zfs[zfs-term-arc,ARC] in hopes to soon need the data.
+If the workload has a large number of random reads, disabling prefetch may actually improve performance by reducing unnecessary reads.
+Adjust this value at any time with man:sysctl[8].
+* `_vfs.zfs.txg.timeout_` - Upper number of seconds between crossref:zfs[zfs-term-txg,transaction group]s.
+The current transaction group writes to the pool and a fresh transaction group starts if this amount of time elapses since the previous transaction group.
+A transaction group may trigger earlier if writing enough data.
+The default value is 5 seconds.
+A larger value may improve read performance by delaying asynchronous writes, but this may cause uneven performance when writing the transaction group.
+Adjust this value at any time with man:sysctl[8].
+* `_vfs.zfs.l2arc.write_max_` - Limit the amount of data written to the crossref:zfs[zfs-term-l2arc,L2ARC] per second.
+This tunable extends the longevity of SSDs by limiting the amount of data written to the device.
+Adjust this value at any time with man:sysctl[8].
+* `_vfs.zfs.l2arc.write_boost_` - Adds the value of this tunable to `vfs.zfs.l2arc.write_max` and increases the write speed to the SSD until evicting the first block from the crossref:zfs[zfs-term-l2arc,L2ARC].
+This "Turbo Warmup Phase" reduces the performance loss from an empty L2ARC after a reboot.
+Adjust this value at any time with man:sysctl[8].
+* `_vfs.zfs.l2arc.rebuild_enabled_` - Make the contents of the crossref:zfs[zfs-term-l2arc,L2ARC] persist across reboots.
+With the default of `1`, ZFS rebuilds the L2ARC from the log blocks stored on the cache device when importing a pool, avoiding the long warm-up period an empty cache would otherwise need.
+Set this value to `0` to always start with an empty L2ARC.
+* `_vfs.zfs.vdev.max_active_` - Upper number of I/O requests active on each device in the pool.
+A higher value keeps the device command queues full and may give higher throughput.
+A lower value reduces latency.
+Adjust this value at any time with man:sysctl[8].
+* `_vfs.zfs.vdev.*_min_active_` and `_vfs.zfs.vdev.*_max_active_` - Lower and upper number of concurrent I/O requests the scheduler issues to each device for every I/O class: `sync_read`, `sync_write`, `async_read`, `async_write`, `scrub`, and others.
+These per-class limits replace the delay-based crossref:zfs[zfs-term-scrub,scrub] and crossref:zfs[zfs-term-resilver,resilver] throttling found in older versions of ZFS.
+For example, increasing `vfs.zfs.vdev.scrub_max_active` lets a scrub use a larger share of each device's queue, finishing sooner at the cost of foreground I/O latency.
+* `_vfs.zfs.resilver_min_time_ms_` - Lower amount of time, in milliseconds, that a crossref:zfs[zfs-term-resilver,resilver] spends working between transaction group flushes; the default is `3000` (3 seconds).
+Increase this value to complete a resilver sooner when a degraded pool is at risk of losing another device, at the cost of slowing down normal pool operation.
+Adjust this value at any time with man:sysctl[8].
+
+[[zfs-advanced-arc]]
+=== ARC Sizing and Monitoring
+
+ZFS keeps recently and frequently used data and metadata in RAM in the crossref:zfs[zfs-term-arc,ARC].
+The ARC grows on demand up to `vfs.zfs.arc.max` and shrinks again when other programs need memory, so on a dedicated storage server it is normal for most of the RAM to be in use.
+The ARC stores blocks in their compressed on-disk form and decompresses them on access.
+This _compressed ARC_ lets the same amount of RAM cache more data, increasing the effective hit rate.
+
+man:top[1] shows the current ARC size in its memory summary line.
+The `kstat.zfs.misc.arcstats` man:sysctl[8] tree exposes detailed statistics, including the current size, the target maximum, and hit and miss counters:
+
+[source,shell]
+....
+% sysctl kstat.zfs.misc.arcstats.size kstat.zfs.misc.arcstats.c_max kstat.zfs.misc.arcstats.hits kstat.zfs.misc.arcstats.misses
+kstat.zfs.misc.arcstats.size: 8963041280
+kstat.zfs.misc.arcstats.c_max: 15843122176
+kstat.zfs.misc.arcstats.hits: 954912441
+kstat.zfs.misc.arcstats.misses: 21356847
+....
+
+package:filesystems/zfs-stats[] summarizes these statistics in a human-readable report.
+
+The ARC yields memory under pressure, but on systems that share RAM between ZFS and other large consumers such as databases, virtual machines, or jails, the competition can cause uneven performance on both sides.
+Capping the ARC leaves a predictable amount of memory for the other workloads.
+For example, to limit the ARC to 8 GB, add this line to [.filename]#/boot/loader.conf#:
[.programlisting]
....
-options KVA_PAGES=512
+vfs.zfs.arc.max="8G"
....
-This expands the kernel address space, allowing the `vm.kvm_size` tunable to push beyond the imposed limit of 1 GB, or the limit of 2 GB for PAE.
-To find the most suitable value for this option, divide the desired address space in megabytes by four.
-In this example `512` for 2 GB.
+Changing `vfs.zfs.arc.max` with man:sysctl[8] takes effect immediately, although shrinking a warm ARC frees memory gradually.
-==== Loader Tunables
+[[zfs-advanced-zil-slog]]
+=== Synchronous Writes, the ZIL, and SLOG
-Increases the [.filename]#kmem# address space on all FreeBSD architectures.
-A test system with 1 GB of physical memory benefitted from adding these options to [.filename]#/boot/loader.conf# and then restarting:
+Every pool has a crossref:zfs[zfs-term-zil,ZIL].
+Applications that request synchronous semantics with man:fsync[2] or the `O_SYNC` flag do not get an acknowledgment until ZFS commits the corresponding intent log record to stable storage.
+By default the ZIL lives on the pool's regular vdevs, so every synchronous write competes with normal pool I/O.
+Asynchronous writes bypass the ZIL and are written with the next crossref:zfs[zfs-term-txg,transaction group].
-[.programlisting]
+A dedicated crossref:zfs[zfs-term-vdev-log,log] vdev, often called a _SLOG_ (separate log), moves the ZIL to a fast device:
+
+[source,shell]
....
-vm.kmem_size="330M"
-vm.kmem_size_max="330M"
-vfs.zfs.arc.max="40M"
-vfs.zfs.vdev.cache.size="5M"
+# zpool add mypool log mirror nda0 nda1
....
-For a more detailed list of recommendations for ZFS-related tuning, see https://wiki.freebsd.org/ZFSTuningGuide[].
+A SLOG helps only workloads that generate a lot of synchronous writes, such as NFS servers and databases.
+It makes no difference for purely asynchronous workloads.
+
+A few gigabytes of SLOG capacity is plenty: the ZIL holds at most a few seconds of incoming writes, roughly one transaction group's worth, before ZFS writes the data to its final location in the pool.
+Choose SSDs with power-loss protection and low sustained write latency, and mirror the log devices.
+ZFS reads the ZIL only after a crash, so losing an unmirrored SLOG together with a system crash loses the writes that were acknowledged but not yet committed to the pool.
+
+The `sync` dataset property controls synchronous write behavior:
+
+* `sync=standard` (the default) commits synchronous writes to the ZIL and asynchronous writes with the next transaction group.
+* `sync=always` treats every write as synchronous, trading performance for safety that helps only applications which fail to request synchronous writes when they should.
+* `sync=disabled` treats every write as asynchronous.
+
+[WARNING]
+====
+With `sync=disabled`, ZFS acknowledges synchronous writes before they reach stable storage.
+The pool itself stays consistent, but after a crash or power failure, applications and NFS clients silently lose up to a few seconds of data they believed was safely written.
+Do not use `sync=disabled` for databases, NFS exports, or any other data that matters.
+====
+
+The `logbias` property tunes how ZFS uses the log: `logbias=latency` (the default) favors the SLOG to minimize commit latency, while `logbias=throughput` writes synchronous data directly to the main pool, reserving SLOG capacity for latency-sensitive datasets.
+See man:zfsprops[7] for details.
+
+[[zfs-advanced-directio]]
+=== Direct I/O
+
+[NOTE]
+====
+Direct I/O requires OpenZFS 2.3 or later, first available in FreeBSD 15.0.
+====
+
+The `direct` dataset property controls how ZFS handles requests made with the `O_DIRECT` flag:
+
+* `direct=standard` (the default) honors `O_DIRECT`: properly aligned reads and writes bypass the crossref:zfs[zfs-term-arc,ARC].
+* `direct=always` treats every properly aligned read or write as a direct request.
+* `direct=disabled` silently ignores `O_DIRECT` and handles every request through the ARC.
+
+Direct I/O avoids the memory copies and caching overhead of the ARC.
+On pools of very fast NVMe devices this overhead can dominate the cost of the I/O itself, and applications that maintain their own caches, such as databases, waste RAM by caching the same data twice.
+For such workloads, direct I/O can increase throughput and reduce memory use.
+
+ZFS still verifies checksums for direct reads and writes, so data integrity protection is unchanged.
+Direct writes must be aligned to the dataset `recordsize`; ZFS silently redirects the unaligned portion of a request through the ARC.
+See man:zfsprops[7] for the full alignment rules and restrictions.
+
+[[zfs-advanced-recordsize]]
+=== `recordsize` and `volblocksize`
+
+The `recordsize` property sets the largest block ZFS uses to store a file in a dataset, 128 KB by default.
+Matching the block size to the workload's I/O pattern avoids reading and writing more data than the application asked for.
+
+For databases that perform small random reads and writes, set `recordsize` to the database page size, typically 8 KB or 16 KB, before creating the database files.
+For datasets storing large media files or backup streams that are read and written sequentially, `recordsize=1M` reduces metadata overhead and improves compression.
+Volumes use the `volblocksize` property instead, which defaults to 16 KB and cannot change after creating the volume.
+
+Changing `recordsize` affects newly written files only; existing files keep their original block size.
+Not even crossref:zfs[zfs-zfs-rewrite,`zfs rewrite`] applies a new `recordsize` to existing files, as property changes that affect the logical block size have no effect on rewritten blocks; copy the files anew to store them with the new value.
[[zfs-links]]
== Further Resources
+* https://openzfs.github.io/openzfs-docs/[OpenZFS Documentation], including the Performance and Tuning guides
* https://openzfs.org/[OpenZFS]
-* https://wiki.freebsd.org/ZFSTuningGuide[FreeBSD Wiki - ZFS Tuning]
-* https://calomel.org/zfs_raid_speed_capacity.html[Calomel Blog - ZFS Raidz Performance, Capacity and Integrity]
+* man:zfs[4] describes every kernel tunable, man:zpoolconcepts[7] describes vdev types and pool layout, and man:zfsprops[7] describes every dataset property
+* Snapshot and replication automation tools from the Ports Collection:
+** package:filesystems/zrepl[] - a replication daemon supporting push and pull setups, automatic snapshot management, and resumable transfers
+** package:sysutils/sanoid[] - policy-driven snapshot management, with the bundled `syncoid` handling replication over SSH
+** package:filesystems/zfstools[] - automatic snapshot rotation in the style of the OpenSolaris auto-snapshot service, run from man:cron[8]
+** package:filesystems/zap[] - simple snapshot maintenance and remote replication, configured through ZFS properties instead of a configuration file
[[zfs-term]]
== ZFS Features and Terminology
-More than a file system, ZFS is fundamentally different.
-ZFS combines the roles of file system and volume manager, enabling new storage devices to add to a live system and having the new space available on the existing file systems in that pool at once.
-By combining the traditionally separate roles, ZFS is able to overcome previous limitations that prevented RAID groups being able to grow.
+ZFS is fundamentally different from traditional file systems.
+ZFS combines the roles of file system and volume manager, enabling the addition of new storage devices to a live system and making the new space available on the existing file systems in that pool at once.
+By combining the traditionally separate roles, ZFS overcomes previous limitations that prevented RAID groups from growing.
A _vdev_ is a top level device in a pool and can be a simple disk or a RAID transformation such as a mirror or RAID-Z array.
ZFS file systems (called _datasets_) each have access to the combined free space of the entire pool.
Used blocks from the pool decrease the space available to each file system.
@@ -3104,12 +4198,12 @@
|[[zfs-term-pool]]pool
|A storage _pool_ is the most basic building block of ZFS. A pool consists of one or more vdevs, the underlying devices that store the data. A pool is then used to create one or more file systems (datasets) or block devices (volumes).
-These datasets and volumes share the pool of remaining free space. Each pool is uniquely identified by a name and a GUID. The ZFS version number on the pool determines the features available.
+These datasets and volumes share the pool of remaining free space. Each pool is uniquely identified by a name and a GUID. Pools created by modern OpenZFS are at pool version 5000; from that version on, individual capabilities are managed by _feature flags_ rather than the version number. See man:zpool-features[7] and crossref:zfs[zfs-zpool-upgrade,Upgrading a Storage Pool] for details.
|[[zfs-term-vdev]]vdev Types
-a|A pool consists of one or more vdevs, which themselves are a single disk or a group of disks, transformed to a RAID. When using a lot of vdevs, ZFS spreads data across the vdevs to increase performance and maximize usable space. All vdevs must be at least 128 MB in size.
+a|A pool consists of one or more vdevs, which themselves are a single disk or a group of disks, transformed to a RAID. When using a lot of vdevs, ZFS spreads data across the vdevs to increase performance and maximize usable space. All vdevs must be at least 64 MB in size.
-* [[zfs-term-vdev-disk]] _Disk_ - The most basic vdev type is a standard block device. This can be an entire disk (such as [.filename]#/dev/ada0# or [.filename]#/dev/da0#) or a partition ([.filename]#/dev/ada0p3#). On FreeBSD, there is no performance penalty for using a partition rather than the entire disk. This differs from recommendations made by the Solaris documentation.
+* [[zfs-term-vdev-disk]] _Disk_ - The most basic vdev type is a standard block device. This can be an entire disk (such as [.filename]#/dev/ada0#) or a partition ([.filename]#/dev/ada0p3#). On FreeBSD, there is no performance penalty for using a partition rather than the entire disk. This differs from recommendations made by the Solaris documentation.
+
[CAUTION]
====
@@ -3123,22 +4217,23 @@
+
[NOTE]
====
-To upgrade a regular single disk vdev to a mirror vdev at any time, use `zpool
-crossref:zfs[zfs-zpool-attach,attach]`.
+To upgrade a regular single disk vdev to a mirror vdev at any time, use `zpool crossref:zfs[zfs-zpool-attach,attach]`.
====
-* [[zfs-term-vdev-raidz]] _RAID-Z_ - ZFS uses RAID-Z, a variation on standard RAID-5 that offers better distribution of parity and eliminates the "RAID-5 write hole" in which the data and parity information become inconsistent after an unexpected restart. ZFS supports three levels of RAID-Z which provide varying levels of redundancy in exchange for decreasing levels of usable storage. ZFS uses RAID-Z1 through RAID-Z3 based on the number of parity devices in the array and the number of disks which can fail before the pool stops being operational.
+* [[zfs-term-vdev-raidz]] _RAID-Z_ - ZFS uses RAID-Z, a variation on standard RAID-5 that offers better distribution of parity and eliminates the "RAID-5 write hole" in which the data and parity information become inconsistent after an unexpected restart. ZFS supports three levels of RAID-Z which provide varying levels of redundancy in exchange for decreasing levels of usable storage. ZFS uses RAID-Z1 through RAID-Z3 based on the number of parity devices in the array and the number of disks which can fail before the pool stops being operational.
+
In a RAID-Z1 configuration with four disks, each 1 TB, usable storage is 3 TB and the pool will still be able to operate in degraded mode with one faulted disk. If another disk goes offline before replacing and resilvering the faulted disk would result in losing all pool data.
+
-In a RAID-Z3 configuration with eight disks of 1 TB, the volume will provide 5 TB of usable space and still be able to operate with three faulted disks. Sun(TM) recommends no more than nine disks in a single vdev. If more disks make up the configuration, the recommendation is to divide them into separate vdevs and stripe the pool data across them.
+In a RAID-Z3 configuration with eight disks of 1 TB, the volume will provide 5 TB of usable space and still be able to operate with three faulted disks. The traditional recommendation is no more than nine disks in a single vdev. If more disks make up the configuration, the recommendation is to divide them into separate vdevs and stripe the pool data across them.
+
A configuration of two RAID-Z2 vdevs consisting of 8 disks each would create something like a RAID-60 array. A RAID-Z group's storage capacity is about the size of the smallest disk multiplied by the number of non-parity disks. Four 1 TB disks in RAID-Z1 has an effective size of about 3 TB, and an array of eight 1 TB disks in RAID-Z3 will yield 5 TB of usable space.
-* [[zfs-term-vdev-spare]] _Spare_ - ZFS has a special pseudo-vdev type for keeping track of available hot spares. Note that installed hot spares are not deployed automatically; manually configure them to replace the failed device using `zfs replace`.
-* [[zfs-term-vdev-log]] _Log_ - ZFS Log Devices, also known as ZFS Intent Log
- (crossref:zfs[zfs-term-zil,ZIL]) move the intent log from the regular pool devices to a dedicated device, typically an SSD. Having a dedicated log device improves the performance of applications with a high volume of synchronous writes like databases. Mirroring of log devices is possible, but RAID-Z is not supported. If using a lot of log devices, writes will be load-balanced across them.
-* [[zfs-term-vdev-cache]] _Cache_ - Adding a cache vdev to a pool will add the
- storage of the cache to the crossref:zfs[zfs-term-l2arc,L2ARC]. Mirroring cache devices is impossible. Since a cache device stores only new copies of existing data, there is no risk of data loss.
++
+Growing a raidz vdev one disk at a time is possible with crossref:zfs[zfs-zpool-raidz-expansion,RAID-Z expansion], which requires OpenZFS 2.3 or later, first available in FreeBSD 15.0.
+* [[zfs-term-vdev-draid]] _dRAID_ - A variant of RAID-Z that distributes data, parity, and spare capacity across all member disks using fixed-width stripes. Instead of dedicated hot spare disks, dRAID provides _distributed spares_: spare capacity spread over every member disk, allowing a failed disk to rebuild sequentially onto all remaining disks at once and restoring redundancy much faster than a traditional resilver onto a single spare. Specify a dRAID vdev as `draid[parity][:datad][:childrenc][:sparess]`, as described in man:zpoolconcepts[7]. See crossref:zfs[zfs-zpool-draid,dRAID Pools] for details.
+* [[zfs-term-vdev-spare]] _Spare_ - ZFS has a special pseudo-vdev type for keeping track of available hot spares. When man:zfsd[8] is enabled, ZFS deploys installed hot spares automatically to replace failed devices; otherwise, configure them manually to replace the failed device using `zpool replace`. See crossref:zfs[zfs-zpool-spares,Hot Spares and Automatic Replacement with zfsd] for details.
+* [[zfs-term-vdev-log]] _Log_ - Every pool has a ZFS Intent Log (crossref:zfs[zfs-term-zil,ZIL]), stored on the regular pool devices by default. A dedicated log vdev, often called a SLOG, moves the intent log to a separate device, typically an SSD. Having a dedicated log device improves the performance of applications with a high volume of synchronous writes like databases. Mirroring of log devices is possible, but RAID-Z is not supported. If using a lot of log devices, writes will be load-balanced across them. See crossref:zfs[zfs-advanced-zil-slog,"Synchronous Writes, the ZIL, and SLOG"] for details.
+* [[zfs-term-vdev-cache]] _Cache_ - Adding a cache vdev to a pool will add the storage of the cache to the crossref:zfs[zfs-term-l2arc,L2ARC]. Mirroring cache devices is impossible. Since a cache device stores only new copies of existing data, there is no risk of data loss.
+* [[zfs-term-vdev-special]] _Special/Dedup_ - Allocation class vdevs that store pool metadata, optionally small file blocks (controlled by the `special_small_blocks` dataset property), and deduplication tables on fast devices such as SSDs, speeding up a pool of slower disks. These vdevs are pool-critical: losing a special or dedup vdev loses the pool, so give them the same level of redundancy as the data vdevs. See crossref:zfs[zfs-zpool-special,Special Allocation Classes] for details.
|[[zfs-term-txg]] Transaction Group (TXG)
|Transaction Groups are the way ZFS groups blocks changes together and writes them to the pool. Transaction groups are the atomic unit that ZFS uses to ensure consistency. ZFS assigns each transaction group a unique 64-bit consecutive identifier. There can be up to three active transaction groups at a time, one in each of these three states:
@@ -3147,7 +4242,7 @@
writes. There is always a transaction group in the open state, but the
transaction group may refuse new writes if it has reached a limit. Once the
open transaction group has reached a limit, or reaching the
- crossref:zfs[zfs-advanced-tuning-txg-timeout,`vfs.zfs.txg.timeout`], the transaction group advances to the next state.
+ crossref:zfs[zfs-advanced-tuning,`vfs.zfs.txg.timeout`], the transaction group advances to the next state.
* _Quiescing_ - A short state that allows any pending operations to finish without blocking the creation of a new open transaction group. Once all the transactions in the group have completed, the transaction group advances to the final state.
* _Syncing_ - Write all the data in the transaction group to stable storage.
This process will in turn change other data, such as metadata and space maps,
@@ -3175,17 +4270,15 @@
read from the regular disks. L2ARC can also speed up
crossref:zfs[zfs-term-deduplication,deduplication] because a deduplication table
(DDT) that does not fit in RAM but does fit in the L2ARC will be much faster
-than a DDT that must read from disk. Limits on the data rate added to the cache
-devices prevents prematurely wearing out SSDs with extra writes. Until the cache
-is full (the first block evicted to make room), writes to the L2ARC limit to the
-sum of the write limit and the boost limit, and afterwards limit to the write
-limit. A pair of man:sysctl[8] values control these rate limits.
-crossref:zfs[zfs-advanced-tuning-l2arc_write_max,`vfs.zfs.l2arc_write_max`]
-controls the number of bytes written to the cache per second, while
-crossref:zfs[zfs-advanced-tuning-l2arc_write_boost,`vfs.zfs.l2arc_write_boost`] adds to this limit during the "Turbo Warmup Phase" (Write Boost).
+than a DDT that must read from disk.
+Since OpenZFS 2.0, the L2ARC is persistent: after a reboot, ZFS rebuilds the cache contents from the headers stored on the cache device instead of starting with an empty cache.
+Limits on the data rate added to the cache devices prevent prematurely wearing out SSDs with extra writes.
+Until the cache is full (the first block evicted to make room), writes to the L2ARC limit to the sum of the write limit and the boost limit, and afterwards limit to the write limit.
+A pair of man:sysctl[8] values control these rate limits: `vfs.zfs.l2arc.write_max` controls the number of bytes written to the cache per second, while `vfs.zfs.l2arc.write_boost` adds to this limit until the first eviction from the cache.
+See crossref:zfs[zfs-advanced-tuning,Tuning] for details.
|[[zfs-term-zil]]ZIL
-|ZIL accelerates synchronous transactions by using storage devices like SSDs that are faster than those used in the main storage pool. When an application requests a synchronous write (a guarantee that the data is stored to disk rather than merely cached for later writes), writing the data to the faster ZIL storage then later flushing it out to the regular disks greatly reduces latency and improves performance. Synchronous workloads like databases will profit from a ZIL alone. Regular asynchronous writes such as copying files will not use the ZIL at all.
+|The ZFS Intent Log (ZIL) is the on-disk record that ZFS uses to replay synchronous writes after a crash. Every pool has a ZIL; by default it occupies a small amount of space on the regular pool devices. When an application requests a synchronous write (a guarantee that the data is stored to disk rather than merely cached for later writes), ZFS commits the data to the ZIL first and flushes it out to the regular storage with the next transaction group. Moving the ZIL to a dedicated crossref:zfs[zfs-term-vdev-log,log vdev] (SLOG) on a device faster than the main storage, such as an SSD, greatly reduces latency and improves performance. Synchronous workloads like databases will profit from a fast ZIL alone. Regular asynchronous writes such as copying files will not use the ZIL at all. See crossref:zfs[zfs-advanced-zil-slog,"Synchronous Writes, the ZIL, and SLOG"] for details.
|[[zfs-term-cow]]Copy-On-Write
|Unlike a traditional file system, ZFS writes a different block rather than overwriting the old data in place. When completing this write the metadata updates to point to the new location. When a shorn write (a system crash or power loss in the middle of writing a file) occurs, the entire original contents of the file are still available and ZFS discards the incomplete write. This also means that ZFS does not require a man:fsck[8] after an unexpected shutdown.
@@ -3216,42 +4309,64 @@
version and the live file system contains any changes made since taking the
snapshot using no other space. New data written to the live file system uses new
blocks to store this data. The snapshot will grow as the blocks are no longer
-used in the live file system, but in the snapshot alone. Mount these snapshots
-read-only allows recovering of previous file versions. A
-crossref:zfs[zfs-zfs-snapshot,rollback] of a live file system to a specific
+used in the live file system, but in the snapshot alone. Mounting these snapshots
+read-only allows recovering previous file versions. A
+crossref:zfs[zfs-zfs-snapshot-rollback,rollback] of a live file system to a specific
snapshot is possible, undoing any changes that took place after taking the
snapshot. Each block in the pool has a reference counter which keeps track of
the snapshots, clones, datasets, or volumes use that block. As files and
-snapshots get deleted, the reference count decreases, reclaiming the free space
+snapshots get deleted, the reference count decreases, reclaiming the free space
when no longer referencing a block. Marking snapshots with a
-crossref:zfs[zfs-zfs-snapshot,hold] results in any attempt to destroy it will
+crossref:zfs[zfs-zfs-snapshot-hold,hold] means any attempt to destroy it
returns an `EBUSY` error. Each snapshot can have holds with a unique name each.
-The crossref:zfs[zfs-zfs-snapshot,release] command removes the hold so the snapshot can deleted. Snapshots, cloning, and rolling back works on volumes, but independently mounting does not.
+The crossref:zfs[zfs-zfs-snapshot-hold,release] command removes the hold so the snapshot can be deleted. Snapshots, cloning, and rolling back works on volumes, but independently mounting does not.
|[[zfs-term-clone]]Clone
|Cloning a snapshot is also possible. A clone is a writable version of a snapshot, allowing the file system to fork as a new dataset. As with a snapshot, a clone initially consumes no new space. As new data written to a clone uses new blocks, the size of the clone grows. When blocks are overwritten in the cloned file system or volume, the reference count on the previous block decreases. Removing the snapshot upon which a clone bases is impossible because the clone depends on it. The snapshot is the parent, and the clone is the child. Clones can be _promoted_, reversing this dependency and making the clone the parent and the previous parent the child. This operation requires no new space. Since the amount of space used by the parent and child reverses, it may affect existing quotas and reservations.
+|[[zfs-term-bookmark]]Bookmark
+|A bookmark records the point in time at which a snapshot was taken, without keeping any of the snapshot data.
+Bookmarks serve as the source of incremental crossref:zfs[zfs-zfs-send,`zfs send`] streams, making it possible to destroy the snapshot on the sending side while retaining the ability to send further increments.
+Create a bookmark from an existing snapshot with `zfs bookmark`.
+See crossref:zfs[zfs-zfs-bookmark,Bookmarks] for details.
+
+|[[zfs-term-hold]]Hold
+|A hold is a named tag placed on a snapshot that prevents its destruction.
+Attempts to destroy a held snapshot fail with an `EBUSY` error until releasing every hold with `zfs release`.
+Holds protect snapshots that backup and replication tools still depend on.
+See crossref:zfs[zfs-zfs-snapshot-hold,Snapshot Holds] for details.
+
|[[zfs-term-checksum]]Checksum
|Every block is also checksummed. The checksum algorithm used is a per-dataset
property, see crossref:zfs[zfs-zfs-set,`set`]. The checksum of each block is
transparently validated when read, allowing ZFS to detect silent corruption. If
the data read does not match the expected checksum, ZFS will attempt to recover
-the data from any available redundancy, like mirrors or RAID-Z. Triggering a
+the data from any available redundancy, like mirrors or RAID-Z. Trigger a
validation of all checksums with crossref:zfs[zfs-term-scrub,`scrub`]. Checksum algorithms include:
* `fletcher2`
* `fletcher4`
* `sha256`
- The `fletcher` algorithms are faster, but `sha256` is a strong cryptographic hash and has a much lower chance of collisions at the cost of some performance. Deactivating checksums is possible, but strongly discouraged.
+* `sha512`
+* `skein`
+* `edonr`
+* `blake3` (since FreeBSD 14.0)
+
+The `fletcher` algorithms are faster, but the cryptographic hashes (`sha256`, `sha512`, `skein`, `edonr`, and `blake3`) have a much lower chance of collisions at the cost of some performance.
+The default value of `on` currently selects `fletcher4`; crossref:zfs[zfs-term-deduplication,deduplication] requires a cryptographic hash such as `sha256`.
+Deactivating checksums is possible, but strongly discouraged.
|[[zfs-term-compression]]Compression
-|Each dataset has a compression property, which defaults to off. Set this property to an available compression algorithm. This causes compression of all new data written to the dataset. Beyond a reduction in space used, read and write throughput often increases because fewer blocks need reading or writing.
+|Each dataset has a compression property. Since OpenZFS 2.2, new datasets have compression enabled by default (`compression=on`, which selects LZ4). This causes compression of all new data written to the dataset. Beyond a reduction in space used, read and write throughput often increases because fewer blocks need reading or writing.
[[zfs-term-compression-lz4]]
-* _LZ4_ - Added in ZFS pool version 5000 (feature flags), LZ4 is now the recommended compression algorithm. LZ4 works about 50% faster than LZJB when operating on compressible data, and is over three times faster when operating on uncompressible data. LZ4 also decompresses about 80% faster than LZJB. On modern CPUs, LZ4 can often compress at over 500 MB/s, and decompress at over 1.5 GB/s (per single CPU core).
+* _LZ4_ - Added in ZFS pool version 5000 (feature flags), LZ4 is the algorithm that `compression=on` selects and the recommended general-purpose choice. LZ4 works about 50% faster than LZJB when operating on compressible data, and is over three times faster when operating on uncompressible data. LZ4 also decompresses about 80% faster than LZJB. On modern CPUs, LZ4 can often compress at over 500 MB/s, and decompress at over 1.5 GB/s (per single CPU core).
+
+[[zfs-term-compression-zstd]]
+* _ZSTD_ - Zstandard offers higher compression ratios than LZ4 with a configurable trade-off between speed and compression, ranging from the fast `zstd-fast` variants to `zstd-19` for maximum space savings. See crossref:zfs[zfs-zfs-compression-zstd,Zstandard Compression] for details.
[[zfs-term-compression-lzjb]]
-* _LZJB_ - The default compression algorithm. Created by Jeff Bonwick (one of the original creators of ZFS). LZJB offers good compression with less CPU overhead compared to GZIP. In the future, the default compression algorithm will change to LZ4.
+* _LZJB_ - A legacy compression algorithm, created by Jeff Bonwick (one of the original creators of ZFS). LZJB offers good compression with less CPU overhead compared to GZIP. LZ4 has replaced it as the default algorithm; prefer LZ4 or ZSTD for new datasets.
[[zfs-term-compression-gzip]]
* _GZIP_ - A popular stream compression algorithm available in ZFS. One of the main advantages of using GZIP is its configurable level of compression. When setting the `compress` property, the administrator can choose the level of compression, ranging from `gzip1`, the lowest level of compression, to `gzip9`, the highest level of compression. This gives the administrator control over how much CPU time to trade for saved disk space.
@@ -3263,8 +4378,17 @@
|When set to a value greater than 1, the `copies` property instructs ZFS to
maintain copies of each block in the crossref:zfs[zfs-term-filesystem,file
system] or crossref:zfs[zfs-term-volume,volume]. Setting this property on important datasets provides added redundancy from which to recover a block that does not match its checksum. In pools without redundancy, the copies feature is the single form of redundancy. The copies feature can recover from a single bad sector or other forms of minor corruption, but it does not protect the pool from the loss of an entire disk.
+
|[[zfs-term-deduplication]]Deduplication
-|Checksums make it possible to detect duplicate blocks when writing data. With deduplication, the reference count of an existing, identical block increases, saving storage space. ZFS keeps a deduplication table (DDT) in memory to detect duplicate blocks. The table contains a list of unique checksums, the location of those blocks, and a reference count. When writing new data, ZFS calculates checksums and compares them to the list. When finding a match it uses the existing block. Using the SHA256 checksum algorithm with deduplication provides a secure cryptographic hash. Deduplication is tunable. If `dedup` is `on`, then a matching checksum means that the data is identical. Setting `dedup` to `verify`, ZFS performs a byte-for-byte check on the data ensuring they are actually identical. If the data is not identical, ZFS will note the hash collision and store the two blocks separately. As the DDT must store the hash of each unique block, it consumes a large amount of memory. A general rule of thumb is 5-6 GB of ram per 1 TB of deduplicated data). In situations not practical to have enough RAM to keep the entire DDT in memory, performance will suffer greatly as the DDT must read from disk before writing each new block. Deduplication can use L2ARC to store the DDT, providing a middle ground between fast system memory and slower disks. Consider using compression instead, which often provides nearly as much space savings without the increased memory.
+|Checksums make it possible to detect duplicate blocks when writing data. With deduplication, the reference count of an existing, identical block increases, saving storage space. ZFS keeps a deduplication table (DDT) in memory to detect duplicate blocks. The table contains a list of unique checksums, the location of those blocks, and a reference count. When writing new data, ZFS calculates checksums and compares them to the list. When finding a match it uses the existing block. Using the SHA256 checksum algorithm with deduplication provides a secure cryptographic hash. Deduplication is tunable. If `dedup` is `on`, then a matching checksum means that the data is identical. Setting `dedup` to `verify`, ZFS performs a byte-for-byte check on the data ensuring they are actually identical. If the data is not identical, ZFS will note the hash collision and store the two blocks separately. As the DDT must store the hash of each unique block, it consumes a large amount of memory. A general rule of thumb is 5-6 GB of RAM per 1 TB of deduplicated data. In situations not practical to have enough RAM to keep the entire DDT in memory, performance will suffer greatly as the DDT must read from disk before writing each new block. Deduplication can use L2ARC to store the DDT, providing a middle ground between fast system memory and slower disks. Consider using compression instead, which often provides nearly as much space savings without the increased memory.
+_Fast dedup_ batches DDT updates in a log, allows pruning stale entries with `zpool ddtprune`, and caps the table size with the `dedup_table_quota` pool property; it requires OpenZFS 2.3 or later, first available in FreeBSD 15.0.
+See crossref:zfs[zfs-zfs-deduplication,Deduplication] for details.
+
+|[[zfs-term-block-cloning]]Block Cloning
+|Block cloning allows a file copy to reference the existing on-disk blocks of the source file instead of duplicating them, so the copy completes almost instantly and consumes no extra space until either file changes.
+On FreeBSD, man:cp[1] uses man:copy_file_range[2], so ordinary file copies become block clones when the feature is enabled (the default on FreeBSD 15.0).
+The `bcloneused`, `bclonesaved`, and `bcloneratio` pool properties report the resulting space savings.
+See crossref:zfs[zfs-zfs-block-cloning,Block Cloning] for details.
|[[zfs-term-scrub]]Scrub
|Instead of a consistency check like man:fsck[8], ZFS has `scrub`. `scrub` reads
@@ -3272,11 +4396,22 @@
known good checksums stored in the metadata. A periodic check of all the data
stored on the pool ensures the recovery of any corrupted blocks before needing
them. A scrub is not required after an unclean shutdown, but good practice is at
-least once every three months. ZFS verifies the checksum of each block during
+least once every month. ZFS verifies the checksum of each block during
normal use, but a scrub makes certain to check even infrequently used blocks for
silent corruption. ZFS improves data security in archival storage situations.
-Adjust the relative priority of `scrub` with
-crossref:zfs[zfs-advanced-tuning-scrub_delay,`vfs.zfs.scrub_delay`] to prevent the scrub from degrading the performance of other workloads on the pool.
+Pause a running scrub with `zpool scrub -p` and resume it later by running `zpool scrub` again.
+See crossref:zfs[zfs-zpool-scrub,Scrubbing a Pool] for details.
+
+|[[zfs-term-checkpoint]]Checkpoint
+|A checkpoint preserves the entire state of a pool at a single point in time, including pool-wide configuration changes that snapshots cannot capture.
+Rewinding to a checkpoint with `zpool import --rewind-to-checkpoint` undoes everything that happened after taking it, such as enabling pool features, adding or removing vdevs, or destroying datasets.
+A pool has at most one checkpoint at a time.
+See crossref:zfs[zfs-zpool-checkpoint,Pool Checkpoints] for details.
+
+|[[zfs-term-trim]]TRIM
+|TRIM notifies SSDs and other flash-based storage which blocks the pool no longer uses, allowing the device to erase them in advance and sustain write performance.
+Run `zpool trim` manually, or set the `autotrim` pool property to `on` to trim freed space continuously.
+See crossref:zfs[zfs-zpool-trim,TRIM and Initialization] for details.
|[[zfs-term-quota]]Dataset Quota
a|ZFS provides fast and accurate dataset, user, and group space accounting as well as quotas and space reservations. This gives the administrator fine grained control over space allocation and allows reserving space for critical file systems.
@@ -3307,7 +4442,7 @@
for a specific dataset and its descendants. This means that setting a 10 GB
reservation on [.filename]#storage/home/bob# prevents other datasets from using
up all free space, reserving at least 10 GB of space for this dataset. Unlike a
-regular crossref:zfs[zfs-term-refreservation,`refreservation`], space used by snapshots and descendants is not counted against the reservation. For example, if taking a snapshot of [.filename]#storage/home/bob#, enough disk space other than the `refreservation` amount must exist for the operation to succeed. Descendants of the main data set are not counted in the `refreservation` amount and so do not encroach on the space set.
+regular crossref:zfs[zfs-term-refreservation,`refreservation`], space used by snapshots and descendant datasets does count against the reservation.
Reservations of any sort are useful in situations such as planning and testing the suitability of disk space allocation in a new system, or ensuring that enough space is available on file systems for audio logs or system recovery procedures and files.
@@ -3315,11 +4450,21 @@
|The `refreservation` property makes it possible to guarantee an amount of space
for the use of a specific dataset _excluding_ its descendants. This means that
setting a 10 GB reservation on [.filename]#storage/home/bob#, and another
-dataset tries to use the free space, reserving at least 10 GB of space for this
-dataset. In contrast to a regular crossref:zfs[zfs-term-reservation,reservation], space used by snapshots and descendant datasets is not counted against the reservation. For example, if taking a snapshot of [.filename]#storage/home/bob#, enough disk space other than the `refreservation` amount must exist for the operation to succeed. Descendants of the main data set are not counted in the `refreservation` amount and so do not encroach on the space set.
+dataset tries to use the free space, reserving at least 10 GB of space for this
+dataset. In contrast to a regular crossref:zfs[zfs-term-reservation,reservation], space used by snapshots and descendant datasets is not counted against the reservation. For example, if taking a snapshot of [.filename]#storage/home/bob#, enough disk space other than the `refreservation` amount must exist for the operation to succeed. Descendants of the main data set are not counted in the `refreservation` amount and so do not encroach on the space set.
+
+|[[zfs-term-boot-environment]]Boot Environment
+|A boot environment is a bootable clone of the datasets that hold the FreeBSD system, managed with man:bectl[8].
+Creating a boot environment before a system upgrade makes it possible to boot the previous, known-good system from the loader menu if the upgrade causes problems.
+See crossref:zfs[zfs-boot-environments,Boot Environments] for details.
|[[zfs-term-resilver]]Resilver
-|When replacing a failed disk, ZFS must fill the new disk with the lost data. _Resilvering_ is the process of using the parity information distributed across the remaining drives to calculate and write the missing data to the new drive.
+|When replacing a failed disk, ZFS must fill the new disk with the lost data.
+_Resilvering_ is the process of writing that data to the replacement device: mirrors resilver by copying the data from the surviving members, while RAID-Z reconstructs it from the parity information distributed across the remaining drives.
+Unlike a traditional RAID rebuild, resilvering copies allocated data alone rather than every sector of the device.
+A _sequential resilver_ (`zpool attach -s` or `zpool replace -s`) rebuilds mirror and dRAID vdevs even faster by copying blocks in disk order, skipping checksum verification and deferring it to an automatically scheduled scrub.
+See crossref:zfs[zfs-zpool-resilver,Dealing with Failed Devices] for details.
+
|[[zfs-term-online]]Online
|A pool or vdev in the `Online` state has its member devices connected and fully operational. Individual devices in the `Online` state are functioning.
@@ -3342,4 +4487,15 @@
number of missing or failed devices exceeds the level of redundancy in the vdev.
If reconnecting missing devices the pool will return to an
crossref:zfs[zfs-term-online,Online] state. Insufficient redundancy to compensate for the number of failed disks loses the pool contents and requires restoring from backups.
+
+|[[zfs-term-unavail]]Unavail
+|A device in the `Unavail` state cannot be opened, because it is missing or inaccessible.
+The pool continues operating if enough redundancy exists to compensate, running in a crossref:zfs[zfs-term-degraded,Degraded] state.
+If a top-level vdev is `Unavail`, accessing the pool contents is impossible.
+
+|[[zfs-term-removed]]Removed
+|A device in the `Removed` state was physically detached while the system was running.
+Device removal detection is hardware-dependent and unsupported on some platforms.
+When the device reconnects, man:zfsd[8] brings it back online; if the device does not return, zfsd activates an available hot spare in its place.
|===
+

File Metadata

Mime Type
text/plain
Expires
Fri, Jul 17, 2:50 PM (15 h, 31 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
35029441
Default Alt Text
D58175.id181823.diff (187 KB)

Event Timeline