LPIC-2 Advanced Storage: Software RAID (mdadm), iSCSI and NBD
- Assemble a software RAID array with mdadm and read its health and rebuild progress from /proc/mdstat
- Persist an array so it reassembles automatically after a reboot
- Choose a RAID level by reasoning about its speed, capacity, and how many disks it can lose
- Export a LUN from an iSCSI target with targetcli (backstore, IQN, LUN, ACL)
- Attach a remote LUN as a local block device with the iscsiadm initiator and mount it safely
- Attach a network block device (NBD) export with nbd-client and persist it with /etc/nbdtab and a _netdev fstab entry
Why combine disks at all
Frame RAID as turning several fragile disks into one device that is faster, bigger, or survivable.
A single disk is a single point of failure. When it dies, everything on it dies with it — and meanwhile, one disk can only read and write so fast. RAID (Redundant Array of Independent Disks) glues several physical disks together so the operating system sees one block device that is faster, larger, more resilient, or some blend of the three.
Think of it like a delivery company. One van (one disk) is slow and, if it breaks down, the parcel is stuck. Run several vans and you can either split each order across them to deliver faster (striping), send a duplicate van behind every order so a breakdown loses nothing (mirroring), or keep a spare van loaded with enough information to reconstruct any one lost load (parity). RAID levels are just named recipes for which of those tricks you use.
On Linux, you build these arrays in software with `mdadm` ("multiple devices admin"). The kernel exposes the result as a `/dev/mdN` device you partition, format, and mount like any disk.
Build an array with mdadm
Create a RAID device and understand the metadata mdadm writes onto each member.
mdadm --createtakes a list of member block devices and bonds them into a new/dev/mdN. You tell it the level (the recipe) and how many devices participate. Here we build a RAID 5 across three disks.Example — not runnable on your lab box
sudo mdadm --create /dev/md0 --level=5 --raid-devices=3 /dev/sdb /dev/sdc /dev/sddmdadm: Defaulting to version 1.2 metadata mdadm: array /dev/md0 started.
- --create
- Make a brand-new array at the named device (/dev/md0).
- --level=5
- The RAID recipe: 5 = striping with one disk's worth of distributed parity.
- --raid-devices=3
- How many active members make up the array — must match the device list that follows.
array /dev/md0 startedmeans the kernel has assembled the device and it is usable immediately — even though, for a parity level, it begins syncing in the background (next step). Theversion 1.2 metadatanote matters: mdadm writes a superblock near the start of each member disk recording the array's UUID, level, and role. That superblock is what lets the array reassemble later without you retyping any of this.Check yourself
What does mdadm write onto each member disk so the array can be rebuilt on different hardware later?
Read array health from /proc/mdstat
Check status, member health, and live rebuild progress without guessing.
Once an array exists, `/proc/mdstat` is your single window into its health. The kernel keeps this file live: which disks are members, whether any are missing, and — crucially — how far along a rebuild or initial sync has progressed. After creating a parity array, or after replacing a failed disk, watch this file until it reports clean.
Example — not runnable on your lab box
cat /proc/mdstatPersonalities : [raid6] [raid5] [raid4] md0 : active raid5 sdd[3] sdc[1] sdb[0] 209584128 blocks super 1.2 level 5, 512k chunk, algorithm 2 [3/3] [UUU] [=========>...........] recovery = 47.3% (49574912/104792064) finish=4.2min speed=216000K/sec unused devices: <none>Read it line by line. `md0 : active raid5` names the array and level. `[3/3]` means 3 of 3 expected members are present; `[UUU]` shows each member is Up (a _would mark a missing or failed disk —[UU_]means one is gone and you are running degraded). The `recovery = 47.3% ... finish=4.2min` line is the live rebuild progress bar — the array is usable now but not yet redundant, and you should wait for it to finish before trusting it to survive a failure.Example — not runnable on your lab box
sudo mdadm --detail /dev/md0/dev/md0: Version : 1.2 Raid Level : raid5 Array Size : 209584128 (199.87 GiB) Raid Devices : 3 Total Devices : 3 State : clean Active Devices : 3 Working Devices : 3 Failed Devices : 0- --detail
- Print the full status report for one assembled array (state, sizes, per-device roles, array UUID).
Where /proc/mdstatis the quick glance,--detailis the full chart for a single array. `State : clean` with `Failed Devices : 0` is the healthy resting state. This is also where you read the array UUID that you will persist in the next step.Check yourself
In a /proc/mdstat line the array shows
[3/3] [UU_]. What is the state of the array?Persist the array so it survives a reboot
Record the array in mdadm.conf so the system reassembles it automatically at boot.
Here is the step learners forget — and the one labs love to grade. Creating an array assembles it for the running system only. At the next boot, the system scans for arrays to bring up, and it consults `/etc/mdadm.conf` (on some distros
/etc/mdadm/mdadm.conf) to know which arrays it should expect. If your array is not listed there, it may not assemble — or may come back under a different device name like/dev/md127, breaking any/etc/fstabentry that named/dev/md0.mdadm --detail --scanprints exactly theARRAYline the config file wants — UUID, level, and device count. You append it to the config so the description is on record.sudo mdadm --detail --scan | sudo tee -a /etc/mdadm.confARRAY /dev/md0 metadata=1.2 name=server:0 UUID=9f8a1c44:3e2b07d5:1a6f9e22:8c4d51ab
- --detail
- Report on assembled arrays.
- --scan
- Scan all active arrays and emit them in config-file (ARRAY-line) format, ready to save.
- -a (tee)
- Append to the file instead of overwriting it — never clobber an existing mdadm.conf.
The single ARRAYline printed (and appended) ties the array's UUID to its device name. At boot the system matches the on-disk superblock UUID against this line and assembles/dev/md0consistently every time. TheUUID=is the durable identifier — disk ordering or device-name changes won't fool it.Check yourself
Which command produces the exact ARRAY line you append to mdadm.conf so the array reassembles at boot?
Choose the right RAID level
Reason about speed, usable capacity, and fault tolerance to pick a level on purpose.
Picking a level is a trade between three things: speed, usable capacity, and how many disks you can lose without losing data. Here is the mental model for each common level:
- RAID 0 (stripe) — data split across all disks for maximum speed and full capacity, but zero redundancy. Lose any one disk and the whole array is gone. Pure performance, for data you can afford to lose.
- RAID 1 (mirror) — every disk holds an identical copy. Survives losing all but one disk, but you pay half (or more) your capacity for it. Simple and very safe.
- RAID 5 (single parity) — striping plus one disk's worth of parity spread across the set. Survives one disk failure; usable capacity is (N−1) disks. A common balance of capacity and safety, but a rebuild after a failure stresses the remaining disks.
- RAID 6 (double parity) — like 5 but with two parity blocks, so it survives two simultaneous failures; usable capacity is (N−2). The safer choice for large, slow disks where a rebuild takes so long a second failure is plausible.
- RAID 10 (stripe of mirrors) — mirror pairs, then stripe across them. Fast and redundant: survives at least one disk per mirror pair. The premium choice for databases — you pay half your capacity, like RAID 1.
The one-line rule of thumb: *0 = speed, 1 = safety, 5 = balance, 6 = balance with a wider safety margin, 10 = speed and safety (at a price).*
Check yourself
You need an array that keeps working even if two disks fail at the same time. Which RAID level meets that requirement?
Export a LUN: the iSCSI target with targetcli
Stand up the server side — turn a block device into a network-accessible LUN protected by an ACL.
RAID gives you bigger, safer storage on one machine. iSCSI lets a machine serve raw block storage over the network so another machine can use it as if it were a local disk. The serving side is the target; the consuming side is the initiator.
Think of it as renting a storage unit by mail. The target is the storage facility; a LUN (Logical Unit Number) is one specific unit it rents out; the IQN (iSCSI Qualified Name, like
iqn.2026-06.com.example:storage.disk0) is the globally unique address printed on the door; and the ACL is the access list at the front desk — only an initiator whose own IQN is on that list gets the key.You configure the target with `targetcli`, an interactive shell. The build order is always: backstore → target (IQN) → LUN → ACL.
Example — not runnable on your lab box
sudo targetclitargetcli shell version 2.1.fb49 Copyright 2011-2013 by Datera, Inc and others. /> /backstores/block create name=disk0 dev=/dev/md0 Created block storage object disk0 using /dev/md0. /> /iscsi create iqn.2026-06.com.example:storage.target0 Created target iqn.2026-06.com.example:storage.target0. Created TPG 1.
- /backstores/block create
- Register a real local block device (here our RAID array /dev/md0) as an exportable object.
- /iscsi create <iqn>
- Create the target and its IQN — the unique network address initiators will connect to.
Two foundations are laid. The backstore is the actual storage being shared — we point it at /dev/md0, so the RAID array becomes the thing served over the wire. The `/iscsi create` line mints the target IQN; that string is what an initiator must name to find this storage.TPG 1(Target Portal Group) is the default container the LUN and ACL go into next.Example — not runnable on your lab box
/iscsi/iqn.2026-06.com.example:storage.target0/tpg1/luns create /backstores/block/disk0Created LUN 0.
This maps the backstore into the target as LUN 0 — the actual unit number the initiator will see and attach. No LUN means the target answers but offers no storage. Example — not runnable on your lab box
/iscsi/iqn.2026-06.com.example:storage.target0/tpg1/acls create iqn.2026-06.com.example:client01Created Node ACL for iqn.2026-06.com.example:client01 Created mapped LUN 0.
The ACL authorizes exactly one initiator — the one whose IQN is iqn.2026-06.com.example:client01— to log in and see LUN 0. This IQN must exactly match the initiator's configured name (you'll see where that's set in the next step). Anyone whose IQN isn't on this list is refused at login. Finish withsaveconfigandexitin targetcli so the configuration persists, and make sure the target is reachable on its portal (here the documentation address 192.0.2.10, TCP 3260).Check yourself
In targetcli, which object decides which initiator IQN is allowed to connect to a target?
Attach it: the iSCSI initiator with iscsiadm
Discover, log in, and mount the remote LUN as a local block device — with the IQN matched correctly.
On the client (the initiator) you use `iscsiadm`. The workflow is two beats: discovery (ask the target portal what it offers) then login (actually attach a LUN). After login, the remote LUN appears in
lsblkas an ordinary new disk like/dev/sdX— at which point you partition, format, and mount it like any local device.First, the initiator's own identity. Its IQN lives in
/etc/iscsi/initiatorname.iscsi, and it must match the ACL you set on the target — otherwise login is refused.Example — not runnable on your lab box
cat /etc/iscsi/initiatorname.iscsiInitiatorName=iqn.2026-06.com.example:client01
This is the name the initiator presents at login. It reads `iqn.2026-06.com.example:client01` — identical to the ACL we created on the target. If you change this file, restart iscsidso the new name takes effect. A mismatch here is the number-one reason a login is rejected.Example — not runnable on your lab box
sudo iscsiadm -m discovery -t sendtargets -p 192.0.2.10192.0.2.10:3260,1 iqn.2026-06.com.example:storage.target0
- -m discovery
- Discovery mode — ask a portal which targets it exposes, without attaching anything yet.
- -t sendtargets
- The discovery method: ask the portal to send its list of target IQNs.
- -p 192.0.2.10
- The portal address (IP[:port]) of the target server to query.
Discovery returns the target IQN available at the portal 192.0.2.10:3260— proof the target is reachable and that you're naming it correctly. Nothing is attached yet; you've only learned what's on offer. Getting a result here but failing at login almost always means an ACL/IQN mismatch, not a network problem.Example — not runnable on your lab box
sudo iscsiadm -m node -T iqn.2026-06.com.example:storage.target0 -p 192.0.2.10 --loginLogging in to [iface: default, target: iqn.2026-06.com.example:storage.target0, portal: 192.0.2.10,3260] Login to [iface: default, target: iqn.2026-06.com.example:storage.target0, portal: 192.0.2.10,3260] successful.
- -m node
- Node mode — operate on a discovered target record.
- -T <iqn>
- Which target IQN to act on (from the discovery output).
- --login
- Attach the target's LUN(s) now, creating local block devices.
`Login ... successful` means the target accepted this initiator's IQN against its ACL and attached the LUN. Run lsblknow and a new disk (e.g./dev/sdb) appears — that's the remote LUN presented locally. If instead you see an authorization failure, the initiator IQN doesn't match the target ACL: fix one to equal the other.Now treat it like a disk — but mount it with one special option. Because the device only exists after the network is up and login succeeds, a normal
/etc/fstabline would make the system try to mount it too early at boot and hang. The `_netdev` option tells the system to wait for the network first.echo 'UUID=... /mnt/iscsi ext4 _netdev 0 0' | sudo tee -a /etc/fstabUUID=... /mnt/iscsi ext4 _netdev 0 0
- _netdev
- Marks the mount as requiring the network — the system defers it until networking (and iSCSI login) is available, and unmounts it before networking goes down.
The key token is `_netdev`. Without it, the boot sequence tries to mount the iSCSI filesystem before the initiator has logged in, the device isn't there yet, and the boot stalls in emergency mode — the network-storage equivalent of the missing- mdadm.conftrap from earlier. With it, the mount waits its turn. tee echoes what it appended (that's how the sudo-write idiom shows its work). The _netdev option is load-bearing: it holds the mount until the network is up, so an iSCSI mount never hangs the boot.Check yourself
Which fstab mount option must an iSCSI filesystem use so the system doesn't try to mount it before the network is up?
A lighter network disk: NBD with nbd-client
List a server's NBD exports, attach one by name to a free /dev/nbdN, and make it come back after a reboot with /etc/nbdtab and a _netdev fstab line.
iSCSI is not the only way to put a remote disk on a local device node. NBD (network block device) is a much smaller protocol built into the Linux kernel: a server (
nbd-server, TCP port 10809) offers one or more named exports, and the client (nbd-client) attaches one of them to a local `/dev/nbdN` device. From then on it is an ordinary block device:blkid,mount,mkfsandfsckall work on it. Every read and write travels over the network to the server.There are no IQNs or ACLs to match here. You need three things: the server's name, the export name, and a free
/dev/nbdNon the client. Thenbdkernel module creates/dev/nbd0upwards (modprobe nbdif they are missing).Example — not runnable on your lab box
nbd-client -l nas01Negotiation: .. scratch backups media
- -l <host>
- List the export names the server offers, without attaching anything.
Like iSCSI discovery: you learn what is on offer before you attach. Each line after Negotiationis one export name. Attaching the wrong one puts another team's disk on your box, so read the list against your ticket.Example — not runnable on your lab box
nbd-client -c /dev/nbd0; echo "exit: $?"exit: 1
- -c <device>
- Check a device: prints the PID of the client holding it and exits 0 if it is in use; exits 1 if it is free.
Exit 1 with no PID means /dev/nbd0is free. Exit 0 and a PID means another export is already attached there, so pick the next device. Attaching over a device that is in use fails, or worse, hides the disk that was there.Example — not runnable on your lab box
sudo nbd-client nas01 -N scratch /dev/nbd0 && sudo blkid /dev/nbd0Negotiation: ..size = 64MB Connected /dev/nbd0 /dev/nbd0: LABEL="scratch" UUID="0b6c…" BLOCK_SIZE="4096" TYPE="ext4"
- -N <export>
- Attach the export with this name. Without -N the client asks for the server's default export, which is usually not the one you want.
- /dev/nbd0
- The local device to attach it to; it must be free.
Connected /dev/nbd0means the export is attached.blkidshows the filesystem already on it, so you mount it as it is:mount /dev/nbd0 /mnt/scratch. Don't runmkfson a disk you were asked to attach. That erases data that lives on the server.nbd-client -d /dev/nbd0detaches it again (unmount first).Surviving a reboot takes two files.
/etc/nbdtabtells the system what to attach: one line per device,<device> <host> <export> [options], with the device written without/dev/. With that line in place,nbd-client nbd0attaches it by name, and on a systemd hostsystemctl enable nbd@nbd0does that at boot. Then/etc/fstabmounts the filesystem, with `_netdev` for the same reason as iSCSI: the device does not exist until the network is up and the client has connected.Example — not runnable on your lab box
echo 'nbd0 nas01 scratch' | sudo tee -a /etc/nbdtab echo "UUID=$(sudo blkid -o value -s UUID /dev/nbd0) /mnt/scratch ext4 defaults,_netdev 0 0" | sudo tee -a /etc/fstabnbd0 nas01 scratch UUID=0b6c… /mnt/scratch ext4 defaults,_netdev 0 0
- tee -a
- Append to the file instead of overwriting it, and echo what was written. Run through sudo, it can write a root-owned file that a plain
>>redirect can't. - blkid -o value -s UUID
- Print only the UUID value of the device's filesystem, ready to paste into fstab.
- _netdev
- Mount only once the network is up; unmount before it goes down.
The nbdtab line maps nbd0to exportscratchonnas01. The fstab line mounts it by filesystem UUID, which still matches if the export ends up on a different/dev/nbdNsomeday. To check both files without rebooting: unmount,nbd-client -d /dev/nbd0, thennbd-client nbd0andmount /mnt/scratch. If those two commands bring the disk back, boot will too.Check yourself
Which nbd-client option attaches an export by its name (rather than the server's default export)?
Try it in your live terminal
Inspect real storage state on the sandbox to your right — read-only, nothing is created or destroyed.
You have a real root shell. These commands are read-only — they report on whatever storage exists without building or breaking any array. Run them to make the concepts concrete; if there are no md arrays here, the first command says so plainly.
cat /proc/mdstat 2>/dev/null || echo 'no md arrays'The live health window for software RAID. If any mdNarrays exist you'll see their level, member health ([UUU]vs[UU_]), and any rebuild progress; otherwise you get a plainno md arrays.lsblkThe block-device tree: physical disks, partitions, any mdNarrays, and what's mounted where. This is how you'd confirm an attached iSCSI LUN shows up as a new disk after login.mdadm --version 2>/dev/null || echo 'mdadm not installed'Confirms the RAID toolset is present and prints its version — the tool you'd use to create, inspect ( --detail), and persist (--detail --scan) arrays.
mdadm --createbuilds the array, but persistence is a separate, mandatory step: appendmdadm --detail --scanto/etc/mdadm.conf(and rebuild the initramfs) or the array won't return cleanly after a reboot.- `/proc/mdstat` is the live health window —
[UUU]is healthy, a_means a degraded member, and the recovery line shows rebuild progress while the array runs unprotected. - Pick a RAID level by what you can afford to lose: 0 none, 1/5 one, 6 two, 10 one per mirror pair — capacity and safety always trade against each other.
- iSCSI splits into target (targetcli: backstore → IQN → LUN → ACL) and initiator (iscsiadm: discovery → login); the initiator IQN must match the target ACL or login is refused.
- Mount network block storage with `_netdev` so the system waits for the network instead of hanging at boot.
| Level | Disks it can lose | Usable capacity | Speed | Use it for |
|---|---|---|---|---|
| 0 (stripe) | 0 — any loss kills it | 100% (all disks) | Fastest | Scratch/temp data you can rebuild |
| 1 (mirror) | All but one | 50% (half) | Read-fast, write-normal | Boot/system disks; simple safety |
| 5 (single parity) | 1 | (N−1)/N | Good read, slower write | Balanced general-purpose storage |
| 6 (double parity) | 2 | (N−2)/N | Slower write than 5 | Large slow disks; long rebuilds |
| 10 (stripe+mirror) | 1 per mirror pair | 50% (half) | Fast read and write | Databases needing speed + safety |
