Configure Software RAID 10 (1+0) in Linux with mdadm

Deepak Prasad
Tested on CentOS 7
Package mdadm (1.2 array metadata)
e2fsprogs (mkfs.ext4)
Applies to RHEL, Rocky Linux, AlmaLinux, Oracle Linux, CentOS Stream, Fedora, Debian, Ubuntu, and other Linux distributions with mdadm
Privilege sudo or root for mdadm, mkfs, and mount
Scope Nested software RAID 10 (RAID 1+0) with mdadm on four partitions — create mirrors, stripe them, format, and mount. Brief note on native mdadm --level=10. Does not cover hardware RAID, LVM-on-RAID, or replacing failed members in production.
Related guides mdadm command cheat sheet
Configure software RAID 0
Configure software RAID 1
parted command
mke2fs command

Software RAID 10 in Linux pairs mirroring with striping. This guide builds a nested RAID 1+0 stack with mdadm: a RAID 0 stripe across two RAID 1 mirrors (/dev/md0 and /dev/md1 form the mirrors; /dev/md2 is the top-level stripe). That is a different Linux MD implementation from native mdadm --level=10, which creates one RAID 10 device with near, far, or offset layout options. Four disks give the traditional two-way mirrored-and-striped layout, but MD RAID 10 also supports other device counts and layouts.

IMPORTANT
This guide uses nested RAID 1+0 on four partitions (sdb1 through sde1). For new deployments, also evaluate native mdadm --create --level=10 --raid-devices=4 on a four-disk layout like this lab — see the mdadm command cheat sheet. The nested method here matches how the layers appear in /proc/mdstat.

Quick reference: software RAID 10 with mdadm

Step Command
Confirm member partitions lsblk
Create first mirror (RAID 1) mdadm -C -n2 -l1 /dev/md0 /dev/sd{b,c}1
Create second mirror (RAID 1) mdadm -C -n2 -l1 /dev/md1 /dev/sd{d,e}1
Stripe mirrors (RAID 0) mdadm -C -n2 -l0 -c64 /dev/md2 /dev/md{0,1}
Check array state cat /proc/mdstat
Create ext4 filesystem mkfs.ext4 /dev/md2
Mount the volume mount /dev/md2 /hybrid_array
Inspect top-level array mdadm --detail /dev/md2

Prepare four unused partitions or whole disks of similar usable size before running mdadm. A legacy MBR 0xFD partition type is not required for modern mdadm 1.x metadata; current systems normally identify and assemble arrays from MD superblocks. On GPT you may optionally mark partitions with the Linux RAID partition type for clarity, but mdadm does not depend on that flag to create a metadata-1.x array. Use parted or fdisk to partition the disks, then follow the steps below.


What is RAID 10 (1+0)?

RAID 10 — also written RAID 1+0 — mirrors data within each pair, then stripes across those mirrors. In this article that means a RAID 0 stripe across RAID 1 mirrors (nested RAID 1+0), not a single native MD RAID 10 personality.

Nested RAID 10 topology with RAID 0 stripe /dev/md2 over two RAID 1 mirrors /dev/md0 and /dev/md1

Read the figure top down: /dev/md2 is the RAID 0 stripe you format and mount; /dev/md0 and /dev/md1 are the underlying mirrors built from sdb1/sdc1 and sdd1/sde1. Writes are striped across the mirrors, and each mirror keeps a redundant copy on its partner disk.

Compared with RAID 0+1 (stripe first, mirror second), RAID 1+0 survives more realistic failures because a dead disk in one mirror does not take down the entire stripe partner. Usable capacity is half the raw disk total — four 2 GB partitions yield roughly 4 GB on /dev/md2.


Prerequisites: four member devices

A nested RAID 10 array needs at least four unused disks or partitions of the same or closely matched usable size. Larger members do not increase mirror capacity beyond the smaller partner. In the lab, sdb1, sdc1, sdd1, and sde1 were prepared on four data disks before mdadm ran.

Confirm the disks and partitions are visible:

bash
lsblk
output
[root@node1 ~]# lsblk
NAME            MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
sda               8:0    0   30G  0 disk
├─sda1            8:1    0  512M  0 part /boot
└─sda2            8:2    0 27.5G  0 part
  ├─centos-root 253:0    0 25.5G  0 lvm  /
  └─centos-swap 253:1    0    2G  0 lvm  [SWAP]
sdb               8:16   0    2G  0 disk
└─sdb1            8:17   0    2G  0 part
sdc               8:32   0    2G  0 disk
└─sdc1            8:33   0    2G  0 part
sdd               8:48   0    2G  0 disk
└─sdd1            8:49   0    2G  0 part
sde               8:64   0    2G  0 disk
└─sde1            8:65   0    2G  0 part
sr0              11:0    1 1024M  0 rom

The four data disks (sdb through sde) each expose one partition ready for RAID membership.


Step 1: Create the first RAID 1 mirror

Build /dev/md0 from sdb1 and sdc1:

bash
mdadm -C -n2 -l1 /dev/md0 /dev/sd{b,c}1
output
[root@node1 ~]# mdadm -C -n2 -l1 /dev/md0 /dev/sd{b,c}1
mdadm: Defaulting to version 1.2 metadata
mdadm: array /dev/md0 started.

Step 2: Create the second RAID 1 mirror

Build /dev/md1 from sdd1 and sde1:

bash
mdadm -C -n2 -l1 /dev/md1 /dev/sd{d,e}1
output
[root@node1 ~]# mdadm -C -n2 -l1 /dev/md1 /dev/sd{d,e}1
mdadm: Defaulting to version 1.2 metadata
mdadm: array /dev/md1 started.

The flags used above:

text
-C, --create     Create a new array.
-l, --level=     RAID level (1 = mirror, 0 = stripe, 10 = native RAID 10).
-n, --raid-devices=   Number of active devices in the array.
-c, --chunk=     Chunk size in kilobytes (used on the RAID 0 step below).

On the stripe step, the examples use -c64 for a 64 KiB chunk size. That is not a universal best value. Choose chunk size based on workload and filesystem alignment, or allow current mdadm defaults when you have no measured reason to override it.


Step 3: Verify the RAID 1 mirrors

After each mirror is created, mdadm starts a resync. Check progress with /proc/mdstat:

bash
cat /proc/mdstat
output
[root@node1 ~]# cat /proc/mdstat
Personalities : [raid1]
md1 : active raid1 sde1[1] sdd1[0]
      2094080 blocks super 1.2 [2/2] [UU]
      [=========>...........]  resync = 48.3% (1012736/2094080) finish=0.0min speed=202547K/sec

md0 : active raid1 sdc1[1] sdb1[0]
      2094080 blocks super 1.2 [2/2] [UU]

unused devices: <none>

[2/2] [UU] means both expected RAID 1 members are present and active. In the sample above, md1 is still synchronizing because the resync = 48.3% line is present. Wait until the resync line disappears, or confirm a clean state with mdadm --detail, if you specifically need to verify synchronization has completed.


Step 4: Stripe the mirrors with RAID 0

Create /dev/md2 as a RAID 0 stripe across /dev/md0 and /dev/md1. This is the top-level hybrid RAID 10 volume:

bash
mdadm -C -n2 -l0 -c64 /dev/md2 /dev/md{0,1}
output
[root@node1 ~]# mdadm -C -n2 -l0 -c64 /dev/md2 /dev/md{0,1}
mdadm: /dev/md1 appears to contain an ext2fs file system
       size=2094080K  mtime=Mon Jun 10 16:31:37 2019
Continue creating array? y
mdadm: Defaulting to version 1.2 metadata
mdadm: array /dev/md2 started.

If mdadm reports an existing filesystem or RAID signature, stop and confirm the device contains no data you need. The prompt above appeared because /dev/md1 had been used before. Do not answer y blindly on production hardware.

Inspect signatures with wipefs and remove stale ones only after verifying the device is safe to erase:

bash
wipefs /dev/md1

wipefs -a removes detectable filesystem and RAID signatures and is destructive — use it only when you are certain the device can be wiped.


Step 5: Verify the RAID 10 stack

The finished stack should show two RAID 1 mirrors and one RAID 0 stripe on top:

bash
cat /proc/mdstat
output
[root@node1 ~]# cat /proc/mdstat
Personalities : [raid1] [raid0]
md2 : active raid0 md1[1] md0[0]
      4184064 blocks super 1.2 64k chunks

md1 : active raid1 sde1[1] sdd1[0]
      2094080 blocks super 1.2 [2/2] [UU]

md0 : active raid1 sdc1[1] sdb1[0]
      2094080 blocks super 1.2 [2/2] [UU]

unused devices: <none>

/dev/md2 has roughly 4 GiB of usable capacity, combining the two ~2 GiB RAID 1 mirrors into the top-level RAID 0 stripe.

Inspect the top-level device with mdadm --detail:

bash
mdadm --detail /dev/md2
output
[root@node1 ~]# mdadm --detail /dev/md2
/dev/md2:
           Version : 1.2
     Creation Time : Fri Jun 14 14:07:15 2019
        Raid Level : raid0
        Array Size : 4184064 (3.99 GiB 4.28 GB)
      Raid Devices : 2
     Total Devices : 2
       Persistence : Superblock is persistent

       Update Time : Fri Jun 14 14:07:15 2019
             State : clean
    Active Devices : 2
   Working Devices : 2
    Failed Devices : 0
     Spare Devices : 0

        Chunk Size : 64K

Consistency Policy : none

              Name : node1.golinuxcloud.com:2  (local to host node1.golinuxcloud.com)
              UUID : c2ba009d:d077b4b5:eb28f342:e91ea39e
            Events : 0

    Number   Major   Minor   RaidDevice State
       0       9        0        0      active sync   /dev/md0
       1       9        1        1      active sync   /dev/md1

Raid Level : raid0 on /dev/md2 is expected — the top device is the stripe layer; the mirrors underneath remain RAID 1.


Step 6: Create a filesystem and mount point

Format the stripe device with ext4 — see mke2fs command for other filesystem options:

bash
mkfs.ext4 /dev/md2
output
[root@node1 ~]# mkfs.ext4 /dev/md2
mke2fs 1.42.9 (28-Dec-2013)
Filesystem label=
OS type: Linux
Block size=4096 (log=2)
Fragment size=4096 (log=2)
Stride=16 blocks, Stripe width=32 blocks
261632 inodes, 1046016 blocks
52300 blocks (5.00%) reserved for the super user
First data block=0
Maximum filesystem blocks=1071644672
32 block groups
32768 blocks per group, 32768 fragments per group
8176 inodes per group
Superblock backups stored on blocks:
        32768, 98304, 163840, 229376, 294912, 819200, 884736

Allocating group tables: done
Writing inode tables: done
Creating journal (16384 blocks): done
Writing superblocks and filesystem accounting information: done

Create a mount point for the new volume:

bash
mkdir /hybrid_array

Mount /dev/md2 on the new directory:

bash
mount /dev/md2 /hybrid_array

Confirm the filesystem size and free space:

bash
df -h /hybrid_array
output
[root@node1 ~]# df -h /hybrid_array
Filesystem      Size  Used Avail Use% Mounted on
/dev/md2        3.9G   16M  3.7G   1% /hybrid_array
NOTE
A manual mount is temporary. Add the filesystem to /etc/fstab using its UUID, and ensure the MD array definitions are present in your distribution's mdadm configuration or initramfs so the nested arrays assemble before the mount is attempted. mdadm --detail --scan can generate prototype ARRAY entries for currently active arrays; review them before adding them to the appropriate mdadm.conf and rebuilding the initramfs where your distribution requires it — Debian and Ubuntu commonly use /etc/mdadm/mdadm.conf; RHEL-family systems integrate through initramfs and distro-specific mdadm config paths. See create filesystem and partition in Linux for fstab patterns.

Failure tolerance and controller layout

RAID 10 can survive a disk failure on each mirror as long as the failed disks are not partners in the same RAID 1 pair. If both disks on one mirror die, that half of the stripe is lost even when the other mirror is healthy.

Controller placement matters for nested arrays. A bad layout puts both disks of one mirror on the same I/O channel:

text
Controller A: sdb, sdd   →  Mirror 1 (both on one channel)
Controller B: sdc, sde   →  Mirror 2 (both on one channel)

A single controller failure degrades an entire mirror. A safer layout spreads mirror partners across channels using the same four data disks from this lab:

text
Controller A: sdb, sdd
Controller B: sdc, sde

Mirror 1: sdb + sdc
Mirror 2: sdd + sde

One controller failure then leaves each RAID 1 mirror degraded but still active, so both /dev/md0 and /dev/md1 remain available and the top-level /dev/md2 stripe can continue operating. The kernel MD layer supports degraded RAID 1 operation with one surviving member.

A nested design can also stripe more than two RAID 1 mirrors, and individual mirrors can include spare members. For new deployments beyond this four-disk lab, evaluate native mdadm --level=10, which represents the array as one MD RAID 10 device and supports RAID 10-specific layouts at various device counts — see the mdadm command cheat sheet.


References

Summary

Software RAID 10 in Linux combines mirroring and striping for workloads that need both redundancy and I/O throughput. This guide built a nested RAID 1+0 stack: mdadm created two RAID 1 mirrors on four partitions, striped them into /dev/md2, formatted the result with ext4, and mounted it at /hybrid_array.

The /proc/mdstat output shows each layer: two mirrors under an active raid0 stripe. That nested view differs from native mdadm --level=10, which creates one MD RAID 10 device with its own layout algorithms. Both provide mirrored-and-striped redundancy, but they are different Linux MD implementations; four disks give the traditional layout, while native MD RAID 10 also supports other device counts.

Before creating arrays on production hardware, use four unused disks or partitions of closely matched size, spread mirror partners across controllers when possible, and add the filesystem to /etc/fstab by UUID while ensuring mdadm configuration or initramfs assembly covers all three MD devices. For day-to-day mdadm operations beyond this layout, use the mdadm command cheat sheet.

Omer Cakmak

Linux Administrator

Highly skilled at managing Debian, Ubuntu, CentOS, Oracle Linux, and Red Hat servers. Proficient in bash scripting, Ansible, and AWX central server management, he handles server operations on OpenStack, KVM, Proxmox, and VMware.

  • Debian
  • Ubuntu
  • Linux
  • Red Hat Enterprise Linux
  • Shell Script
  • System Administration