Logo

ZFS on Linux: the ultimate filesystem

ZFS on Linux: the ultimate filesystem

ZFS combines filesystem and volume manager: snapshots, compression, deduplication, RAID-Z, replication, integrity checking. The filesystem every serious sysadmin should know.

Introduction

ZFS (Zettabyte File System), ported from OpenSolaris to Linux:

  • SHA-256 checksums on every block (silent data corruption detection)
  • Near-instant snapshots and clones
  • Transparent compression (LZ4, ZSTD, gzip)
  • Deduplication (optional, RAM-intensive)
  • RAID-Z (RAID5/6 without the "write hole")
  • Async replication (zfs send/zfs receive)
  • Virtual volumes (ZVOL) for iSCSI/VM
  • Practical limits: 256 zettabytes

Use cases: file servers, backup, VM storage, NAS, datalakes.

Prerequisites

  • Linux VPS / server Debian 12 / Ubuntu 24.04
  • 8 GB RAM minimum (ZFS loves RAM)
  • Root access
  • At least 2 disks (or partitions) for mirrors/RAIDZ

Step 1: Installation

Ubuntu (native since 20.04)

sudo apt update
sudo apt install -y zfsutils-linux
zfs version

Debian (contrib repo)

sudo nano /etc/apt/sources.list
# Add "contrib" to main lines

sudo apt update
sudo apt install -y zfs-dkms zfsutils-linux
sudo modprobe zfs
zfs version

Step 2: List disks

lsblk
sudo fdisk -l

Identify empty disks. Use /dev/disk/by-id/ for stability:

ls -lh /dev/disk/by-id/

Step 3: Simple pool

sudo zpool create datapool /dev/disk/by-id/ata-WDC_WD20EFRX_xxx
sudo zpool status
sudo zfs list

Pool datapool auto-mounts at /datapool.

⚠️ For production, never create a pool on a single disk. Use mirror or raidz.

Step 4: Mirror (RAID1)

sudo zpool create datapool mirror \
    /dev/disk/by-id/ata-WDC_WD20EFRX_aaa \
    /dev/disk/by-id/ata-WDC_WD20EFRX_bbb

Tolerates 1 disk loss, double read perf, same write as single.

Step 5: RAID-Z (RAID5)

For 4 disks with 1 parity (tolerates 1 dead disk):

sudo zpool create datapool raidz \
    /dev/disk/by-id/ata-disk-1 \
    /dev/disk/by-id/ata-disk-2 \
    /dev/disk/by-id/ata-disk-3 \
    /dev/disk/by-id/ata-disk-4

RAID-Z2 (RAID6, tolerates 2 disks):

sudo zpool create datapool raidz2 disk1 disk2 disk3 disk4 disk5 disk6

RAID-Z3 (3 parities) for 8+ disks.

Step 6: Datasets

A dataset = sub-FS in the pool with its own properties:

sudo zfs create datapool/home
sudo zfs create datapool/var
sudo zfs create datapool/backups
sudo zfs list
sudo zfs set compression=zstd datapool/backups
sudo zfs set compression=lz4 datapool/home
sudo zfs set atime=off datapool
sudo zfs set recordsize=1M datapool/backups
sudo zfs set recordsize=16K datapool/var/lib/mysql

Step 7: Compression

sudo zfs set compression=lz4 datapool
sudo zfs get compression,compressratio datapool

LZ4: fast, free CPU. ZSTD: better compression, more CPU. For cold data, ZSTD-19:

sudo zfs set compression=zstd-19 datapool/archives

Step 8: Snapshots

sudo zfs snapshot datapool/home@before-update
sudo zfs list -t snapshot

Instant, COW, space-efficient. Thousands possible.

Rollback:

sudo zfs rollback datapool/home@before-update

⚠️ Destroys later changes.

Read-only access:

ls /datapool/home/.zfs/snapshot/before-update/

Clone:

sudo zfs clone datapool/home@before-update datapool/home-test

Delete:

sudo zfs destroy datapool/home@before-update

Step 9: Replication (zfs send/receive)

sudo zfs snapshot datapool/home@2026-05-17
sudo zfs send datapool/home@2026-05-17 | ssh root@backup-server "zfs receive backuppool/home"

Incremental:

sudo zfs send -i datapool/home@2026-05-16 datapool/home@2026-05-17 | ssh root@backup-server "zfs receive backuppool/home"

For auto replication, use sanoid + syncoid:

sudo apt install -y sanoid
sudo nano /etc/sanoid/sanoid.conf
[datapool/home]
    use_template = production

[template_production]
    hourly = 36
    daily = 30
    monthly = 6
    yearly = 3
    autosnap = yes
    autoprune = yes
sudo systemctl enable --now sanoid.timer

syncoid --recursive datapool/home root@backup:backuppool/home

Step 10: ARC tuning

ZFS uses RAM cache (ARC). Default: up to 50% RAM.

sudo nano /etc/modprobe.d/zfs.conf
options zfs zfs_arc_max=4294967296    # 4 GB in bytes
options zfs zfs_arc_min=1073741824    # 1 GB
sudo update-initramfs -u
sudo reboot

Monitor:

arc_summary
sudo cat /proc/spl/kstat/zfs/arcstats

Step 11: Maintenance and scrub

Scrub = full integrity check.

sudo zpool scrub datapool
sudo zpool status datapool

Monthly cron:

0 2 1 * * /usr/sbin/zpool scrub datapool
sudo zpool status -v datapool

ZFS auto-repairs detected errors if enough redundancy.

Step 12: Advanced operations

Hot spare

sudo zpool add datapool spare /dev/disk/by-id/ata-new-disk

Replace dead disk

sudo zpool replace datapool old-disk new-disk

L2ARC cache (SSD)

sudo zpool add datapool cache /dev/disk/by-id/nvme-cache-disk

SLOG for ZIL (NVMe)

sudo zpool add datapool log /dev/disk/by-id/nvme-slog

Useful for sync workloads (databases, NFS).

Export / import

sudo zpool export datapool
sudo zpool import datapool
sudo zpool import -d /dev/disk/by-id/ datapool

Troubleshooting

Pool DEGRADED

sudo zpool status -v
sudo zpool replace datapool /dev/disk/by-id/bad-disk /dev/disk/by-id/new-disk
sudo zpool status         # watch resilver

"out of memory"

ARC too big. Limit zfs_arc_max.

Degraded performance

zpool iostat -v datapool 5

Check slow disk (high latency). Consider SLOG or L2ARC.

Pool lost after reboot

sudo zpool import
sudo zpool import datapool
sudo systemctl enable zfs-import-cache.service zfs-mount.service

Quota reached

sudo zfs set quota=50G datapool/home
sudo zfs set quota=none datapool/home    # remove

Useful commands

sudo zpool create / destroy / status / import / export
sudo zpool list
sudo zpool iostat -v 5
sudo zpool scrub datapool

sudo zfs create datapool/foo
sudo zfs destroy datapool/foo
sudo zfs list
sudo zfs list -t snapshot
sudo zfs get all datapool/foo

sudo zfs snapshot datapool/foo@snap1
sudo zfs rollback datapool/foo@snap1
sudo zfs clone datapool/foo@snap1 datapool/foo-clone

sudo zfs send datapool/foo@snap1 | ssh remote zfs receive otherpool/foo
sudo zfs send -i @snap1 datapool/foo@snap2 | ssh remote zfs receive otherpool/foo

arc_summary
sudo zpool history
sudo zfs get compressratio datapool

sudo cat /proc/spl/kstat/zfs/arcstats | grep size

Conclusion

ZFS is the Linux storage Swiss Army knife:

  • Integrity (checksums + scrub)
  • Instant snapshots / replication
  • Compression / dedup
  • Robust RAID-Z

Limits:

  • RAM-hungry (1 GB per TB recommended)
  • CDDL license incompatible with GPL (not in mainline Linux kernel)
  • No pool shrink

Going further:

  • Use sanoid/syncoid for auto snapshots and replication
  • Combine with TrueNAS Scale for full UI and turnkey NAS
  • For max perf, add SLOG NVMe + L2ARC SSD

Resources

Join our Discord community server

For any questions, suggestions, or just to chat with the community, join us on Discord!

900+Members