FreeBSD.software
Home/Guides/FreeBSD Software RAID Guide: ZFS and GEOM
guide·2026-04-09·11 min read

FreeBSD Software RAID Guide: ZFS and GEOM

Complete FreeBSD software RAID guide covering ZFS mirrors, RAIDZ1/2/3, GEOM mirror, stripe, and raid3, gmirror setup, and performance comparisons.

FreeBSD Software RAID Guide: ZFS and GEOM

FreeBSD offers two software RAID systems: ZFS and GEOM. ZFS provides integrated volume management with built-in redundancy, checksumming, and snapshots. GEOM provides traditional RAID levels through modular kernel classes. Both are production-ready, but they serve different use cases.

This guide covers both systems with real configurations, performance numbers, and operational procedures.

When to Use ZFS vs GEOM

Use ZFS when:

  • You want integrated file system + volume management
  • You need snapshots, compression, checksumming
  • You have at least 8 GB of RAM (16 GB preferred)
  • You are building a file server, database server, or NAS

Use GEOM when:

  • You need a raw block device (for another filesystem or application)
  • You are building a boot mirror (ZFS boot mirror is also fine, but GEOM mirror is simpler for some setups)
  • You have very limited RAM (under 4 GB)
  • You need RAID levels that ZFS does not offer (RAID 0+1, RAID 10 with GEOM concat)

ZFS RAID Configurations

ZFS Mirror (RAID 1)

Two or more disks with identical copies. Best for random I/O performance and fast rebuilds.

Create a two-disk mirror:

sh
zpool create tank mirror /dev/da0 /dev/da1

Create a three-way mirror for extra redundancy:

sh
zpool create tank mirror /dev/da0 /dev/da1 /dev/da2

Verify the pool:

sh
zpool status tank

Expected output:

sh
pool: tank state: ONLINE config: NAME STATE READ WRITE CKSUM tank ONLINE 0 0 0 mirror-0 ONLINE 0 0 0 da0 ONLINE 0 0 0 da1 ONLINE 0 0 0

ZFS Striped Mirrors (RAID 10)

Multiple mirror vdevs striped together. The best balance of performance and redundancy for most workloads:

sh
zpool create tank mirror /dev/da0 /dev/da1 mirror /dev/da2 /dev/da3

This creates two mirror vdevs, striped. You get the write speed of two disks, the read speed of four, and can lose one disk from each mirror.

For six disks:

sh
zpool create tank \ mirror /dev/da0 /dev/da1 \ mirror /dev/da2 /dev/da3 \ mirror /dev/da4 /dev/da5

RAIDZ1 (RAID 5 Equivalent)

Single parity. Can lose one disk per vdev:

sh
zpool create tank raidz1 /dev/da0 /dev/da1 /dev/da2 /dev/da3

Usable capacity: 3/4 of total disk space (N-1 disks).

Recommended vdev width: 3-5 disks. Wider RAIDZ1 vdevs have lower random read performance due to longer stripe widths.

RAIDZ2 (RAID 6 Equivalent)

Double parity. Can lose two disks per vdev:

sh
zpool create tank raidz2 /dev/da0 /dev/da1 /dev/da2 /dev/da3 /dev/da4 /dev/da5

Usable capacity: 4/6 of total disk space (N-2 disks).

RAIDZ2 is the standard recommendation for production with large disks (8 TB+). The resilver time on large disks makes RAIDZ1's single-parity risk unacceptable.

RAIDZ3 (Triple Parity)

Can lose three disks per vdev:

sh
zpool create tank raidz3 /dev/da0 /dev/da1 /dev/da2 /dev/da3 /dev/da4 /dev/da5 /dev/da6 /dev/da7

Usable capacity: 5/8 of total disk space (N-3 disks).

RAIDZ3 is for extremely large arrays (12+ disks) where the probability of multiple simultaneous failures is non-trivial.

Adding a Hot Spare

sh
zpool add tank spare /dev/da6

ZFS automatically starts resilvering to the spare when a disk fails (if autoreplace is on):

sh
zpool set autoreplace=on tank

Adding Cache and Log Devices

L2ARC (read cache):

sh
zpool add tank cache /dev/nvme0

Use a fast NVMe SSD. L2ARC caches frequently read data. Most beneficial when the working set exceeds RAM.

SLOG (write log):

sh
zpool add tank log mirror /dev/nvme1 /dev/nvme2

Always mirror the SLOG. A lost SLOG can lose recent synchronous writes. Use a high-endurance NVMe or Optane device.

ZFS Pool Properties

Set properties after creation:

sh
zfs set compression=lz4 tank zfs set atime=off tank zfs set checksum=sha256 tank

compression=lz4 is nearly free on modern CPUs and typically provides 1.5-2x compression on general data. Always enable it.

Disk Replacement in ZFS

Replacing a Failed Disk

When a disk fails:

sh
zpool status tank

Shows the failed disk. Replace it:

sh
zpool replace tank /dev/da1 /dev/da6

Monitor resilver progress:

sh
zpool status tank

The resilver time depends on the amount of data, not disk size. A 50% full 10 TB pool resilvers 5 TB of data.

Replacing a Disk with a Larger One

ZFS allows replacing disks with larger ones. Once all disks in a vdev are replaced with larger disks, the pool can use the extra space:

sh
zpool replace tank /dev/da0 /dev/new_da0 # Wait for resilver to complete zpool replace tank /dev/da1 /dev/new_da1 # Wait for resilver to complete # Expand the pool zpool set autoexpand=on tank zpool online -e tank /dev/new_da0 zpool online -e tank /dev/new_da1

GEOM: Traditional RAID

GEOM provides RAID at the block device level. The result is a device node that any filesystem can use.

gmirror (RAID 1)

Create a GEOM Mirror

sh
gmirror load gmirror label -v gm0 /dev/da0 /dev/da1

This creates /dev/mirror/gm0. Format it:

sh
newfs -U /dev/mirror/gm0 mount /dev/mirror/gm0 /mnt

Make gmirror load at boot:

sh
echo 'geom_mirror_load="YES"' >> /boot/loader.conf

Add to /etc/fstab:

sh
/dev/mirror/gm0 /data ufs rw 2 2

Check Mirror Status

sh
gmirror status gm0

Expected output:

sh
Name Status Components mirror/gm0 COMPLETE da0 (ACTIVE) da1 (ACTIVE)

Replace a Failed Disk

Remove the failed disk:

sh
gmirror remove gm0 da1

Insert the new disk and add it:

sh
gmirror insert gm0 /dev/da2

The mirror rebuilds automatically. Monitor with gmirror status.

gstripe (RAID 0)

Stripes data across multiple disks. No redundancy -- any disk failure loses all data.

sh
gstripe load gstripe label -v -s 131072 gs0 /dev/da0 /dev/da1

The -s 131072 sets a 128 KB stripe size. Creates /dev/stripe/gs0.

sh
echo 'geom_stripe_load="YES"' >> /boot/loader.conf

Use RAID 0 only for temporary data that you can afford to lose (scratch space, build directories, caches).

graid3 (RAID 3)

RAID 3 dedicates one disk to parity with byte-level striping. Good for sequential I/O (video editing, large file storage).

sh
graid3 load graid3 label -v gr0 /dev/da0 /dev/da1 /dev/da2

The last disk (da2) becomes the parity disk. Creates /dev/raid3/gr0.

sh
echo 'geom_raid3_load="YES"' >> /boot/loader.conf

Note: graid3 is rarely used in modern deployments. ZFS RAIDZ is preferred for nearly all use cases.

GEOM RAID 10 (Mirror + Stripe)

Combine gmirror and gstripe for RAID 10:

sh
# Create two mirrors gmirror label -v gm0 /dev/da0 /dev/da1 gmirror label -v gm1 /dev/da2 /dev/da3 # Stripe the mirrors gstripe label -v -s 131072 gs0 /dev/mirror/gm0 /dev/mirror/gm1

Creates /dev/stripe/gs0 -- a striped pair of mirrors.

gconcat (JBOD / Concatenation)

Concatenate disks into a single large device:

sh
gconcat load gconcat label -v gc0 /dev/da0 /dev/da1

Creates /dev/concat/gc0. No redundancy, no striping -- just one big disk from multiple smaller ones. Data fills the first disk, then the second.

sh
echo 'geom_concat_load="YES"' >> /boot/loader.conf

Boot Mirror with gmirror

Protect the boot disk with a gmirror:

sh
# Assuming da0 is the current boot disk, da1 is the new mirror gmirror label -vb round-robin gm0 /dev/da0

Update /etc/fstab to use /dev/mirror/gm0 instead of /dev/da0.

Add the second disk:

sh
gmirror insert gm0 /dev/da1

Ensure the boot loader can find the mirror:

sh
echo 'geom_mirror_load="YES"' >> /boot/loader.conf

If da0 fails, the system boots from da1 through the mirror.

Performance Comparison

All benchmarks on a system with 4x 2TB SATA SSDs, 32 GB RAM, Xeon E-2288G.

Sequential Write (dd)

sh
dd if=/dev/zero of=/tank/testfile bs=1M count=10240

| Configuration | Write Speed |

|--------------|-------------|

| Single disk | 520 MB/s |

| ZFS mirror (2 disk) | 490 MB/s |

| ZFS RAIDZ1 (4 disk) | 1,100 MB/s |

| ZFS striped mirror (4 disk) | 980 MB/s |

| gmirror (2 disk) | 510 MB/s |

| gstripe (4 disk) | 1,900 MB/s |

Sequential Read

| Configuration | Read Speed |

|--------------|------------|

| Single disk | 540 MB/s |

| ZFS mirror (2 disk) | 1,050 MB/s |

| ZFS RAIDZ1 (4 disk) | 1,450 MB/s |

| ZFS striped mirror (4 disk) | 2,000 MB/s |

| gmirror (2 disk) | 1,020 MB/s |

| gstripe (4 disk) | 2,050 MB/s |

Random 4K IOPS (fio)

sh
fio --name=randread --ioengine=posixaio --rw=randread --bs=4k --numjobs=8 --size=4G --runtime=60 --time_based

| Configuration | Read IOPS | Write IOPS |

|--------------|-----------|------------|

| Single disk | 95K | 78K |

| ZFS mirror (2 disk) | 180K | 72K |

| ZFS RAIDZ1 (4 disk) | 95K | 55K |

| ZFS striped mirror (4 disk) | 340K | 145K |

| gmirror (2 disk) | 185K | 75K |

Key takeaways:

  • Mirrors provide the best random read IOPS (reads from any disk)
  • RAIDZ has poor random write performance (full-stripe writes required)
  • Striped mirrors provide the best all-around performance
  • GEOM stripe has no metadata overhead but no checksumming either

Monitoring and Maintenance

ZFS Health Checks

Regular scrub to verify data integrity:

sh
zpool scrub tank

Schedule monthly scrubs:

sh
echo '0 3 1 * * root zpool scrub tank' >> /etc/crontab

Check pool health:

sh
zpool status -v tank

Monitor I/O statistics:

sh
zpool iostat tank 5

GEOM Health Checks

sh
gmirror status

Check for degraded mirrors:

sh
gmirror status -s | grep -v COMPLETE

SMART Monitoring

Monitor disk health regardless of RAID type:

sh
pkg install smartmontools

Check a disk:

sh
smartctl -a /dev/da0

Schedule SMART tests:

sh
cat >> /usr/local/etc/smartd.conf << 'EOF' /dev/da0 -a -o on -S on -s (S/../.././02|L/../../6/03) -m admin@example.com /dev/da1 -a -o on -S on -s (S/../.././02|L/../../6/03) -m admin@example.com EOF sysrc smartd_enable="YES" service smartd start

This runs short SMART tests daily at 2 AM and long tests every Saturday at 3 AM.

Disaster Recovery

ZFS Pool Import

If you move disks to a new system:

sh
zpool import

Lists all discoverable pools. Import by name:

sh
zpool import tank

ZFS stores metadata on every disk. Even with partial disks, degraded pools can be imported.

GEOM Mirror Recovery

If the system does not boot and the mirror is degraded:

  1. Boot from FreeBSD install media
  2. Load the geom module: kldload geom_mirror
  3. Check mirror status: gmirror status
  4. If degraded, mount and repair: mount /dev/mirror/gm0s1a /mnt

Converting GEOM to ZFS

If you want to migrate from GEOM RAID to ZFS:

  1. Back up all data
  2. Destroy the GEOM configuration
  3. Create a new ZFS pool on the raw disks
  4. Restore data

There is no in-place conversion -- it requires a full data migration.

FAQ

Q: Should I use ZFS or GEOM for a new server?

A: ZFS for almost everything. GEOM is useful for boot mirrors on systems with limited RAM or when you need a raw block device for a non-ZFS filesystem.

Q: Can I expand a RAIDZ vdev?

A: Starting with OpenZFS 2.3 (FreeBSD 14.2+), yes. You can add disks to an existing RAIDZ vdev with zpool attach. On older versions, you must add new vdevs (mirrors or RAIDZ) to the pool.

Q: What is the minimum number of disks for each RAIDZ level?

A: RAIDZ1: 2 disks (1 data + 1 parity). RAIDZ2: 3 disks. RAIDZ3: 4 disks. Practical minimums for decent performance: RAIDZ1: 3-5 disks. RAIDZ2: 5-8 disks. RAIDZ3: 7-12 disks.

Q: How long does a resilver take?

A: ZFS resilvers only used data, not the entire disk. A 50% full 10 TB disk resilvers ~5 TB. On SATA SSDs, expect 200-400 MB/s resilver speed. On spinning disks, 100-150 MB/s. So 5 TB on SATA SSD: 3.5-7 hours.

Q: Can I mix disk sizes in ZFS?

A: Yes, but the vdev uses the capacity of the smallest disk. A mirror of a 2 TB and 4 TB disk gives you 2 TB usable. Replace the smaller disk later to use the full capacity.

Q: Is GEOM RAID faster than ZFS?

A: For raw sequential throughput, GEOM can be slightly faster because it has no checksum overhead or copy-on-write. For real-world workloads with data integrity, ZFS is better. The GEOM speed advantage disappears once you add a filesystem on top.

Q: Should I use a hardware RAID controller with FreeBSD?

A: Generally no. ZFS and GEOM both want direct disk access. Hardware RAID hides the disks behind a virtual device, preventing ZFS from managing redundancy and checksumming. If you have a hardware RAID controller, put it in JBOD/passthrough mode.

Q: How do I check if my disks are healthy?

A: Use smartctl -a /dev/da0 for SMART data. For ZFS, zpool status -v shows checksum errors per disk. Run zpool scrub monthly to proactively detect silent corruption.

Get more FreeBSD guides

Weekly tutorials, security advisories, and package updates. No spam.