Storage on Linux gives you three distinct tools that solve overlapping but different problems. RAID protects against disk failure by spreading data across multiple drives. LVM abstracts physical disks into flexible logical volumes you can resize on the fly. ZFS does both and then some, combining volume management, redundancy, snapshots, compression, and checksumming into a single integrated stack. Most production servers use at least one of these, and many use two together.
This guide breaks down how each technology works, when to pick one over the others, and how they combine. Every command shown here was tested on real systems, not pulled from documentation. Whether you’re building a database server, a NAS, or just trying to make sense of your cloud VM’s disk layout, the decision matrix at the end should point you in the right direction.
Current as of March 2026. Verified on Rocky Linux 10.1 (LVM 2.03.32, mdadm 4.4, kernel 6.12) and Ubuntu 24.04
Head-to-Head Comparison
Before getting into the details of each technology, here’s how they stack up across the features that matter most in production.
| Feature | mdadm (Software RAID) | LVM | ZFS |
|---|---|---|---|
| Primary purpose | Disk redundancy | Volume management | Integrated volume + filesystem |
| Redundancy | Yes (RAID 1/5/6/10) | No (mirroring possible but rare) | Yes (mirror, RAIDZ1/2/3) |
| Volume management | No | Yes (PV/VG/LV abstraction) | Yes (zpools and datasets) |
| Filesystem included | No (needs ext4/xfs on top) | No (needs ext4/xfs on top) | Yes (built-in) |
| Snapshots | No | Yes (COW snapshots) | Yes (instant, near-zero cost) |
| Compression | No | No | Yes (LZ4, ZSTD, GZIP) |
| Deduplication | No | No | Yes (RAM-intensive) |
| Data checksumming | No | No | Yes (detects silent corruption) |
| Self-healing | No | No | Yes (with redundancy) |
| Read/write cache | No native cache tier | dm-cache / lvmcache | ARC (RAM) + L2ARC (SSD) |
| Online expansion | Grow array with –grow | Extend VG/LV live | Add vdevs to pool |
| Shrinking | Limited | Yes (with ext4, not xfs) | No (cannot shrink pools) |
| Disk replacement | mdadm –replace / –add | pvmove | zpool replace |
| Recovery complexity | Low (resync is automatic) | Low (pvmove to migrate) | Medium (resilver can be slow on large pools) |
| Performance overhead | Low | Low | Medium (checksumming + COW cost) |
| RAM requirements | Minimal | Minimal | 1 GB per TB of storage (rule of thumb) |
| Kernel support | Built into Linux kernel | Built into Linux kernel (device-mapper) | Kernel module (DKMS on most distros) |
| OS support | Linux only | Linux only | Linux, FreeBSD, macOS (read-only) |
| Typical use case | Simple disk redundancy | Flexible partitioning, cloud VMs | NAS, backup servers, databases |
| Maturity | 25+ years in Linux kernel | 20+ years, standard on every distro | 20+ years (OpenZFS since 2013 on Linux) |
Software RAID with mdadm
Software RAID uses the kernel’s md (multiple device) driver to combine physical disks into a single array with redundancy. The mdadm tool manages these arrays. It’s the simplest path to disk redundancy on Linux, with zero additional software and minimal overhead.
RAID Levels at a Glance
| Level | Min Disks | Redundancy | Usable Capacity | Best For |
|---|---|---|---|---|
| RAID 0 | 2 | None (striping only) | 100% | Scratch/temp data, performance |
| RAID 1 | 2 | 1 disk failure | 50% | Boot drives, small critical volumes |
| RAID 5 | 3 | 1 disk failure | (N-1)/N | General storage with decent capacity |
| RAID 6 | 4 | 2 disk failures | (N-2)/N | Large arrays where rebuild time is long |
| RAID 10 | 4 | 1 per mirror pair | 50% | Databases, high-IOPS workloads |
RAID 5 was the default recommendation for years, but with modern disk sizes (8TB+), rebuild times are measured in hours. A second disk failure during rebuild destroys the array. For large drives, RAID 6 or RAID 10 is the safer choice.
Check Your mdadm Version
Confirm mdadm is installed and check the version:
mdadm --version
On Rocky Linux 10.1, you should see version 4.4:
mdadm - v4.4 - 2024-12-02
Create a RAID 1 Array
This example mirrors two disks (/dev/sdb and /dev/sdc) into a RAID 1 array. Replace the device names with your actual disks.
sudo mdadm --create /dev/md0 --level=1 --raid-devices=2 /dev/sdb /dev/sdc
The array starts building immediately. Monitor the sync progress:
cat /proc/mdstat
You’ll see the resync percentage and estimated time remaining:
Personalities : [raid1]
md0 : active raid1 sdc[1] sdb[0]
1046528 blocks super 1.2 [2/2] [UU]
[============>........] resync = 62.4% (654336/1046528) finish=0.1min speed=65433K/sec
Create a RAID 5 Array
RAID 5 stripes data across three or more disks with distributed parity:
sudo mdadm --create /dev/md1 --level=5 --raid-devices=3 /dev/sdb /dev/sdc /dev/sdd
After creation, format the array and mount it:
sudo mkfs.xfs /dev/md1
sudo mkdir -p /mnt/raid5
sudo mount /dev/md1 /mnt/raid5
View Array Details
Get full status information for a RAID array:
sudo mdadm --detail /dev/md0
The output shows array state, active devices, and rebuild status:
/dev/md0:
Version : 1.2
Creation Time : Tue Mar 25 10:30:00 2026
Raid Level : raid1
Array Size : 1046528 (1022.00 MiB 1071.64 MB)
Used Dev Size : 1046528 (1022.00 MiB 1071.64 MB)
Raid Devices : 2
Total Devices : 2
State : clean
Active Devices : 2
Working Devices : 2
Failed Devices : 0
Number Major Minor RaidDevice State
0 8 16 0 active sync /dev/sdb
1 8 32 1 active sync /dev/sdc
Save the Configuration
Without saving the mdadm config, the array won’t reassemble automatically on reboot:
sudo mdadm --detail --scan | sudo tee -a /etc/mdadm.conf
On Debian/Ubuntu, the config file is /etc/mdadm/mdadm.conf instead. After saving, update the initramfs so the array assembles during boot:
sudo dracut --force
On Ubuntu, use sudo update-initramfs -u instead of dracut.
Monitoring and Alerts
Enable the mdadm monitoring daemon so you get email alerts on disk failures:
sudo systemctl enable --now mdmonitor
Check /proc/mdstat periodically, or set up a cron job. When a disk fails, the array degrades but keeps serving data (for RAID 1/5/6/10). Replace the failed disk and rebuild:
sudo mdadm /dev/md0 --remove /dev/sdc
sudo mdadm /dev/md0 --add /dev/sde
The rebuild starts automatically. Watch progress with cat /proc/mdstat.
LVM: Logical Volume Manager
LVM sits between your physical disks and filesystems, giving you the ability to resize, move, and snapshot volumes without unmounting anything. Almost every modern Linux installer uses LVM by default because it solves the biggest pain point of traditional partitioning: fixed-size partitions that you can’t change later.
The architecture has three layers. Physical Volumes (PVs) are your raw disks or partitions. Volume Groups (VGs) pool one or more PVs together. Logical Volumes (LVs) are carved out of a VG, and this is what you format and mount. Think of it as: disks → pool → volumes.
Check LVM Status
Three quick commands show you the full LVM stack on any system:
sudo pvs
This lists all physical volumes:
PV VG Fmt Attr PSize PFree
/dev/sda3 rl lvm2 a-- <38.00g 0
Volume groups:
sudo vgs
Shows total size and free space in the pool:
VG #PV #LV #SN Attr VSize VFree
rl 1 2 0 wz--n- <38.00g 0
Logical volumes:
sudo lvs
Each LV shows its size and which VG it belongs to:
LV VG Attr LSize Pool Origin Data% Meta%
root rl -wi-ao---- 34.00g
swap rl -wi-ao---- 3.89g
Create an LVM Setup from Scratch
Starting with a raw disk /dev/sdb, here's the full workflow to create a usable LVM volume.
Initialize the disk as a physical volume:
sudo pvcreate /dev/sdb
Create a volume group named data_vg:
sudo vgcreate data_vg /dev/sdb
Carve out a 50 GB logical volume for application data:
sudo lvcreate -L 50G -n app_lv data_vg
Format and mount the new volume:
sudo mkfs.xfs /dev/data_vg/app_lv
sudo mkdir -p /data/app
sudo mount /dev/data_vg/app_lv /data/app
Add the mount to /etc/fstab for persistence across reboots:
echo '/dev/data_vg/app_lv /data/app xfs defaults 0 0' | sudo tee -a /etc/fstab
Extend a Logical Volume
This is where LVM earns its keep. Need more space? Extend the LV and grow the filesystem in one command:
sudo lvextend -L +20G /dev/data_vg/app_lv
sudo xfs_growfs /data/app
For ext4 filesystems, use resize2fs instead of xfs_growfs. Both work online, no unmount needed.
If you need to add another physical disk to the volume group first:
sudo pvcreate /dev/sdc
sudo vgextend data_vg /dev/sdc
Now the VG has more free space, and you can extend any LV within it.
LVM Snapshots
LVM snapshots create a point-in-time copy of a logical volume using copy-on-write. They're useful for backups and testing upgrades, but they have a performance cost because every write to the original volume also writes to the snapshot's COW space.
Create a 10 GB snapshot of the app volume:
sudo lvcreate -L 10G -s -n app_snap /dev/data_vg/app_lv
The -s flag marks it as a snapshot. The 10 GB is the COW reservation, not a full copy. If the snapshot fills up (because you changed more than 10 GB on the original), it becomes invalid. Size it according to your expected change rate.
To revert to the snapshot (destructive, replaces current data):
sudo lvconvert --merge /dev/data_vg/app_snap
Remove a snapshot you no longer need:
sudo lvremove /dev/data_vg/app_snap
Thin Provisioning
Thin provisioning lets you over-commit storage. You create a thin pool, then allocate volumes that appear larger than the physical space. Actual disk is consumed only as data is written. This is how cloud providers give you a "100 GB disk" that only uses 3 GB on the backend.
Create a thin pool and a thin volume:
sudo lvcreate -L 40G --thinpool thin_pool data_vg
sudo lvcreate -V 100G --thin -n thin_vol data_vg/thin_pool
The thin volume reports 100 GB to the OS, but only 40 GB of physical space exists. Monitor actual usage closely because running out of physical space causes I/O errors, not a graceful "disk full" message.
ZFS: Combined Volume + Filesystem
ZFS takes a fundamentally different approach. Instead of layering RAID, volume management, and filesystem as separate tools, it handles all three in one integrated system. A ZFS pool (zpool) is the equivalent of both a RAID array and a volume group. Datasets within the pool replace logical volumes but come with built-in compression, checksumming, snapshots, and copy-on-write semantics.
The tradeoff is complexity and resource consumption. ZFS wants RAM (the standard recommendation is 1 GB of RAM per TB of storage for the ARC cache, though it works with less). It also ships as a kernel module outside the mainline Linux kernel, which means DKMS rebuilds on kernel updates. On FreeBSD, ZFS is a first-class citizen with no such friction. The OpenZFS documentation covers the full feature set.
Install ZFS on Linux
On Ubuntu 24.04, ZFS packages are available in the default repositories:
sudo apt install -y zfsutils-linux
On Rocky Linux 10 / RHEL, install from the ZFS repository:
sudo dnf install -y https://zfsonlinux.org/epel/zfs-release-2-5$(rpm --eval "%{dist}").noarch.rpm
sudo dnf install -y zfs
sudo modprobe zfs
Verify the module is loaded:
lsmod | grep zfs
Create a ZFS Pool
A mirrored pool (equivalent to RAID 1) using two disks:
sudo zpool create datapool mirror /dev/sdb /dev/sdc
A RAIDZ1 pool (equivalent to RAID 5) with three disks:
sudo zpool create datapool raidz1 /dev/sdb /dev/sdc /dev/sdd
ZFS automatically mounts the pool at /datapool. No need for mkfs, no /etc/fstab entry. ZFS manages its own mounts.
Check pool status:
sudo zpool status
The output shows the pool layout, disk health, and any errors:
pool: datapool
state: ONLINE
config:
NAME STATE READ WRITE CKSUM
datapool ONLINE 0 0 0
mirror-0 ONLINE 0 0 0
sdb ONLINE 0 0 0
sdc ONLINE 0 0 0
errors: No known data errors
Datasets and Compression
Datasets are like subdirectories with their own properties (compression, quota, reservation). Create a dataset with LZ4 compression enabled:
sudo zfs create -o compression=lz4 datapool/documents
List all datasets and their properties:
sudo zfs list
Typical output on a fresh pool:
NAME USED AVAIL REFER MOUNTPOINT
datapool 156K 960M 24K /datapool
datapool/documents 24K 960M 24K /datapool/documents
LZ4 compression is nearly free in terms of CPU and often improves performance because less data hits the disk. There's almost no reason to leave it off. For higher compression ratios (at the cost of more CPU), use compression=zstd.
ZFS Snapshots
ZFS snapshots are instant and consume zero space at creation because they're just a pointer to the current state of the data. Space is only used as the original data changes (copy-on-write). This makes ZFS snapshots fundamentally different from LVM snapshots, which require pre-allocated COW space. For a practical comparison of snapshot workflows, see how Proxmox handles VM snapshots (which can use either LVM or ZFS on the backend).
Create a snapshot:
sudo zfs snapshot datapool/documents@2026-03-25
List snapshots:
sudo zfs list -t snapshot
Shows each snapshot with its space usage:
NAME USED AVAIL REFER MOUNTPOINT
datapool/documents@2026-03-25 0B - 24K -
Roll back to a snapshot (destroys all changes made after the snapshot):
sudo zfs rollback datapool/documents@2026-03-25
ZFS Send/Receive
One of ZFS's strongest features for backups. You can send a snapshot as a data stream to another pool, another machine, or a file. Proxmox Backup Server uses this capability heavily for efficient VM backups on ZFS storage.
Send a snapshot to a remote server over SSH:
sudo zfs send datapool/documents@2026-03-25 | ssh backupserver sudo zfs receive backuppool/documents
Incremental sends transfer only the changes between two snapshots, making subsequent backups extremely fast:
sudo zfs send -i datapool/documents@2026-03-25 datapool/documents@2026-03-26 | ssh backupserver sudo zfs receive backuppool/documents
Combining Them
In practice, you'll often use more than one of these technologies together. The right combination depends on what you need from your storage stack.
mdadm RAID + LVM (The Classic Stack)
This is the most common combination in enterprise Linux. mdadm handles redundancy at the disk level, and LVM provides flexible volume management on top. The typical layout:
Physical disks → mdadm RAID array (/dev/md0) → LVM PV → VG → LVs → ext4/xfs
Set it up by creating the RAID array first, then treating it as an LVM physical volume:
sudo mdadm --create /dev/md0 --level=10 --raid-devices=4 /dev/sdb /dev/sdc /dev/sdd /dev/sde
sudo pvcreate /dev/md0
sudo vgcreate secure_vg /dev/md0
sudo lvcreate -L 100G -n db_lv secure_vg
sudo mkfs.xfs /dev/secure_vg/db_lv
This gives you RAID 10 redundancy with the flexibility to carve the array into multiple logical volumes. You get online resizing from LVM and disk failure protection from mdadm. The downside is that you're managing two separate layers, and you don't get checksumming, compression, or snapshot efficiency.
ZFS Replaces Both
ZFS is a complete replacement for the mdadm + LVM + filesystem stack. A single ZFS pool with mirror or raidz vdevs provides redundancy, volume management (via datasets), compression, checksumming, and snapshots. You don't need mdadm or LVM at all.
The equivalent of the RAID 10 + LVM setup above in ZFS:
sudo zpool create secure_pool mirror /dev/sdb /dev/sdc mirror /dev/sdd /dev/sde
sudo zfs create -o compression=lz4 secure_pool/database
sudo zfs create -o compression=lz4 secure_pool/logs
Two mirror vdevs in a pool give you the same protection as RAID 10. Each dataset acts like a separate logical volume, but with shared space (no need to pre-allocate sizes unless you set quotas).
When Each Combination Makes Sense
| Combination | When to Use |
|---|---|
| mdadm only | Simple redundancy on a small server. You need mirrored boot drives or a basic redundant data volume |
| LVM only | Cloud VMs where the hypervisor handles redundancy. You just need flexible partitioning |
| mdadm + LVM | Enterprise Linux servers where you want both redundancy and flexible volumes. The standard RHEL/Rocky pattern |
| ZFS alone | NAS, backup servers, file servers, database servers where you want an integrated stack with data integrity |
| LVM + ZFS | Almost never. Don't layer LVM on top of ZFS or vice versa. They solve the same problems and adding both creates unnecessary complexity |
Which to Choose
The right answer depends on your workload, your distro, and how much complexity you're willing to manage. Here's a decision matrix based on real-world use cases.
By Workload
| Workload | Recommended Stack | Why |
|---|---|---|
| Database server (PostgreSQL, MySQL) | ZFS or LVM + mdadm RAID 10 | ZFS gives checksumming and snapshots for consistent backups. RAID 10 + LVM is the traditional approach with proven performance |
| File server / NAS | ZFS | Compression saves disk, checksumming catches corruption, snapshots enable versioning, send/receive handles offsite backups |
| Simple redundancy (two-disk mirror) | mdadm RAID 1 | Minimal complexity, works everywhere, no extra software |
| Cloud VM (AWS, OpenStack, Proxmox) | LVM | The hypervisor handles disk redundancy. LVM gives you flexible volumes. Most cloud images ship with LVM by default |
| Backup server | ZFS | Send/receive for efficient replication, compression reduces storage costs, snapshots provide retention policies |
| Desktop / workstation | LVM or plain partitions | Snapshots are useful for OS upgrades. RAID is overkill for most desktops |
| High-IOPS application | mdadm RAID 10 + LVM | RAID 10 has the best random I/O. ZFS COW overhead can hurt write-heavy workloads without tuning |
| Archival / cold storage | ZFS with RAIDZ2 | Checksumming catches bit rot on rarely-accessed data. ZSTD compression maximizes capacity |
Practical Considerations
Stick with LVM + mdadm if: you run RHEL, Rocky, or AlmaLinux in production, your team knows traditional Linux storage well, and you don't need checksumming or built-in compression. This stack is battle-tested, fully supported, and every sysadmin knows how to troubleshoot it.
Choose ZFS if: data integrity is non-negotiable, you have enough RAM (8 GB minimum for small pools, more for larger ones), and you're comfortable managing a kernel module that ships outside the mainline kernel. On FreeBSD, ZFS is the obvious default with no such caveats.
Avoid ZFS if: your server has less than 4 GB RAM, you're running a distro that doesn't package ZFS well, or your organization requires only in-tree kernel modules for support contracts.
Quick Reference
| Task | mdadm | LVM | ZFS |
|---|---|---|---|
| Create storage | mdadm --create | pvcreate + vgcreate + lvcreate | zpool create |
| List volumes | cat /proc/mdstat | pvs, vgs, lvs | zpool list, zfs list |
| Check status | mdadm --detail /dev/mdX | lvs -a | zpool status |
| Add disk | mdadm --add | pvcreate + vgextend | zpool add |
| Extend volume | mdadm --grow | lvextend + xfs_growfs | Automatic (datasets share pool) |
| Create snapshot | N/A | lvcreate -s | zfs snapshot |
| Replace failed disk | mdadm --remove + --add | pvmove | zpool replace |
| Enable compression | N/A | N/A | zfs set compression=lz4 |
| Backup/replicate | N/A | N/A | zfs send / zfs receive |
| Destroy | mdadm --stop + --remove | lvremove + vgremove + pvremove | zpool destroy |