| Tested on | RHEL 10.2 (Coughlan) |
|---|---|
| Package | lvm2 2.03.36-2.el10.x86_64xfsprogs 6.16.0-1.el10.x86_64e2fsprogs 1.47.1-5.el10.x86_64util-linux 2.40.2-18.el10.x86_64 |
| Applies to | Ubuntu, Debian, Kali Linux, Linux Mint, Pop!_OS, Raspberry Pi OS, elementary OS, Zorin OS, Parrot OS, MX Linux, RHEL, Rocky Linux, AlmaLinux, Oracle Linux, CentOS Stream, Fedora, Arch Linux |
| Privilege | sudo or root for pvcreate, vgcreate, lvcreate, mkfs, and mount |
| Scope | End-to-end workflow to create PV, VG, and LV, format and mount a filesystem, persist with fstab UUID, add PV capacity with vgextend, remove layers safely, and inspect with pvs, vgs, and lvs. Does not cover installer LVM layout, extending an LV and growing its filesystem, thin provisioning, snapshots, RAID LVM, or LV reduction. |
| Related guides | How LVM works in Linux Create filesystem on a partition or LV Create LVM during installation Extend an LVM logical volume parted command |
Logical Volume Manager (LVM) stacks a few named layers between raw disks and the directories you use every day. Once you see how physical volumes, volume groups, and logical volumes connect, the same pvcreate, vgcreate, and lvcreate sequence works on exam labs and production hosts.
This walkthrough builds a small volume group from blank storage, formats a logical volume, mounts it by UUID, adds a second physical volume with vgextend, and tears everything down in dependency order. For architecture diagrams and PE sizing theory, see how LVM works in Linux.
What Is LVM?
Standard partitions have fixed boundaries until you explicitly resize them. LVM adds a storage-pool layer that makes allocating and extending logical block devices more flexible. If /data fills up, you can often grow an LV instead of repartitioning the disk underneath.
- Physical volume (PV) — a disk, partition, or other block device labeled for LVM (
pvcreate). The PV holds metadata and contributes capacity to exactly one VG. - Volume group (VG) — the named pool (
vgcreate). All free space in the VG is shared before you assign it to LVs. - Logical volume (LV) — the slice you format and mount (
lvcreate). Device paths look like/dev/vgname/lvnameor/dev/mapper/vgname-lvname. - Physical extents (PEs) — fixed-size chunks inside the VG (often 4 MB on RHEL).
lvcreate -lcounts extents;lvcreate -Luses megabytes or gigabytes. - Filesystem — still a separate step. An LV is raw block space until you run
mkfs.xfs,mkfs.ext4, or another formatter.
Capacity flows in one direction. Each layer only sees what sits directly below it.
| Layer | Example | Role |
|---|---|---|
| Disk or partition | /dev/sdb, /dev/sdb1 |
Raw block device you hand to LVM |
| Physical volume | /dev/sdb after pvcreate |
LVM metadata plus allocatable PEs |
| Volume group | vg_lab |
Pool of PEs from one or more PVs |
| Logical volume | /dev/vg_lab/lv_data |
Named block device carved from the VG |
| Filesystem | XFS or ext4 on the LV | Directory tree layout (mkfs) |
| Mount point | /mnt/lvdata |
Path where the filesystem attaches |
Adding a second PV with vgextend grows the VG free column in vgs. The LV and filesystem do not grow until you run lvextend and a filesystem resize tool in a separate guide.
Prepare a Disk or Partition for LVM
Start by confirming which devices belong to the running OS and which are blank. The lsblk command maps disks, partitions, and LVM stacks in one tree. On my lab host the system disk is sda with rhel-root and rhel-swap already on LVM.
lsblk -o NAME,SIZE,TYPE,FSTYPE,MOUNTPOINTSNAME SIZE TYPE FSTYPE MOUNTPOINTS
sda 30G disk
├─sda1 1M part
├─sda2 2G part xfs /boot
└─sda3 28G part LVM2_member
├─rhel-root 25G lvm xfs /
└─rhel-swap 3G lvm swap [SWAP]
sr0 1024M romDo not run pvcreate on sda or any partition that holds /, /boot, or swap. Use a spare whole disk (common on RHCSA labs as /dev/sdb) or a GPT partition you created with parted on that spare disk.
When only one physical disk is attached, practice with file-backed loop devices in a user-owned directory. On a two-disk lab, set DISK1=/dev/sdb and DISK2=/dev/sdc instead of the loop commands below.
LAB="$HOME/lvm-lab"
mkdir -p "$LAB"
truncate -s 512M "$LAB/disk1.img"
truncate -s 256M "$LAB/disk2.img"
DISK1=$(sudo losetup --find --show --nooverlap "$LAB/disk1.img")
DISK2=$(sudo losetup --find --show --nooverlap "$LAB/disk2.img")
printf 'DISK1=%s DISK2=%s\n' "$DISK1" "$DISK2"DISK1=/dev/loop0 DISK2=/dev/loop1--show returns the assigned loop device. --nooverlap prevents associating a second loop device with the same backing file.
A spare-looking disk may still hold application data or belong to another volume group you need. Before pvcreate or wipefs, confirm the target is unmounted, not in use, and not a member of a required VG:
lsblk -f "$DISK1"NAME FSTYPE FSVER LABEL UUID FSAVAIL FSUSE% MOUNTPOINTS
loop0sudo pvsPV VG Fmt Attr PSize PFree
/dev/sda3 rhel lvm2 a-- 28.00g 0findmnt "$DISK1"No output means the device is not mounted. If pvs lists the device under a VG you rely on, stop and pick a different disk.
Check for leftover filesystem or partition signatures before pvcreate. On a truly blank device wipefs prints nothing and exits successfully.
sudo wipefs "$DISK1"No output means the device had no detectable signatures. wipefs -a destroys signatures on the device; use it only after lsblk -f, pvs, and findmnt confirm the device is unmounted, holds no required data, and is not part of a needed VG.
Create a Physical Volume
pvcreate writes LVM metadata to the device and registers it as a PV. The device must not be mounted and must not already belong to another volume group.
sudo pvcreate "$DISK1"Physical volume "/dev/loop0" successfully created.List PVs with summary columns:
sudo pvsPV VG Fmt Attr PSize PFree
/dev/loop0 lvm2 --- 512.00m 512.00m
/dev/sda3 rhel lvm2 a-- 28.00g 0The first line shows DISK1 with an empty VG column, which means it is a PV but not yet in a volume group. The rhel line is the existing OS stack on sda3.
pvdisplay adds UUID and allocatable state for one PV:
sudo pvdisplay "$DISK1" | head -12"/dev/loop0" is a new physical volume of "512.00 MiB"
--- NEW Physical volume ---
PV Name /dev/loop0
VG Name
PV Size 512.00 MiB
Allocatable NO
PE Size 0
Total PE 0
Free PE 0
Allocated PE 0
PV UUID 0CIVOh-mPZ9-20Rd-IXj0-Fchf-C1FM-1IFRz1Allocatable NO on a fresh PV is normal until the PV joins a VG.
Create a Volume Group
vgcreate takes a new VG name and one or more PVs. Pick a short name (vg_lab, vg_data); you cannot rename lightly in production without extra steps.
sudo vgcreate vg_lab "$DISK1"Volume group "vg_lab" successfully createdCheck pool size and free space:
sudo vgsVG #PV #LV #SN Attr VSize VFree
rhel 1 2 0 wz--n- 28.00g 0
vg_lab 1 0 0 wz--n- 508.00m 508.00mVSize is slightly below the raw disk size because LVM reserves metadata. VFree is the capacity still available for new or larger LVs. #PV counts member physical volumes; #LV counts logical volumes in the group.
Create a Logical Volume
lvcreate allocates space from the VG. Use -L for a size (200M, 10G) or -l for a number of extents (-l 50 or -l 100%FREE to consume all free PEs).
XFS on RHEL 10 requires at least 300 MB on the LV. This example requests 350 MB; LVM rounds up to the nearest extent (352 MB here).
sudo lvcreate -L 350M -n lv_data vg_labRounding up size to full physical extent 352.00 MiB
Logical volume "lv_data" created.List logical volumes:
sudo lvsLV VG Attr LSize Pool Origin Data% Meta% Move Log Cpy%Sync Convert
root rhel -wi-ao---- 25.00g
swap rhel -wi-ao---- 3.00g
lv_data vg_lab -wi-a----- 352.00mThe LV path is /dev/vg_lab/lv_data (symlink) or /dev/mapper/vg_lab-lv_data (device mapper name). Thin volumes and thin pools are a different LV type and are out of scope here.
Create and Mount a Filesystem
An LV is block storage until you format it. This walkthrough uses XFS:
sudo mkfs.xfs /dev/vg_lab/lv_dataTo use ext4 instead, replace the command above with:
sudo mkfs.ext4 /dev/vg_lab/lv_dataUse ext4 rather than xfs in the /etc/fstab entry when you choose ext4. Do not run both commands on the same logical volume.
Sample output: meta-data=/dev/vg_lab/lv_data isize=512 agcount=4, agsize=22528 blks = sectsz=512 attr=2, projid32bit=1 = crc=1 finobt=1, sparse=1, rmapbt=1 = reflink=1 bigtime=1 inobtcount=1 nrext64=1 = exchange=0 metadir=0 data = bsize=4096 blocks=90112, imaxpct=25 = sunit=0 swidth=0 blks naming =version 2 bsize=4096 ascii-ci=0, ftype=1, parent=0 log =internal log bsize=4096 blocks=16384, version=2 = sectsz=512 sunit=0 blks, lazy-count=1 realtime =none extsz=4096 blocks=0, rtextents=0 = rgcount=0 rgsize=0 extents = zoned=0 start=0 reserved=0 Discarding blocks...Done.
Create a mount point and mount the LV temporarily:
```bash
sudo mkdir -p /mnt/lvdata
sudo mount /dev/vg_lab/lv_data /mnt/lvdataConfirm the mount:
findmnt /mnt/lvdataTARGET SOURCE FSTYPE OPTIONS
/mnt/lvdata /dev/mapper/vg_lab-lv_data xfs rw,relatime,seclabel,attr2,inode64,logbufs=8,logbsize=32k,noquotaCheck usable space (filesystem size is slightly below LV size due to metadata):
df -h /mnt/lvdataFilesystem Size Used Avail Use% Mounted on
/dev/mapper/vg_lab-lv_data 288M 24M 265M 9% /mnt/lvdataFor a persistent mount, capture the filesystem UUID and append one line to /etc/fstab. UUID survives reboots and LV renames more reliably than a bare device path.
Back up /etc/fstab and skip the append when the line already exists so a repeated lab run does not duplicate entries:
sudo cp -a /etc/fstab /etc/fstab.bak-lvm-labFS_UUID=$(sudo blkid -s UUID -o value /dev/vg_lab/lv_data)
FSTAB_LINE="UUID=$FS_UUID /mnt/lvdata xfs defaults 0 0"
grep -qF "$FSTAB_LINE" /etc/fstab || printf '%s\n' "$FSTAB_LINE" | sudo tee -a /etc/fstabUUID=2a627038-926e-4272-8e38-8962cb5f803c /mnt/lvdata xfs defaults 0 0On RHEL 10, regenerate systemd mount units after editing /etc/fstab, then test this mount point directly:
sudo systemctl daemon-reload
sudo umount /mnt/lvdata
sudo mount /mnt/lvdata
findmnt /mnt/lvdataTARGET SOURCE FSTYPE OPTIONS
/mnt/lvdata /dev/mapper/vg_lab-lv_data xfs rw,relatime,seclabel,attr2,inode64,logbufs=8,logbsize=32k,noquotaThat sequence is safer than mount -a, which tries every unmounted entry in /etc/fstab. Reboot and verify the persistent mount only when you used a real spare disk or partition. Skip reboot testing for the loop-backed lab unless you separately configure loop devices to be recreated at boot.
More formatter and mount options live in create filesystem on a partition or LV.
Add Capacity to a Volume Group
Growing usable space for applications takes two steps: expand the VG (this section) then extend the LV and filesystem in a follow-up procedure. Here you only add a second PV to the pool.
Repeat the same safety checks on the second device before pvcreate. On a two-disk lab, /dev/sdc may hold data even when /dev/sdb is blank:
lsblk -f "$DISK2"NAME FSTYPE FSVER LABEL UUID FSAVAIL FSUSE% MOUNTPOINTS
loop1sudo pvsPV VG Fmt Attr PSize PFree
/dev/loop0 vg_lab lvm2 a-- 508.00m 156.00m
/dev/sda3 rhel lvm2 a-- 28.00g 0findmnt "$DISK2"No output means the device is not mounted. Run pvcreate only when the second device is unmounted, absent from every required VG, and holds no needed signatures or data.
sudo wipefs "$DISK2"Create the PV on the second device:
sudo pvcreate "$DISK2"Physical volume "/dev/loop1" successfully created.Add it to the existing volume group:
sudo vgextend vg_lab "$DISK2"Volume group "vg_lab" successfully extendedConfirm the larger pool and new free extents:
sudo vgs vg_labVG #PV #LV #SN Attr VSize VFree
vg_lab 2 1 0 wz--n- 760.00m 408.00m#PV is now 2 and VFree increased. The lv_data LV is still 352 MB until you run lvextend and grow XFS or ext4.
Remove LVM Components Safely
LVM removal follows the reverse dependency chain: unmount filesystem, remove LV, remove VG, remove PV. Skipping a step leaves devices busy or metadata orphaned.
Unmount the filesystem:
sudo umount /mnt/lvdataRemove the exact /etc/fstab line that contains your filesystem UUID with sudoedit /etc/fstab, then reload systemd mount units:
sudo systemctl daemon-reloadRemove the logical volume. LVM prompts for confirmation when the LV is still active:
sudo lvremove /dev/vg_lab/lv_dataDo you really want to remove active logical volume vg_lab/lv_data? [y/n]: y
Logical volume "lv_data" successfully removed.Remove the volume group:
sudo vgremove vg_labVolume group "vg_lab" successfully removedRemove physical volume labels from both lab disks:
sudo pvremove "$DISK1" "$DISK2"Labels on physical volume "/dev/loop0" successfully wiped.
Labels on physical volume "/dev/loop1" successfully wiped.RHEL 10 enables /etc/lvm/devices/system.devices by default. The following lvmdevices step applies to RHEL 10 and other systems where the LVM devices file is enabled. Distributions that do not use system.devices can skip it.
On RHEL 10, pvcreate automatically adds newly initialized devices to that file. After pvremove, list entries and delete any that still reference your temporary loop devices. lvmdevices --deldev is the documented removal command.
sudo lvmdevicesDevice /dev/sda3 IDTYPE=sys_wwid IDNAME=t10.ATA_VBOX_HARDDISK_VBa05c0dfc-76f70818 DEVNAME=/dev/sda3 PVID=kK0T8ElrVUPAkSfdwTwIlJS32aVaNd7X PART=3
Device /dev/loop0 IDTYPE=loop_file IDNAME=/root/lvm-lab/disk1.img DEVNAME=/dev/loop0 PVID=none
Device /dev/loop1 IDTYPE=loop_file IDNAME=/root/lvm-lab/disk2.img DEVNAME=/dev/loop1 PVID=noneOnly run --deldev for devices that still appear in that list:
sudo lvmdevices --deldev "$DISK1"
sudo lvmdevices --deldev "$DISK2"Release loop devices when you used file-backed disks:
sudo losetup -d "$DISK1" "$DISK2"
rm -rf "$LAB"Verify only the OS volume group remains:
sudo vgsVG #PV #LV #SN Attr VSize VFree
rhel 1 2 0 wz--n- 28.00g 0Inspect and Troubleshoot LVM
Use the three summary commands together when a mount fails or capacity looks wrong:
| Command | Shows |
|---|---|
pvs |
Each PV, its VG membership, and free space on the PV |
vgs |
VG size, free PEs, and counts of PVs and LVs |
lvs |
LV names, sizes, and open/active attributes |
| Symptom | Likely cause | Fix |
|---|---|---|
Device /dev/... not found |
VG not activated, wrong path, or LV removed | Run sudo vgchange -ay vgname; confirm with lvs and /dev/mapper/ |
Insufficient free extents on lvcreate |
VG free space too small for requested size | Check VFree with vgs; request a smaller LV or add another PV with vgextend |
pvcreate warns about existing signature |
Old filesystem or PV label on disk | Confirm with lsblk -f, pvs, and findmnt; then wipefs -a only on an unmounted spare device with no required data |
| LV exists but is not mounted | No fstab entry or mount not run | findmnt; fix the UUID line in /etc/fstab, run systemctl daemon-reload, then mount the mount point |
df size smaller than lvs size |
Normal filesystem metadata; or LV grown without filesystem resize | Compare lvs vs df; grow the filesystem after lvextend |
mkfs.xfs refuses small LV |
LV below RHEL 10 XFS minimum (300 MB) | Increase lvcreate size or use mkfs.ext4 |
LVM Quick Reference
| Task | Command |
|---|---|
| Initialize PV | pvcreate /dev/sdb |
| Create VG | vgcreate vg_name /dev/sdb |
| Create LV by size | lvcreate -L 10G -n lv_name vg_name |
| Create LV by extents | lvcreate -l 100%FREE -n lv_name vg_name |
| Format XFS | mkfs.xfs /dev/vg_name/lv_name |
| Format ext4 | mkfs.ext4 /dev/vg_name/lv_name |
| Mount | mount /dev/vg_name/lv_name /mount/point |
| fstab by UUID | UUID=... /mount/point xfs defaults 0 0 |
| Add PV to VG | vgextend vg_name /dev/sdc |
| Summary | pvs, vgs, lvs |
| Remove LV | lvremove /dev/vg_name/lv_name |
| Remove VG | vgremove vg_name |
| Remove PV label | pvremove /dev/sdb |
References
- LVM2 manual — lvm(8)
- pvcreate(8)
- vgcreate(8)
- lvcreate(8)
- Red Hat — Basic logical volume management (RHEL 10)
Summary
You built LVM from the bottom up: blank storage became a physical volume, the PV joined a volume group, and lvcreate carved a logical volume from free extents. Formatting with mkfs.xfs or mkfs.ext4 and mounting turned that LV into a directory tree you can persist with a UUID line in /etc/fstab.
The main pitfall on small lab volumes is the RHEL 10 XFS minimum of 300 MB. This walkthrough requests 350 MB so the LV remains safely above that minimum after extent rounding; use mkfs.ext4 when you need a smaller volume. Another common mistake is stopping after vgextend; free space sits in the VG until you extend the LV and grow the filesystem in a follow-up procedure.
For production and RHCSA practice, always confirm lsblk and pvs so you target the spare disk, not the OS PV. After vgextend, run lvextend and grow the filesystem before applications see the new pool space.

