---
abstract: This post describes how to deploy the first layer of dynamic storage.
author: Xander Harris
blogpost: true
category: Cluster Services
date: 2025-10-08
tags: kubelet, storage, workloads, deployment, csi
title: Deploy the CSI LVM Storage Classes
---

While it's possible to statically provision storage, especially if you're
a contrarian fool like the author (and presumably readers) of this work,
it is also much less convenient and perhaps a little gauche to do so.

In this post we'll deploy to our cluster the first layer of dynamically
provisioned storage that we will make available via the Container
Storage Interface, or {term}`CSI`. It will use the {term}`CSI` {term}`LVM`
driver.

## Deployment of {term}`CSI` {term}`LVM` vi {term}`Helm`

```{sidebar}
In the unlikely case that you have a (some would say unnatural) keen desire
to read reference manuals, you will find that both the [Container Storage
Interface](https://kubernetes.io/blog/2019/01/15/container-storage-interface-ga/)
is pretty mature at this point and, despite its frankly confusing and somewhat
upsetting logo, the documentation is actually
[quite good](https://github.com/container-storage-interface/spec/blob/master/spec.md),
and you may even find yourself aided byt the
[CSI-LVM](https://metal-stack.io/docs/references/csi-driver-lvm/) documentation
specificially.

A similar statement may be made for the
[Logical Volume Manager](https://linuxhandbook.com/lvm-guide/)[^beginners]
which even has a
[fairly handy cheatsheet](https://ioflood.com/blog/lvm-linux-command/), which
the author is not inclined to believe that anyone who would take the time to
learn what a Logical Volume Manager and a Container Storage Interface are would
find useful when the reference manuals for both are still readily available
on the Internet.

> Don't let him fool you, his keyboard only has the three keys to begin with
>  -- The Editorial Board
```

This post assumes you already have a working bare-metal Kubernetes cluster
running on your network that was deployed using this tutorial up to this
point.

### Preparation

First, you'll need a bare-metal Kubernetes cluster with some nodes on it that
have a spare drive, or at least a spare partition on which to install
an {term}`LVM`. As you can see blow, the author's current cluster had one
control plane and four nodes that are ready with one node that is not ready.
The reason for its lack of readiness is that it contains no LVM on which the
csi-lvm-driver DaemonSet can install its required partitions.

```{code-block} shell
kubectl get nodes
---
NAME     STATUS     ROLES           AGE     VERSION
cms00    Ready      control-plane   2d21h   v1.34.1
cms01    Ready      <none>          2d21h   v1.34.1
cms02    Ready      <none>          2d21h   v1.34.1
cms03    Ready      <none>          2d21h   v1.34.1
cms04    Ready      <none>          2d21h   v1.34.1
cms05    NotReady   <none>          22h     v1.34.1
```

## LVM

To make this node ready for dynamically provisioned storage, you'll first
need to install LVM on it. Once LVM is installed you'll need to cheat-sheet
a persistent volume from one of the disks or partitions on the system, then
create a volume group inside the Logical volume manager and finally add the
persistent volume to that volume group.

The full process looks like this.

1. Install LVM.

   ```{code-block} shell
   sudo pacman -Syyu --noconfirm
   ```

   This will generate a new set of kernel images for you. If you are like the
   author (that is paranoid and incapable of fully trusting any machine with
   anything, you'll also want add the `lvm2` hook to your
   {file}`/etc/mkinitpio.conf` then use that to make your own fresh kernel
   images and reboot your system so that step 2 will be the first thing
   you run once you log back in.

   1. To update {file}`/etc/mkinitcpio.conf`, change this line.

      ```{code-block} shell
      :caption: should be around line 55
      HOOKS=(base udev autodetect microcode modconf kms keyboard keymap plymouth consolefont block filesystems fsck)
      ```

   2. So that it looks like this.

      ```{code-block} shell
      HOOKS=(base udev autodetect microcode modconf kms keyboard keymap plymouth lvm2 consolefont block filesystems fsck)
      ```

2. Make a fresh kernel images.

   ```{code-block} shell
   mkinitcpio -P && reboot
   ```

3. After you have finished your reboot, run `fdisk`.

   ```{code-block} shell
   fdisk -l
   ---

   Disk /dev/nvme0n1: 1.82 TiB, 2000398934016 bytes, 3907029168 sectors
   Disk model: WD Blue SN580 2TB
   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: B72C1947-1628-4825-A5AD-7E7B816B5AF2

   Device           Start        End    Sectors  Size Type
   /dev/nvme0n1p1    2048    4196351    4194304    2G EFI System
   /dev/nvme0n1p2 4196352 3907028991 3902832640  1.8T Linux filesystem


   Disk /dev/nvme1n1: 238.47 GiB, 256060514304 bytes, 500118192 sectors
   Disk model: iDsonix
   Units: sectors of 1 * 512 = 512 bytes
   Sector size (logical/physical): 512 bytes / 512 bytes
   I/O size (minimum/optimal): 512 bytes / 512 bytes
   ```

4. The second option should do, we will now create a persistent volume.

   ```{code-block} shell
   pvcreate /dev/nvme1n1
   ```

5. Now we create a volume group.

   ```{code-block} shell
   vgcreate csi-lvm /dev/nvme1n1
   ```

6. And you're all set. You should shortly see your node come up as available.

### Installation via Helm

1. Create a namespace for the required workloads.

   ```{code-block} shell
   kubectl create ns storage
   ```

2. Clone the storage classes repository.

   ```{code-block} shell
   git clone https://github.com/edwardtheharris/helm-storage-classes
   ```

3. Update your working directory.

   ```{code-block} shell
   cd helm-storage-classes/lvm
   ```

4. Install the app with Helm.

   ```{code-block} shell
   helm upgrade --install csi-lvm . -f values.yaml
   ```

5. Once this deployment completes successfully, you'll be able to use
   the `csi-lvm-linear` `StorageClass` to dynamically provision storage
   for your workloads.

[^beginners]: The author would suggest to you that anyone who has taken the
  time to learn about the Container Storage Interface or its abbreviation
  is not the sort of person who would use something as pedestrian as a
  cheat-sheet, or a supposed Artificial Intelligence for that matter. At least
  they got half right in both cases; suppose you put them together and you get
  an artificial cheat (not quite a portmanteau), which is an apt descriptor
  for the latter.

  > The editorial board would like to apologize for all of this unnecessary
  > pomposity and thank you, the reader, for making all of this possible.
