Configure LVM in Linux Step by Step

Tested on RHEL 10.2 (Coughlan)
Package lvm2 2.03.36-2.el10.x86_64
xfsprogs 6.16.0-1.el10.x86_64
e2fsprogs 1.47.1-5.el10.x86_64
util-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.

IMPORTANT
This article covers manual LVM creation on a spare disk or partition after the OS is installed. It does not cover the Anaconda or Calamares installer path (see create LVM during installation), growing an existing LV and filesystem (extend an LVM logical volume), or advanced features such as thin pools and snapshots.

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/lvname or /dev/mapper/vgname-lvname.
  • Physical extents (PEs) — fixed-size chunks inside the VG (often 4 MB on RHEL). lvcreate -l counts extents; lvcreate -L uses 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.

bash
lsblk -o NAME,SIZE,TYPE,FSTYPE,MOUNTPOINTS
output
NAME           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 rom

Do 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.

bash
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"
output
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:

bash
lsblk -f "$DISK1"
output
NAME  FSTYPE FSVER LABEL UUID FSAVAIL FSUSE% MOUNTPOINTS
loop0
bash
sudo pvs
output
PV         VG   Fmt  Attr PSize  PFree
  /dev/sda3  rhel lvm2 a--  28.00g    0
bash
findmnt "$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.

bash
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.

bash
sudo pvcreate "$DISK1"
output
Physical volume "/dev/loop0" successfully created.

List PVs with summary columns:

bash
sudo pvs
output
PV         VG   Fmt  Attr PSize   PFree
  /dev/loop0      lvm2 ---  512.00m 512.00m
  /dev/sda3  rhel lvm2 a--   28.00g      0

The 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:

bash
sudo pvdisplay "$DISK1" | head -12
output
"/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-1IFRz1

Allocatable 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.

bash
sudo vgcreate vg_lab "$DISK1"
output
Volume group "vg_lab" successfully created

Check pool size and free space:

bash
sudo vgs
output
VG     #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.00m

VSize 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).

bash
sudo lvcreate -L 350M -n lv_data vg_lab
output
Rounding up size to full physical extent 352.00 MiB
  Logical volume "lv_data" created.

List logical volumes:

bash
sudo lvs
output
LV      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.00m

The 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:

bash
sudo mkfs.xfs /dev/vg_lab/lv_data

To use ext4 instead, replace the command above with:

bash
sudo mkfs.ext4 /dev/vg_lab/lv_data

Use 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.

text
Create a mount point and mount the LV temporarily:

```bash
sudo mkdir -p /mnt/lvdata
sudo mount /dev/vg_lab/lv_data /mnt/lvdata

Confirm the mount:

bash
findmnt /mnt/lvdata
output
TARGET      SOURCE                     FSTYPE OPTIONS
/mnt/lvdata /dev/mapper/vg_lab-lv_data xfs    rw,relatime,seclabel,attr2,inode64,logbufs=8,logbsize=32k,noquota

Check usable space (filesystem size is slightly below LV size due to metadata):

bash
df -h /mnt/lvdata
output
Filesystem                   Size  Used Avail Use% Mounted on
/dev/mapper/vg_lab-lv_data   288M   24M  265M    9% /mnt/lvdata

For 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:

bash
sudo cp -a /etc/fstab /etc/fstab.bak-lvm-lab
bash
FS_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/fstab
output
UUID=2a627038-926e-4272-8e38-8962cb5f803c /mnt/lvdata xfs defaults 0 0

On RHEL 10, regenerate systemd mount units after editing /etc/fstab, then test this mount point directly:

bash
sudo systemctl daemon-reload
sudo umount /mnt/lvdata
sudo mount /mnt/lvdata
findmnt /mnt/lvdata
output
TARGET      SOURCE                     FSTYPE OPTIONS
/mnt/lvdata /dev/mapper/vg_lab-lv_data xfs    rw,relatime,seclabel,attr2,inode64,logbufs=8,logbsize=32k,noquota

That 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:

bash
lsblk -f "$DISK2"
output
NAME  FSTYPE FSVER LABEL UUID FSAVAIL FSUSE% MOUNTPOINTS
loop1
bash
sudo pvs
output
PV         VG     Fmt  Attr PSize   PFree
  /dev/loop0 vg_lab lvm2 a--  508.00m 156.00m
  /dev/sda3  rhel   lvm2 a--   28.00g      0
bash
findmnt "$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.

bash
sudo wipefs "$DISK2"

Create the PV on the second device:

bash
sudo pvcreate "$DISK2"
output
Physical volume "/dev/loop1" successfully created.

Add it to the existing volume group:

bash
sudo vgextend vg_lab "$DISK2"
output
Volume group "vg_lab" successfully extended

Confirm the larger pool and new free extents:

bash
sudo vgs vg_lab
output
VG     #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:

bash
sudo umount /mnt/lvdata

Remove the exact /etc/fstab line that contains your filesystem UUID with sudoedit /etc/fstab, then reload systemd mount units:

bash
sudo systemctl daemon-reload

Remove the logical volume. LVM prompts for confirmation when the LV is still active:

bash
sudo lvremove /dev/vg_lab/lv_data
output
Do 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:

bash
sudo vgremove vg_lab
output
Volume group "vg_lab" successfully removed

Remove physical volume labels from both lab disks:

bash
sudo pvremove "$DISK1" "$DISK2"
output
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.

bash
sudo lvmdevices
output
Device /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=none

Only run --deldev for devices that still appear in that list:

bash
sudo lvmdevices --deldev "$DISK1"
sudo lvmdevices --deldev "$DISK2"

Release loop devices when you used file-backed disks:

bash
sudo losetup -d "$DISK1" "$DISK2"
rm -rf "$LAB"

Verify only the OS volume group remains:

bash
sudo vgs
output
VG   #PV #LV #SN Attr   VSize  VFree
  rhel   1   2   0 wz--n- 28.00g    0

Inspect 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


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.


Frequently Asked Questions

1. What is the order to create LVM in Linux?

Prepare a blank disk or partition, run pvcreate on each physical volume, vgcreate to pool PVs into a volume group, lvcreate to carve a logical volume, then mkfs and mount. Capacity flows disk to PV to VG to LV to filesystem.

2. Can I use a whole disk for LVM without partitioning?

Yes. pvcreate accepts a whole disk such as /dev/sdb or a GPT partition such as /dev/sdb1. Do not run pvcreate on partitions that already hold your root filesystem or boot loader.

3. Why does mkfs.xfs fail on a small logical volume?

On RHEL 10, mkfs.xfs requires a filesystem of at least 300 MB. Use a larger lvcreate size, or format with mkfs.ext4 when you need a smaller volume.

4. Should I mount an LVM logical volume by device path or UUID in fstab?

Use the filesystem UUID from blkid in /etc/fstab. UUID survives LV renames and is clearer than a /dev/mapper path when multiple volumes exist on one host.

5. How do I add more space to LVM after the volume group is created?

Prepare another blank disk or partition, pvcreate it, then vgextend the existing volume group. Free space appears in vgs until you lvextend the logical volume and grow the filesystem.
Deepak Prasad

R&D Engineer

Founder of GoLinuxCloud with more than 15 years of expertise in Linux, Python, Go, Laravel, DevOps, Kubernetes, Git, Shell scripting, OpenShift, AWS, Networking, and Security. With extensive experience, he excels across development, DevOps, networking, and security, delivering robust and efficient solutions for diverse projects.

  • Go (programming language)
  • Python (programming language)
  • DevOps
  • Computer Security
  • Cloud Computing
  • Kubernetes
  • Linux
  • Ansible (software)