← All articles
STORAGE ZFS Storage Pool Setup: The Complete Home Lab Guide 2026-02-09 · zfs · storage · raid

ZFS Storage Pool Setup: The Complete Home Lab Guide

Storage 2026-02-09 zfs storage raid nas backup homelab

ZFS is the filesystem that makes storage nerds emotional. Originally developed by Sun Microsystems, it combines a filesystem and volume manager into one integrated system. It checksums every block of data to prevent silent corruption, supports instant snapshots, built-in compression, and flexible RAID configurations — all without needing a hardware RAID controller.

For home labs, ZFS is the gold standard for storing anything you care about. But it has a learning curve, and getting the initial setup right matters. This guide walks through everything you need to know to build a ZFS storage pool that will serve your home lab well.

Why ZFS

Every filesystem stores data on disk. What makes ZFS different is what it does beyond that:

The trade-off: ZFS is hungry for RAM. The general recommendation is 1 GB of RAM per TB of storage, plus whatever your OS and applications need. For a 16 TB pool, budget at least 16 GB for ZFS alone. ECC RAM is recommended but not strictly required — the ZFS community has moved past the old "you MUST use ECC" stance, though ECC is always preferable.

Pool Types Explained

A ZFS pool (zpool) is made up of one or more "vdevs" (virtual devices). The pool stripes data across vdevs. Each vdev can be a single disk, a mirror, or a RAID-Z group. Understanding vdevs is key to understanding ZFS.

Single Disk (No Redundancy)

zpool create tank /dev/sda

One disk, no redundancy. If the disk dies, your data is gone. Fine for scratch space or data you can easily re-download. Terrible for anything else.

Mirror (RAID 1 Equivalent)

zpool create tank mirror /dev/sda /dev/sdb

Two or more disks holding identical copies. You can lose any disk except the last one without data loss. 50% storage efficiency with 2 disks, 33% with 3, and so on.

Best for: Small pools (2-4 disks), maximum resilience, best random read performance.

RAID-Z1 (RAID 5 Equivalent)

zpool create tank raidz1 /dev/sda /dev/sdb /dev/sdc /dev/sdd

Distributes data across disks with single-parity redundancy. Can survive one disk failure. With 4 disks, you get 75% storage efficiency.

Best for: 3-5 disks where you want a balance of redundancy and capacity.

RAID-Z2 (RAID 6 Equivalent)

zpool create tank raidz2 /dev/sda /dev/sdb /dev/sdc /dev/sdd /dev/sde /dev/sdf

Double parity. Can survive two simultaneous disk failures. With 6 disks, you get 67% storage efficiency.

Best for: 5-8 disks, especially with larger drives where rebuild times are long.

RAID-Z3

Triple parity. Can survive three disk failures. Rarely needed in home labs but exists for large pools with high-capacity drives.

Practical Recommendations

Disk Count Recommended Layout Storage Efficiency Fault Tolerance
2 Mirror 50% 1 disk
3 RAIDZ1 67% 1 disk
4 2x Mirror 50% 1 per mirror
4 RAIDZ1 75% 1 disk
5-6 RAIDZ2 60-67% 2 disks
6 3x Mirror 50% 1 per mirror
8+ 2x RAIDZ2 67-75% 2 per vdev

The 4-disk decision is the classic home lab dilemma: RAIDZ1 gives you 75% capacity but only tolerates one failure. Two mirrors give you 50% capacity but can tolerate one failure per mirror pair and deliver much better random I/O performance. If performance matters, go mirrors. If capacity matters, go RAIDZ1.

Creating Your First Pool

Identify Your Disks

# List all disks
lsblk

# Get disk IDs (use these instead of /dev/sdX — they're persistent)
ls -la /dev/disk/by-id/

# You'll see entries like:
# ata-WDC_WD40EFRX-68N32N0_WD-WCC7K0PL1234
# Use these full paths for your pool

Always use /dev/disk/by-id/ paths when creating ZFS pools. The /dev/sdX names can change between reboots as disks are detected in different orders. Disk IDs are permanent.

Create the Pool

# Mirror of two 4TB drives
zpool create -o ashift=12 tank \
  mirror \
    /dev/disk/by-id/ata-WDC_WD40EFRX-68N32N0_WD-AAA \
    /dev/disk/by-id/ata-WDC_WD40EFRX-68N32N0_WD-BBB

# RAIDZ1 with four drives
zpool create -o ashift=12 tank \
  raidz1 \
    /dev/disk/by-id/ata-WDC_WD40EFRX-68N32N0_WD-AAA \
    /dev/disk/by-id/ata-WDC_WD40EFRX-68N32N0_WD-BBB \
    /dev/disk/by-id/ata-WDC_WD40EFRX-68N32N0_WD-CCC \
    /dev/disk/by-id/ata-WDC_WD40EFRX-68N32N0_WD-DDD

The -o ashift=12 flag tells ZFS to use 4K sectors, which matches modern drives. Always include it — the default of ashift=9 (512-byte sectors) causes poor performance on modern drives, and you can't change it after pool creation.

Configure Pool Properties

# Enable LZ4 compression (recommended for almost everything)
zfs set compression=lz4 tank

# Set a reasonable record size for general use
zfs set recordsize=128k tank

# Enable extended attributes
zfs set xattr=sa tank

# Disable access time updates (reduces unnecessary writes)
zfs set atime=off tank

Create Datasets

Datasets are like sub-filesystems within your pool. Each can have its own settings (compression, quotas, snapshots). Use them instead of plain directories.

# Create datasets for different purposes
zfs create tank/media
zfs create tank/backups
zfs create tank/vms
zfs create tank/documents

# Set different record sizes for different workloads
zfs set recordsize=1M tank/media        # Large files benefit from larger records
zfs set recordsize=64k tank/vms         # VM images work well with 64k
zfs set recordsize=128k tank/backups    # General purpose

# Set a quota to prevent one dataset from eating all the space
zfs set quota=2T tank/media

# Check your datasets
zfs list

Snapshots

Snapshots are ZFS's superpower. They're instant, nearly free (they only consume space as data changes), and make recovery trivial.

# Take a snapshot
zfs snapshot tank/documents@2026-02-09

# List snapshots
zfs list -t snapshot

# See how much space a snapshot is using
zfs list -t snapshot -o name,used,refer

# Roll back to a snapshot (destroys all changes since)
zfs rollback tank/documents@2026-02-09

# Clone a snapshot into a new dataset (for testing)
zfs clone tank/documents@2026-02-09 tank/documents-test

# Delete a snapshot
zfs destroy tank/documents@2026-02-09

Automated Snapshots

Don't manage snapshots manually. Use sanoid for automated snapshot management with configurable retention policies.

# Install sanoid
sudo apt install sanoid

# Configure /etc/sanoid/sanoid.conf
[tank/documents]
  use_template = production

[tank/media]
  use_template = media

[template_production]
  hourly = 24
  daily = 30
  monthly = 6
  autosnap = yes
  autoprune = yes

[template_media]
  daily = 7
  monthly = 3
  autosnap = yes
  autoprune = yes
# Enable the sanoid timer
sudo systemctl enable --now sanoid.timer

Sanoid takes snapshots on schedule and automatically prunes old ones based on your retention policy. Its companion tool syncoid handles replication — sending snapshots to another ZFS system for off-site backup.

Scrubs

A scrub reads every block on every disk in the pool and verifies checksums. If it finds corruption on a redundant pool, it automatically repairs it using the good copy. This is the feature that makes ZFS worth the complexity.

# Run a scrub manually
zpool scrub tank

# Check scrub status
zpool status tank

# You'll see something like:
#   scan: scrub in progress since Sun Feb 09 02:00:00 2026
#     1.23T scanned at 456M/s, 890G issued at 334M/s
#     0 repaired, 72.3% done

Schedule scrubs to run regularly. Monthly is the standard recommendation for home labs:

# Create a systemd timer (or use cron)
# /etc/cron.d/zfs-scrub
0 2 1 * * root /sbin/zpool scrub tank

On Proxmox, ZFS scrubs are already scheduled automatically — check with systemctl list-timers | grep zfs.

Monitoring Your Pool

# Pool health overview
zpool status

# Pool I/O statistics
zpool iostat -v 5    # Updates every 5 seconds

# Dataset space usage
zfs list

# Detailed pool information
zpool get all tank

# Check compression ratio
zfs get compressratio tank

A healthy pool shows state: ONLINE for the pool and every disk. If any disk shows DEGRADED, FAULTED, or UNAVAIL, you need to investigate immediately.

# Example of a healthy pool
# NAME                   STATE     READ WRITE CKSUM
# tank                   ONLINE       0     0     0
#   mirror-0             ONLINE       0     0     0
#     ata-WDC...WD-AAA   ONLINE       0     0     0
#     ata-WDC...WD-BBB   ONLINE       0     0     0

The READ, WRITE, and CKSUM columns show error counts. Any non-zero value in CKSUM is a red flag — it means ZFS detected (and with redundancy, repaired) silent data corruption. Non-zero READ or WRITE errors suggest a failing disk.

Adding Capacity

You can expand a ZFS pool by adding new vdevs. You cannot add individual disks to an existing RAIDZ vdev (though this feature is in development as of OpenZFS 2.3).

# Add another mirror pair to an existing pool
zpool add tank mirror \
  /dev/disk/by-id/ata-WDC...-CCC \
  /dev/disk/by-id/ata-WDC...-DDD

# Replace a failed disk
zpool replace tank \
  /dev/disk/by-id/ata-OLD-DISK \
  /dev/disk/by-id/ata-NEW-DISK

Common Mistakes

Not using disk-by-id paths: Using /dev/sda works until you add or remove a disk and everything shifts. Always use /dev/disk/by-id/.

Forgetting ashift=12: Create the pool with -o ashift=12. You can't change this later. Wrong ashift on modern drives tanks performance.

No scrubs: A pool without regular scrubs is a pool that might have silent corruption you don't know about. Schedule them monthly.

RAIDZ1 with large drives: Rebuilding a RAIDZ1 pool after a disk failure with 8+ TB drives can take days. During that rebuild, if another disk fails, you lose everything. Use RAIDZ2 for large drives.

Treating ZFS like backup: RAID is not backup. ZFS with mirrors protects against disk failure. It does not protect against accidental deletion, ransomware, or your house burning down. Use snapshots for accidental deletion recovery, and replicate to another system (or off-site) for real backup.

ZFS rewards you for doing the setup right the first time. Take the time to understand your vdev layout, enable compression, set up automated snapshots, and schedule scrubs. Your data will thank you.