Software RAID HOWTO: mdadm, LVM RAID, ZFS and Btrfs on Linux

Updated November 2026

Linux software RAID combines several disks so that one (or two) can fail without losing data. The standard tool is mdadm, which manages the kernel's md driver. This page builds a mirror with mdadm, shows how to monitor it and replace a dead disk, and then covers the alternatives built into LVM, ZFS and Btrfs.

RAID is not a backup. It protects against a disk dying. It does not protect against rm, ransomware, a bad controller, fire or theft. Keep backups as well.

Choose a RAID level

Common levels with four 4 TB disks (two for RAID 1)
LevelUsable spaceDisks that may failGood for
RAID 1 (mirror), 2 disks4 TB1boot and system disks, small servers
RAID 5, 4 disks12 TB1bulk storage with small disks
RAID 6, 4 disks8 TB2large disks, archives
RAID 10, 4 disks8 TB1 per mirror pairdatabases, VMs (fast writes)

The arithmetic: RAID 5 loses one disk to parity, so 4 × 4 TB − 4 TB = 12 TB. RAID 6 loses two: 16 − 8 = 8 TB. RAID 10 mirrors pairs, so half the space: 8 TB. With today's large disks, a RAID 5 rebuild reads every other disk completely and can take a day or more; a second failure in that time loses the array. That is why RAID 6 or RAID 10 is the usual choice for disks of several terabytes.

Create a RAID 1 array with mdadm

Install mdadm (sudo apt install mdadm or sudo dnf install mdadm). As of November 2026 the current release is mdadm 4.6. The examples use /dev/sdb and /dev/sdc as the two new, empty disks; yours may have other names.

The next commands destroy everything on the disks you name. Confirm each device with lsblk (size, model and serial number) right before you run wipefs, sgdisk or mdadm --create, and make sure none of them holds your running system.

lsblk -o NAME,SIZE,MODEL,SERIAL
sudo wipefs -a /dev/sdb?* /dev/sdc?*        # old partitions first (old RAID, LVM, file system signatures)
sudo wipefs -a /dev/sdb /dev/sdc            # then the partition tables

Wipe the old partitions before the whole disk. If you only wipe the partition table, an old md superblock inside a former RAID partition survives, and it reappears when you create a new partition at the same place. For a partition you know was an md member, sudo mdadm --zero-superblock /dev/sdb1 does the same job. (If a disk has no partitions, the first wipefs line reports that no file matches; that is harmless.)

Using one partition per disk, slightly smaller than the disk, makes it easier to use a replacement disk that is a few megabytes smaller later. The type code FD00 marks it as Linux RAID:

sudo sgdisk -n 1:0:-100M -t 1:FD00 /dev/sdb
sudo sgdisk -n 1:0:-100M -t 1:FD00 /dev/sdc
sudo mdadm --create /dev/md0 --level=1 --raid-devices=2 --bitmap=internal /dev/sdb1 /dev/sdc1

The default metadata format is 1.2. The write-intent bitmap (--bitmap=internal) records which areas were being written, so after a crash or a briefly missing disk only those areas are resynced instead of the whole disk. For RAID 6 over four disks, use --level=6 --raid-devices=4 and list four partitions.

Watch it build: /proc/mdstat

The new array is usable at once, while the kernel copies data in the background:

cat /proc/mdstat
sudo mdadm --detail /dev/md0

Example output during the first sync:

Personalities : [raid1]
md0 : active raid1 sdc1[1] sdb1[0]
      3906763776 blocks super 1.2 [2/2] [UU]
      [==>..................]  resync = 12.4% (484438528/3906763776) finish=301.2min speed=189340K/sec
      bitmap: 26/30 pages [104KB], 65536KB chunk

[2/2] [UU] means two of two disks are up. A failed or missing disk shows as _, for example [2/1] [U_]. That is a degraded array: it still works, but one more failure loses data.

File system, fstab and mdadm.conf

sudo mkfs.ext4 -L data /dev/md0
sudo mkdir -p /srv/data
echo "UUID=$(sudo blkid -s UUID -o value /dev/md0) /srv/data ext4 defaults 0 2" | sudo tee -a /etc/fstab
sudo mount -a

Record the array in mdadm's configuration file so it is assembled with the same name at boot, then rebuild the initramfs, which assembles arrays early in boot:

# Debian and Ubuntu
sudo mdadm --detail --scan | sudo tee -a /etc/mdadm/mdadm.conf
sudo update-initramfs -u

# Fedora, RHEL, AlmaLinux, Rocky Linux
sudo mdadm --detail --scan | sudo tee -a /etc/mdadm.conf
sudo dracut -f

# Arch Linux
sudo mdadm --detail --scan | sudo tee -a /etc/mdadm.conf
sudo mkinitcpio -P

The added line looks like ARRAY /dev/md0 metadata=1.2 UUID=…. Check the file afterwards for duplicate lines from earlier attempts.

Monitoring: know when a disk fails

A degraded array that nobody notices is just one disk away from data loss. Tell mdadm where to send alerts by adding to mdadm.conf:

MAILADDR admin@example.com
# or run your own script for each event:
# PROGRAM /usr/local/sbin/raid-alert

The monitor runs as mdmonitor.service, the unit shipped by upstream mdadm; check it with systemctl status mdmonitor. Mail needs a working local mail command, such as a small relay like msmtp or a full MTA. Test the whole chain:

sudo systemctl enable --now mdmonitor.service
sudo mdadm --monitor --scan --oneshot --test     # sends a TestMessage per array

Scrub regularly

Disks can develop unreadable sectors in data that is rarely read. A "check" reads every block and fixes bad sectors from the other copy before you need it. Upstream mdadm ships mdcheck_start.timer, which runs on the first Sunday of the month; distributions use it or their own monthly job. List what is scheduled with systemctl list-timers | grep -i md. To run a check by hand:

echo check | sudo tee /sys/block/md0/md/sync_action
cat /proc/mdstat                          # shows "check = x%"
cat /sys/block/md0/md/mismatch_cnt        # after it finishes

A mismatch_cnt of 0 is normal. On RAID 1 and RAID 10, a non-zero value does not always mean damage; the most common harmless cause is swap stored on the array. On RAID 5 and RAID 6, any mismatch points to a hardware problem. The count is in sectors and rounded up to md's I/O unit, so 128 can mean a single bad 64 KB block.

Also watch the disks themselves with smartmontools (sudo smartctl -a /dev/sdb) and replace disks whose reallocated or pending sector counts keep rising.

Replace a failed disk

Find which member failed and note its serial number, so you pull the right disk:

sudo mdadm --detail /dev/md0             # failed member shows as "faulty"
lsblk -o NAME,SERIAL

Mark it failed (if the kernel has not already), remove it from the array, and swap the hardware:

sudo mdadm /dev/md0 --fail /dev/sdc1 --remove /dev/sdc1
# shut down if your disks are not hot-swappable, replace the disk, boot

Copy the partition table from the good disk to the new one (assume it appears as /dev/sdd), give it fresh unique IDs, and add it:

Check the direction. The disk after --replicate= is the one that gets overwritten. Swapping the two names destroys the partition table of your last good disk. Confirm the new disk's name with lsblk -o NAME,SIZE,SERIAL first.

sudo sgdisk --replicate=/dev/sdd /dev/sdb     # copy sdb's table TO sdd
sudo sgdisk --randomize-guids /dev/sdd
sudo mdadm /dev/md0 --add /dev/sdd1
watch cat /proc/mdstat                         # "recovery = x%"

If a disk is still working but getting worse, add the new disk as a spare first and use sudo mdadm /dev/md0 --replace /dev/sdc1 --with /dev/sdd1. The array then stays fully redundant during the copy.

If the array holds /boot or the system, also install the boot loader on the new disk. On UEFI systems the EFI system partition cannot simply be an md member; keep one ESP per disk and copy it, or let your distribution's installer handle a RAID install.

LVM RAID

If you already use LVM, it can mirror or stripe individual logical volumes using the same md code inside the kernel. This creates a 500 GB mirrored volume in volume group vg0:

sudo lvcreate --type raid1 --mirrors 1 -L 500G -n data vg0
sudo lvs -a -o name,segtype,sync_percent,devices vg0
sudo lvchange --syncaction check vg0/data      # scrub
sudo lvconvert --repair vg0/data               # after a PV failed; uses free space in the VG

The advantage is flexibility: you can mirror only the volumes that matter and convert levels later. See man lvmraid.

ZFS

ZFS combines RAID, volume management and a file system with checksums on every block, so a scrub can tell which copy is correct. It is not part of the mainline Linux kernel, so it is built as a separate module. Debian ships it in contrib (sudo apt install linux-headers-$(dpkg --print-architecture) zfs-dkms zfsutils-linux), Ubuntu packages it as zfsutils-linux, and OpenZFS provides repositories for RHEL-based systems. As of November 2026 the current OpenZFS series is 2.4.

sudo zpool create tank mirror /dev/disk/by-id/ata-DISK1 /dev/disk/by-id/ata-DISK2
# or two-disk-redundant: sudo zpool create tank raidz2 disk1 disk2 disk3 disk4
sudo zpool status
sudo zpool scrub tank
sudo zpool replace tank /dev/disk/by-id/ata-OLD /dev/disk/by-id/ata-NEW

Use /dev/disk/by-id/ names, which do not change when disks are moved between ports.

Btrfs RAID

Btrfs has its own multi-disk profiles. RAID 1, RAID 1C3/1C4 (three or four copies) and RAID 10 are mature. RAID 5 and 6 are still marked unstable in the Btrfs documentation and should not hold data you care about.

sudo mkfs.btrfs -m raid1 -d raid1 /dev/sdb /dev/sdc
sudo mount /dev/sdb /srv/data
sudo btrfs filesystem usage /srv/data
sudo btrfs device stats /srv/data         # per-disk error counters
sudo btrfs scrub start /srv/data
sudo btrfs replace start /dev/sdc /dev/sdd /srv/data

Common mistakes

The classic 2000 Software-RAID HOWTO, written for the old raidtools, is kept by the LDP at tldp.org. The Linux RAID wiki that replaced it is now archived, and the kernel's md documentation is the current reference.

Sources