LVM – Logical Volume Management in Linux
With the implementation of LVM we can reallocate storage space and extend our storage volumes really easily. In the example below I’d like to show how. Keep in mind that these examples are for learning purposes only, it doesn’t make a lot of sense in real life. So what I did here is that I found an old thumb drive in my drawer and I partitioned it with my Raspberry Pi to practice LVM in Linux. So let’s begin.
First let’s install the lvm2 package on our Linux system, since we’re using a Debian-based system we’ll use the apt package manager:
sudo apt install lvm2
For learning and for demonstration purposes I used a simple 8 gig thumb drive which I’ve divided to 5 partitions with fdisk before.
It’s important to change the default ‘Linux Filesystem’ partition type to ‘Linux LVM’ in fdisk for each partition:
Partition type or alias (type L to list all): lvm
Changed type of partition 'Linux filesystem' to 'Linux LVM'.
These are the partitions I’ve created with fdisk:
Command (m for help): p
Disk /dev/sda: 7.48 GiB, 8027897856 bytes, 15679488 sectors
Disk model: DataTraveler 2.0
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: gpt
Disk identifier: BC399462-A719-AF42-AEF1-4CBF153B7820
Device Start End Sectors Size Type
/dev/sda1 2048 2099199 2097152 1G Linux LVM
/dev/sda2 2099200 4196351 2097152 1G Linux LVM
/dev/sda3 4196352 8390655 4194304 2G Linux LVM
/dev/sda4 8390656 12584959 4194304 2G Linux LVM
/dev/sda5 12584960 15679454 3094495 1.5G Linux LVM
So this our initial state, I didn’t put any filesystem on the partitions, I didn’t mount anything.
Step 1 – Create Physical Volumes
With the pvcreate command we’re creating physical volumes, we simply assign an LVM header to the partitions meaning that now they can be added to a volume group. We’re utilizing the brace expansion feature of the bash shell here, so we don’t need to list each partitions with its absolute path.
viktor@raspberry:~ $ sudo pvcreate /dev/sda{1,2,3,4,5}
Physical volume "/dev/sda1" successfully created.
Physical volume "/dev/sda2" successfully created.
Physical volume "/dev/sda3" successfully created.
Physical volume "/dev/sda4" successfully created.
Physical volume "/dev/sda5" successfully created.
We can make sure that the physical volumes has been created successfully with pvdisplay (more verbose) or the pvs (less verbose output) command. We can also discover the physical volumes on our system with the pvscan command.
viktor@raspberry:~ $ sudo pvs
PV VG Fmt Attr PSize PFree
/dev/sda1 lvm2 --- 1.00g 1.00g
/dev/sda2 lvm2 --- 1.00g 1.00g
/dev/sda3 lvm2 --- 2.00g 2.00g
/dev/sda4 lvm2 --- 2.00g 2.00g
/dev/sda5 lvm2 --- <1.48g <1.48g
Step 2 – Create the Volume Group
At this point we simply group together our partitions into a “pool” which we’ve designated before with the LVM header using pvcreate command. I name the volume group simply VG_TEST:
viktor@raspberry:~ $ sudo vgcreate VG_TEST /dev/sda{1,2,3,4,5}
Volume group "VG_TEST" successfully created
As we’ve seen before with the physical volumes we can utilize the vgdisplay and the vgs commands to check that our volume has been successfully created:
viktor@raspberry:~ $ sudo vgdisplay
--- Volume group ---
VG Name VG_TEST
System ID
Format lvm2
Metadata Areas 5
Metadata Sequence No 1
VG Access read/write
VG Status resizable
MAX LV 0
Cur LV 0
Open LV 0
Max PV 0
Cur PV 5
Act PV 5
VG Size <7.46 GiB
PE Size 4.00 MiB
Total PE 1909
Alloc PE / Size 0 / 0
Free PE / Size 1909 / <7.46 GiB
VG UUID 3Oro83-CiKC-DmAH-aLzf-HdzL-8r5N-yy8Onc
viktor@raspberry:~ $ sudo vgs
VG #PV #LV #SN Attr VSize VFree
VG_TEST 5 0 0 wz--n- <7.46g <7.46g
As we can see we still have all the free disk space, since we didn’t create any logical volumes yet.
Step 3 – Create Logical Volumes
We’re going to “carve out” spaces from our volume group to create logical volumes using the lvcreate command specifying its size and name. We have two choices here: we can specify the logical volume as fixed size, as I did below:
viktor@raspberry:~ $ sudo lvcreate VG_TEST -L 500M -n LILCHUNK
WARNING: ext4 signature detected on /dev/VG_TEST/LILCHUNK at offset 1080. Wipe it? [y/n]: y
Wiping ext4 signature on /dev/VG_TEST/LILCHUNK.
Logical volume "LILCHUNK" created.
or we can define as how much % of the volume group we’d like “cut out”. Issuing the command below will create a logical volume with half the size of the volume group, I named it BIGCHUNK:
viktor@raspberry:~ $ sudo lvcreate VG_TEST -l 50%FREE -n BIGCHUNK
WARNING: ext4 signature detected on /dev/VG_TEST/BIGCHUNK at offset 1080. Wipe it? [y/n]: y
Wiping ext4 signature on /dev/VG_TEST/BIGCHUNK.
Logical volume "BIGCHUNK" created.
Similarly to Step 1 and 2 I issued the lvdisplay and the lvs commands as well as the vgdisplay command: now it can be seen that the free space in the volume group has been reduced since we’ve created 2 logical volumes and allocated free space for them with the size of 500 MB and 3.48 GB.
viktor@raspberry:~ $ sudo lvdisplay
--- Logical volume ---
LV Path /dev/VG_TEST/LILCHUNK
LV Name LILCHUNK
VG Name VG_TEST
LV UUID 2kjJGz-4GsD-sWQW-UnPn-wQyc-olcE-dgTAym
LV Write Access read/write
LV Creation host, time raspberry, 2024-01-20 17:36:44 +0100
LV Status available
# open 0
LV Size 500.00 MiB
Current LE 125
Segments 1
Allocation inherit
Read ahead sectors auto
- currently set to 256
Block device 254:0
--- Logical volume ---
LV Path /dev/VG_TEST/BIGCHUNK
LV Name BIGCHUNK
VG Name VG_TEST
LV UUID Sg4iEM-Fe8E-cGBV-33zw-j5P0-MK5i-teeWdD
LV Write Access read/write
LV Creation host, time raspberry, 2024-01-20 17:41:34 +0100
LV Status available open 0 LV Size 3.48 GiB
Current LE 892
Segments 2
Allocation inherit
Read ahead sectors auto
- currently set to 256
Block device 254:1
viktor@raspberry:~ $ sudo lvs
LV VG Attr LSize Pool Origin Data% Meta% Move Log Cpy%Sync Convert
BIGCHUNK VG_TEST -wi-a----- 3.48g
LILCHUNK VG_TEST -wi-a----- 500.00m
viktor@raspberry:~ $ sudo vgdisplay
--- Volume group ---
VG Name VG_TEST
System ID
Format lvm2
Metadata Areas 5
Metadata Sequence No 3
VG Access read/write
VG Status resizable
MAX LV 0
Cur LV 2
Open LV 0
Max PV 0
Cur PV 5
Act PV 5
VG Size <7.46 GiB
PE Size 4.00 MiB
Total PE 1909
Alloc PE / Size 1017 / 3.97 GiB
Free PE / Size 892 / 3.48 GiB
VG UUID 3Oro83-CiKC-DmAH-aLzf-HdzL-8r5N-yy8Onc
Step 4 – Create a Filesystem on the Logical Volumes
We can refer to the logical volumes with 2 methods: we can use /dev/VG_TEST/LILCHUNK and /dev/VG_TEST/BIGCHUNK (lvm1 naming convention) or we can use the more modern way with the mapper referring to virtual volumes: /dev/mapper/VG_TEST-LILCHUNK and /dev/mapper/VG_TEST-BIGCHUCK. Feel free to use either of them, both of them works.
To be able to mount and store files on the logical volumes we need to put a filesystem on them. There is nothing special or extraordinary here, we simply use the mkfs command as we would do with any other regular partition.
viktor@raspberry:~ $ sudo mkfs.ext4 /dev/mapper/VG_TEST-BIGCHUNK
mke2fs 1.46.2 (28-Feb-2021)
Creating filesystem with 913408 4k blocks and 228480 inodes
Filesystem UUID: 62e039e9-4305-4812-9be0-90c294958760
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
viktor@raspberry:~ $ sudo mkfs.ext3 /dev/VG_TEST/LILCHUNK
mke2fs 1.46.2 (28-Feb-2021)
Creating filesystem with 512000 1k blocks and 128016 inodes
Filesystem UUID: fa2db26c-fe39-4508-853a-15563c799e2f
Superblock backups stored on blocks:
8193, 24577, 40961, 57345, 73729, 204801, 221185, 401409
Allocating group tables: done
Writing inode tables: done
Creating journal (8192 blocks): done
Writing superblocks and filesystem accounting information: done
I created different filesystems on each logical volumes (ext3 and ext4), just to demonstrate that they don’t necessarily have to be the same. Since both of ext3 and ext4 has journaling capabilities it doesn’t make any practical difference here, in real life you’d prefer ext4 for it’s improved performance, I guess.
viktor@raspberry:~ $ lsblk -f
NAME FSTYPE FSVER LABEL UUID FSAVAIL FSUSE% MOUNTPOINT
sda
|-sda1 LVM2_member LVM2 001 IfNmER-yNUX-PbFu-LjeP-6iPp-YGr1-ULmOcj
| -VG_TEST-LILCHUNK ext3 1.0 fa2db26c-fe39-4508-853a-15563c799e2f |-sda2 LVM2_member LVM2 001 hblW1j-Rln8-ltjL-JaBi-vQdb-EVRx-2FCX9a |-sda3 LVM2_member LVM2 001 59cz7C-ZOLN-HoMi-AuBn-d0Wh-KQCj-bALowz |-VG_TEST-BIGCHUNK ext4 1.0 62e039e9-4305-4812-9be0-90c294958760
|-sda4 LVM2_member LVM2 001 cSK7GL-itKb-WV3K-NQPq-b9YN-uOeC-y1pbso
| -VG_TEST-BIGCHUNK ext4 1.0 62e039e9-4305-4812-9be0-90c294958760 -sda5 LVM2_member LVM2 001 7m3WPu-fI8H-DJMS-UOq1-0j9b-1ENV-JR3SFG
mmcblk0
|-mmcblk0p1 vfat FAT32 bootfs C336-AC83 204.6M 20% /boot
`-mmcblk0p2 ext4 1.0 rootfs eaaa4faa-eab6-400c-950f-dc96ae4e0400 23G 17% /
Step 5 – Mount the Logical Volumes
Now we mount the logical volumes as we’d do with any other regular partition using the mount command. Before issuing the mount command we’ll have to create two empty directories for both logical volumes:
viktor@raspberry:~ $ mkdir /mnt/LVM_{LILCHUNK,BIGCHUNK}
viktor@raspberry:/mnt $ sudo mount /dev/mapper/VG_TEST-BIGCHUNK /mnt/LVM_BIGCHUNK/
viktor@raspberry:/mnt $ sudo mount /dev/VG_TEST/LILCHUNK /mnt/LVM_LILCHUNK/
As it can be seen above I demonstrate both naming convention, both of them works just fine, so don’t get confused because of the naming difference.
Step 6 – Extend the Logical Volume
The real advantage using logical volumes is that they can be extended (or reduced) on the fly, the disk space can be allocated dynamically for the partitions. You’re out of free space? You just need to add more space from the “pool” (volume group) as much as you exactly need. So let’s write some random data to the LILCHUCK exhausting all the free space:
viktor@raspberry:~ $ sudo dd if=/dev/urandom of=/mnt/LVM_LILCHUNK/too_big bs=1M count=500
As we can see from the output of the df command, LILCHUNK is full.
viktor@raspberry:~ $ dd: error writing '/mnt/LVM_LILCHUNK/too_big': No space left on device
viktor@raspberry:~ $ df -h
Filesystem Size Used Avail Use% Mounted on
/dev/root 30G 4.9G 23G 18% /
devtmpfs 333M 0 333M 0% /dev
tmpfs 461M 0 461M 0% /dev/shm
tmpfs 185M 1.2M 184M 1% /run
tmpfs 5.0M 4.0K 5.0M 1% /run/lock
/dev/mmcblk0p1 255M 51M 205M 20% /boot
tmpfs 93M 24K 93M 1% /run/user/1000
/dev/mapper/VG_TEST-BIGCHUNK 3.4G 24K 3.2G 1% /mnt/LVM_BIGCHUNK
/dev/mapper/VG_TEST-LILCHUNK 474M 474M 0 100% /mnt/LVM_LILCHUNK
So let’s allocate some free space from the volume group, again we have two choices: can specify either the exact value we’d like to add to the logical volume (‘L’ flag) or the percentage of the free space in the volume group (‘l’ flag). In this example I add +500MB of free space to LILCHUNK:
viktor@raspberry:~ $ sudo lvextend -L +500M /dev/mapper/VG_TEST-LILCHUNK
Size of logical volume VG_TEST/LILCHUNK changed from 500.00 MiB (125 extents) to 1000.00 MiB (250 extents).
Logical volume VG_TEST/LILCHUNK successfully resized.
viktor@raspberry:~ $ sudo lvs
LV VG Attr LSize Pool Origin Data% Meta% Move Log Cpy%Sync Convert
BIGCHUNK VG_TEST -wi-ao---- 3.48g
LILCHUNK VG_TEST -wi-ao---- 1000.00m
So according to the output we should have a logical partition with the size of around ~1GB. But the df command still doesn’t show that it has been extended properly:
viktor@raspberry:~ $ df -h
Filesystem Size Used Avail Use% Mounted on
/dev/root 30G 4.9G 23G 18% /
devtmpfs 333M 0 333M 0% /dev
tmpfs 461M 0 461M 0% /dev/shm
tmpfs 185M 1.2M 184M 1% /run
tmpfs 5.0M 4.0K 5.0M 1% /run/lock
/dev/mmcblk0p1 255M 51M 205M 20% /boot
tmpfs 93M 24K 93M 1% /run/user/1000
/dev/mapper/VG_TEST-BIGCHUNK 3.4G 24K 3.2G 1% /mnt/LVM_BIGCHUNK
/dev/mapper/VG_TEST-LILCHUNK 474M 474M 0 100% /mnt/LVM_LILCHUNK
The issue here that the ext3 filesystem on the logical volume hasn’t been resized yet, we can do that with the resize2fs utility, if we don’t specify anything it’ll extend the filesystem on the whole volume:
viktor@raspberry:~ $ sudo resize2fs /dev/mapper/VG_TEST-LILCHUNK
resize2fs 1.46.2 (28-Feb-2021)
Filesystem at /dev/mapper/VG_TEST-LILCHUNK is mounted on /mnt/LVM_LILCHUNK; on-line resizing required
old_desc_blocks = 2, new_desc_blocks = 4
The filesystem on /dev/mapper/VG_TEST-LILCHUNK is now 1024000 (1k) blocks long.
In the example below let’s extend BIGCHUNK with specifying the ‘-l’ option, I also use the –resizefs option, so we won’t need to run the resize2fs command separately.
viktor@raspberry:~ $ sudo lvextend --resizefs -l +40%FREE /dev/mapper/VG_TEST-BIGCHUNK
Size of logical volume VG_TEST/BIGCHUNK changed from 3.48 GiB (892 extents) to 4.68 GiB (1199 extents).
Logical volume VG_TEST/BIGCHUNK successfully resized.
resize2fs 1.46.2 (28-Feb-2021)
Filesystem at /dev/mapper/VG_TEST-BIGCHUNK is mounted on /mnt/LVM_BIGCHUNK; on-line resizing required
old_desc_blocks = 1, new_desc_blocks = 1
The filesystem on /dev/mapper/VG_TEST-BIGCHUNK is now 1227776 (4k) blocks long.
viktor@raspberry:~ $ df -h
Filesystem Size Used Avail Use% Mounted on
/dev/root 30G 4.9G 23G 18% /
devtmpfs 333M 0 333M 0% /dev
tmpfs 461M 0 461M 0% /dev/shm
tmpfs 185M 1.2M 184M 1% /run
tmpfs 5.0M 4.0K 5.0M 1% /run/lock
/dev/mmcblk0p1 255M 51M 205M 20% /boot
tmpfs 93M 24K 93M 1% /run/user/1000
/dev/mapper/VG_TEST-BIGCHUNK 4.6G 24K 4.3G 1% /mnt/LVM_BIGCHUNK
/dev/mapper/VG_TEST-LILCHUNK 959M 495M 420M 55% /mnt/LVM_LILCHUNK
So this is how we can create and extend logical volumes in a nutshell. LVM is a very useful thing, the capability to dynamically extend or reduce your volume size according to your needs is something really powerful. LVM has many additional features on top of that, for example we can create snapshots, and restore files we’ve deleted for some reason. I might write about that in a future post.