# Partitioning

Different partioning schemes and their setup

# Understanding Linux file systems

Linux supports a number of different file systems with different sets of features and intended use-cases.

## Ext4: The All-rounder

Ext4 is the latest iteration of the "Extended file system" and is the default choice for most major Linux distributions. As the direct descendant of Ext2 and Ext3, it represents decades of battle-tested development, making it one of the most mature, reliable, and stable file systems for Linux.

Ext4 keeps a dedicated log (or journal) of files that are about to be written to the disk. Once the data is safely committed, it is removed from the journal. This improves file system integrity and prevents data corruption in the event of an unexpected power loss or system crash.

Instead of writing data to the drive immediately, Ext4 buffers the data in system memory and allocates the physical disk blocks just before writing them to storage. This feature improves overall write performance and helps maximize the lifespan of flash-based storage devices, like SSDs.

Ext4 actively optimizes how files are scattered across the drive to prevent file fragmentation, which benefits slower storage devices like HDDs by minimizing seek times of the drive head.

Since Ext4 is mature and battle-tested, tooling around it is as reliable as it gets. There exist a myriad of data recovery tools in the case something *does* go wrong. The file system is also very flexible in that it can be expanded or shrunk as needed.

**When you want it:** You just need a reliable, "set-it-and-forget-it" file system that works well without any manual tuning. Even with its old heritage, it's still a good choice for everyday desktop users, gamers, older computers, and external backup drives. Choose Ext4 if you need low system overhead and want the flexibility to safely shrink your partition later if your storage needs change.

**When you don't want it:** You need advanced, modern storage features. If you want to create instant system-wide snapshots before updating your software, pool multiple different-sized hard drives together, or use transparent data compression to save space on an SSD.

## Btrfs: The new kid on the block

Btrfs is a new type of Linux file system that is designed differently from Ext4 in some respects. Where Ext4 is built around simplicity, btrfs shines with modern storage features.

Btrfs is a copy-on-write (CoW for short) file system, which means that copies of files are only "virtual" and do not occupy any additional storage space, and a copy only becomes "real" once it has been changed. Writes do not overwrite data in place; instead, a modified copy of the block is written to a new location, and metadata is updated to point at the new location.

Btrfs organizes its data in subvolumes, which can be mounted like partitions. Unlike partitions, subvolumes do not have a fixed size. Instead, subvolumes are merely an organizational unit on the same Btrfs partition, the size of which depends on the contents stored in them. Any number of subvolumes can be created for different mount points, e.g. `/` and `/home`. This allows, amongst other things, for multiple operating systems to be installed to the same disk on the same computer without interfering with each other.

Another feature of Btrfs is its ability to create snapshots of the file system. The state of a subvolume can be recorded in a snapshot, e.g. before a critical system update, in order to revert to a previous state of the file system if necessary. Thanks to CoW, snapshots require very little storage space compared to full-fledged backups (although they are no replacement for them!) and can be mounted and booted from like regular subvolumes. This makes it possible to "rewind" the state of the file system with comparatively little effort. Tools such as `snapper` or `timeshift` can simplify and automate the process of creating snapshots during system updates and restoring from snapshots from the commandline.

Btrfs also implements transparent compression of data blocks. Written data is automatically stored in compressed form if the appropriate mount options are set. There are a number of different compression algorithms to choose from, including lz4, gzip and Zstandard. This can also increase the life span of flash based storage devices, as less data is written to the disk and not as much wear-leveling is taking place.

Btrfs comes with RAID management for RAID 0, 1 and 10 built into the file system itself and makes an additional software or firmware RAID superfluous for these configurations. In addition, the integrated RAID functionality offers the advantage that it is aware of used and free data blocks in mirrored setups, which can considerably speed up the reconstruction of a RAID, as only the used blocks are reconstructed. Using the built-in RAID functionality in Btrfs also allows for more storage devices to be added to the RAID later on.

**When you want it:** You love to tinker with your system, run rolling-release Linux distributions (like Arch or openSUSE Tumbleweed), or want bulletproof peace of mind. Its instant snapshotting turns a system-breaking update into a ten-second rollback. It is also ideal if you want to squeeze more space out of your SSD via built-in compression or don't want to agonize over the correct partition sizes for your `/` and `/home` partitions and instead share storage dynamically between them.

**When you don't want it:** Your daily workflow involves heavy database management (like MySQL or PostgreSQL) or hosting several virtual machine images. Because of its Copy-on-Write mechanics, frequently modified files inside VMs and databases will heavily fragment the drive, dragging down performance. You should also avoid it if you plan on using RAID 5 or RAID 6 storage arrays, as the Btrfs implementation for those specific modes remains notoriously unstable.

## XFS: The Enterprise Heavyweight

Originally developed by Silicon Graphics (SGI) in 1993 for their high-end IRIX operating system, XFS is a mature, 64-bit, high-performance file system. It was specifically engineered to handle massive media workstations and massive datasets long before modern storage sizes became common. Today, it is highly revered for its industrial-grade reliability and serves as the default file system for enterprise distributions like Red Hat Enterprise Linux (RHEL) and Fedora Server.

The most notable feature of XFS is its *Allocation Group* architecture. It divides the storage volume into discrete, independent regions. Each allocation group manages its own free space and inodes, allowing multiple CPU cores and application processes to perform read and write operations simultaneously without locking each other out. This results in exceptional parallel I/O performance.

Like Ext4, XFS protects data integrity using a metadata journal, ensuring rapid recovery after a sudden power loss. Its on-disk structure (advanced B+ trees) allows it to track block of free space to write files efficiently into contiguous blocks, preventing file fragmentation and keeping throughput high.

**When you want it:** You are building an enterprise-grade server, a database host, or a media production workstation. XFS shines brightest when handling massive files—like multi-gigabyte raw video footage, massive machine learning datasets, or monolithic backups. It is the go-to choice if you have a powerful multi-core CPU and multiple applications demanding high-speed, parallel access to the disk at the exact same time.

**When you don't want it:** You are setting up a standard consumer laptop or desktop where partition flexibility matters. Because XFS cannot be shrunk under any circumstances, it is a terrible choice if you ever plan to resize your partitions later or dual-boot with another operating system. It is also less efficient than Ext4 on smaller drives that are filled with millions of tiny text or code files rather than giant media blocks.

## Swap: When RAM just isn't enough

A special type of file system on Linux is the Swap file system. As the name implies, it is used for "swapping" out data in RAM onto a storage device, like an SSD or HDD, to make room for actively used data the system needs to work with. It prevents the system from running out of RAM when lots of things are going on at the same time which your physical RAM can't hold.

Swap can either be its own *partition* or a *file*. Which one you choose comes down to what your use-case is and the administrative effort you're willing to put up with.

**A Swap partition** carves out a rigid portion of your drive just for swapped out memory pages. If you want to use hibernation on a laptop, a swap partition typically saves you a couple headaches in setting it up correctly—just point the system at the swap partition and you're done (`resume="PARTLABEL=swap"`). If you upgrade your RAM, however, you will also have to resize your swap partition to be at least as big as the amount of RAM in your system to continue using hibernation. This can entail its own set of administrative headaches, because resizing the swap partition usually means other partitions have to be shrunk (something that's impossible if you went with XFS for example).

**A Swap file** on the other hand is very flexible and can be created in seconds while the system is running. In case you need more swap space, you can simply create another swap file, turn it on, and delete it again after you're done. The downside with this approach with regards to hibernation is that you will have to tell the system the exact physical file offset of the swap file on disk (`resume="PARTLABEL=root" resume_offset=38912`) and need to update this value in case you re-create the swap file. You also won't be able to span multiple swap files for hibernation, as all contents in RAM must fit into a single file. There's also special considerations to take into account when using a swap file in conjunction with btrfs, since its CoW capabilities can cause considerable performance hits and file system fragmentation, unless CoW is specifically disabled for the swap file (`chattr +C`).

If you don't need hibernation and want the flexibility of easily expanding swap space, a **swap file** offers exactly what you need.

If you have a more complex storage setup (e.g. ZFS or btrfs RAID), or you're not bothered by its inflexibility, a **swap partition** is the more sensible choice.

# Singular file system

The simplest, most basic partitioning scheme in any Linux operating system consists of 3 partitions:

| Type                 | File System                | Description                                                                   |
|----------------------|----------------------------|-------------------------------------------------------------------------------|
| EFI System Partition | vfat                       | Stores boot loaders and bootable OS images in `.efi` format                   |
| Root File System     | ext4, btrfs, XFS, or other | Stores the Linux OS files (kernel, system libraries, applications, user data) |
| Swap                 | Swap partition or file     | Stores swapped memory pages from RAM during high memory pressure              |

This guide assumes the following:

* There is only 1 disk that needs partitioning
* `/dev/nvme0n1` is the primary disk

## Preparing the disk

Determine the disks that are installed on your system. This can easily be done with `fdisk`:

~~~sh
fdisk -l
~~~

It outputs a list of disk devices with one or more entries similar to this:

~~~
Disk /dev/nvme0n1: 232.89 GiB, 250059350016 bytes, 488397168 sectors
Disk model: Samsung SSD 840 
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: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
~~~

The line starting the device file with `/dev/` is the relevant one. Start partitioning the disk with `cfdisk`:

<p class="callout danger"><strong>WARNING:</strong> Make sure you are modifying the correct device, else you <em>will</em> lose data!</p>

~~~sh
cfdisk /dev/nvme0n1
~~~

If the disk has no partition table yet, `cfdisk` will ask you to specify one. The default partition table format for UEFI systems is `gpt`. Create a layout with at least 3 partitions:

| Size        | FS Type             |
|-------------|---------------------|
| 1G          | EFI System          |
| (RAM size)  | Linux Swap          |
| (remaining) | Linux root (x86-64) |

<p class="callout info"><strong>NOTE:</strong> Specifying the correct file system type allows some software to automatically detect and assign appropriate mount points to partitions. See <a href="https://www.freedesktop.org/wiki/Specifications/DiscoverablePartitionsSpec/" target="_blank">Discoverable Partitions Specification</a> for more details.</p>

You can verfiy that the partitions have been created by running `fdisk -l` again:

~~~
Disk /dev/nvme0n1: 232.89 GiB, 250059350016 bytes, 488397168 sectors
Disk model: Samsung SSD 840 
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: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX

Device             Start       End   Sectors   Size Type
/dev/nvme0n1p1      2048   2099199   2097152     1G EFI System
/dev/nvme0n1p2   2099200  35653631  33554432    16G Linux swap
/dev/nvme0n1p3  35653632 488396799 452743168 215.9G Linux root (x86-64)
~~~

This time `fdisk` will also list the partitions present on the disk.

<p class="callout info"><strong>NOTE:</strong> You might notice a pattern with how Linux structures its block devices. Partitions also count as "devices" which you can interact with. Each partition has an incrementing counter attached to its name to specify its order in the partition layout.</p>

## Formatting partitions

Format the partition with the appropriate `mkfs` subcommand for the file system you want to use, e.g. ext4:

~~~sh
mkfs.ext4 /dev/nvme0n1p3        # ext4 root file system
mkfs.fat -F 32 /dev/nvme0n1p1   # EFI System Partition
mkswap /dev/nvme0n1p2           # Swap space
~~~

Next mount the file systems:

<p class="callout warning"><strong>ATTENTION:</strong> Depending on which file system you chose earlier for your root file system, additional mount parameters might be beneficial or necessary, e.g. <code>btrfs</code> requires specifying the subvolume you want to mount using the option <code>subvol=NAME</code>. Refer to the file system's manual to determine relevant mount parameters.</p>

~~~sh
mount /dev/nvme0n1p3 -o noatime /mnt
mount /dev/nvme0n1p1 --mkdir /mnt/efi
swapon /dev/nvme0n1p2
~~~

# Singular file system (LUKS, encrypted)

LUKS (Linux Unified Key Setup) is the standard for Linux hard disk encryption. By providing a standard on-disk-format, it does not only facilitate compatibility among distributions, but also provides secure management of multiple user passwords. LUKS stores all necessary setup information in the partition header, enabling to transport or migrate data seamlessly.

Management of LUKS encrypted devices is done via the [`cryptsetup`](https://gitlab.com/cryptsetup/cryptsetup) utility.

<p class="callout info"><strong>NOTE:</strong> Why should you encrypt your data? Encryption ensures that no one but the rightful owner has access to the data. Encryption is therefore not only used to hide sensitive data from prying eyes, it also serves to protect your privacy. Encryption should be considered especially for portable devices such as laptops. In the event of loss or theft, encryption ensures that personal data and secrets (passwords, key files, etc.) do not fall into the wrong hands and are less likely and not as easily be abused.</p>

The simplest, most basic encrypted partitioning scheme in a Linux operating system consists of 3 partitions:

| Type                 | File System | Description                                                                   |
|----------------------|-------------|-------------------------------------------------------------------------------|
| EFI System Partition | vfat        | Stores boot loaders and bootable OS images in `.efi` format                   |
| Swap                 | LUKS2       | Stores swapped memory pages from RAM during high memory pressure              |
| Root File System     | LUKS2       | Stores the Linux OS files (kernel, system libraries, applications, user data) |

This guide assumes the following:

* There is only 1 disk that needs partitioning
* `/dev/nvme0n1` is the primary disk

## Preparing the disk

Determine the disks that are installed on your system. This can easily be done with `fdisk`:

~~~sh
fdisk -l
~~~

It outputs a list of disk devices with one or more entries similar to this:

~~~
Disk /dev/nvme0n1: 232.89 GiB, 250059350016 bytes, 488397168 sectors
Disk model: Samsung SSD 840 
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: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
~~~

The line starting the device file with `/dev/` is the relevant one. Start partitioning the disk with `cfdisk`:

<p class="callout danger"><strong>WARNING:</strong> Make sure you are modifying the correct device, else you <em>will</em> lose data!</p>

~~~sh
cfdisk /dev/nvme0n1
~~~

If the disk has no partition table yet, `cfdisk` will ask you to specify one. The default partition table format for UEFI systems is `gpt`. Create a layout with at least 3 partitions:

| Size        | FS Type             |
|-------------|---------------------|
| 1G          | EFI System          |
| (RAM size)  | Linux Swap          |
| (remaining) | Linux root (x86-64) |

<p class="callout info"><strong>NOTE:</strong> Specifying the correct file system type allows some software to automatically detect and assign appropriate mount points to partitions. See <a href="https://www.freedesktop.org/wiki/Specifications/DiscoverablePartitionsSpec/" target="_blank">Discoverable Partitions Specification</a> for more details.</p>

You can verfiy that the partitions have been created by running `fdisk -l` again:

~~~
Disk /dev/nvme0n1: 232.89 GiB, 250059350016 bytes, 488397168 sectors
Disk model: Samsung SSD 840 
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: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX

Device             Start       End   Sectors   Size Type
/dev/nvme0n1p1      2048   2099199   2097152     1G EFI System
/dev/nvme0n1p2   2099200  35653631  33554432    16G Linux swap
/dev/nvme0n1p3  35653632 488396799 452743168 215.9G Linux root (x86-64)
~~~

This time `fdisk` will also list the partitions present on the disk.

<p class="callout info"><strong>NOTE:</strong> You might notice a pattern with how Linux structures its block devices. Partitions also count as "devices" which you can interact with. Each partition has an incrementing counter attached to its name to specify its order in the partition layout.</p>

## Encrypting partitions

Before writing a file system to the disk a LUKS container needs to be created with the `cryptsetup` utility:

<p class="callout danger"><strong>WARNING:</strong> Do <strong>NOT</strong> forget your passphrase! In case of loss you won't be able to access the data inside the container anymore!</p>

<p class="callout info"><strong>NOTE:</strong> Assigning labels to partitions creates unique nodes in <code>/dev/disk/by-label/</code>, thereby making them uniquely addressable and easy to discern.</p>

~~~bash
cryptsetup luksFormat --label cryptswap /dev/nvme0n1p2
cryptsetup luksFormat --label cryptroot /dev/nvme0n1p3
~~~

Open the newly created LUKS container and supply the passphrase you just set:

<p class="callout info"><strong>NOTE:</strong> <code>root</code> is used as an example here. It is the "mapper name" under which the opened LUKS container will be available at for the runtime of the system, in this example: <code>/dev/mapper/root</code>. You may use whatever name you like.</p>

~~~bash
cryptsetup open /dev/disk/by-label/cryptswap swap
cryptsetup open /dev/disk/by-label/cryptroot root
~~~

You can also pass additional parameters, e.g. for allowing TRIM on LUKS partitions (often referred to as "discards" in the general context of Linux filesystems):

<p class="callout warning"><strong>WARNING:</strong> Allowing TRIM on encrypted partitions has security implications. By allowing TRIM, it becomes possible to derive information about the disk's utilization from freed areas. If you need absolute confidentiality, do not enable TRIM!</p>

~~~bash
cryptsetup open /dev/disk/by-label/cryptswap swap --allow-discards
cryptsetup open /dev/disk/by-label/cryptroot root --allow-discards
~~~

To save the flags permanently, use the `--persistent` option. This eliminates the need to specify options manually or in configuration files, since they are stored in the header section of the LUKS container and applied automatically.

You can always update already opened LUKS containers' flags with `cryptsetup refresh`:

~~~bash
cryptsetup refresh swap --allow-discards --persistent
cryptsetup refresh root --allow-discards --persistent
~~~

### Formatting and mounting partitions

Create file systems for the ESP and the root file system:

~~~bash
mkfs.fat -F32 -n ESP /dev/nvme0n1p1
mkfs.btrfs --label root /dev/mapper/root
mkswap --label swap /dev/mapper/swap
~~~

Create btrfs subvolumes:

~~~bash
mount /dev/mapper/root /mnt
btrfs subvolume create /mnt/@
btrfs subvolume create /mnt/@home
~~~

Mark the root subvolume `@` as the default. This eliminates having to set it later as a kernel `rootflags` option.

Query the subvolumes, to get the subvolid:

~~~bash
btrfs subvolume list /mnt/
~~~

This lists all subvolumes on the partition with their IDs:

~~~
ID 256 gen 4839 top level 5 path @
ID 257 gen 4839 top level 5 path @home
~~~

The root subvolume `@` in this scenario has ID 256:

~~~bash
btrfs subvolume set-default 256 /mnt/
umount -R /mnt
~~~

Mount the file systems:

~~~bash
mount /dev/mapper/root -o noatime,compress=zstd,subvol=@ /mnt
mount --mkdir /dev/mapper/root -o noatime,compress=zstd,subvol=@home /mnt/home
mount --mkdir /dev/disk/by-label/ESP /mnt/efi
swapon /dev/mapper/swap
~~~

# Encrypt non-root devices (LUKS)

If you have more than one hard disk that you need to encrypt (e.g. SSD as main disk, HDD as data disk) there are a few things to keep in mind to ensure continued smooth operation without any loss of convenience.

The layout is as follows:

| Type             | File System | Description                                                      |
|------------------|-------------|------------------------------------------------------------------|
| Home File System | LUKS2       | Stores user home directories and personal files                  |

## Preparing the disk

Determine the disks that are installed on your system. This can easily be done with `fdisk`:

~~~sh
fdisk -l
~~~

Start partitioning the disk with `cfdisk`:

<p class="callout danger"><strong>WARNING:</strong> Make sure you are modifying the correct device, else you <em>will</em> lose data!</p>

~~~sh
cfdisk /dev/sda
~~~

If the disk has no partition table yet, `cfdisk` will ask you to specify one. The default partition table format for UEFI systems is `gpt`. Create a layout to your liking, e.g.:

| Size        | FS Type    |
|-------------|------------|
| (disk size) | Linux home |

## Formatting partitions

Before writing a file system to the disk a LUKS container needs to be created with the `cryptsetup` utility:

<p class="callout danger"><strong>WARNING:</strong> Do <strong>NOT</strong> forget your passphrase! In case of loss you won't be able to access the data inside the container anymore!</p>

<p class="callout info"><strong>NOTE:</strong> Using <code>/dev/sda</code> as an example of a SATA HDD that is intended to be mounted at <code>/home</code>.</p>

~~~bash
cryptsetup luksFormat --label crypthome /dev/sda1
~~~

Open the newly created LUKS container and supply the passphrase you just set:

<p class="callout info"><strong>NOTE:</strong> If you want to enable TRIM support you can use <code>cryptsetup open --allow-discards &lt;device&gt; &lt;mappername&gt;</code>; this comes at a slight security/confidentiality penalty, as it allows insights into file system structures by monitoring which parts were deleted. Additionally passing <code>--persistent</code> will add it to the flags section of the LUKS metadata and apply the flag automatically on every <code>cryptsetup</code> operation.</p>

~~~bash
cryptsetup open /dev/disk/by-label/crypthome home
~~~

Create a file system for the home file system:

~~~bash
mkfs.ext4 -L home /dev/mapper/home
~~~

Mount the file systems:

~~~bash
mount --mkdir /dev/mapper/home -o noatime /mnt/home
~~~

# LVM + dm-cache (unencrypted)

LVM dm-cache is a feature of the Linux device mapper, which uses a fast storage device to boost data read/write speeds of a slower one. It achieves this by transparently copying blocks of frequently accessed data to the faster storage device in the background. On subsequent reads/writes the faster storage device is queried first. If the requested data blocks are not on there, it automatically falls back on the slower source storage device.

This makes it possible to combine the benefits of SSD speeds with the low cost and high storage capacity of HDDs, when comparable pure SSD-based storage with the same capacity is too expensive or otherwise unavailable.

<p class="callout info"><strong>NOTE:</strong> This partition scheme is tailored towards a desktop computer setup with enough RAM and no SWAP (and therefore no hibernate/suspend-to-disk support).</p>

<p class="callout warning"><strong>CAUTION:</strong> This setup does <strong>NOT</strong> utilize LUKS disk encryption.</p>

This guide assumes the following:
* `/dev/nvme0n1` is the primary disk (cache device)
* `/dev/sda` is the secondary disk (origin device)

## Nomenclature

| Term                 | Description                                                                                                            |
|----------------------|------------------------------------------------------------------------------------------------------------------------|
| Physical Volume (PV) | On-disk partitioning format to be combined in a VG to a common storage pool                                            |
| Volume Group (VG)    | Grouping of one or more PVs to provide a combined storage pool from which storage can be requested in the form of LVs. |
| Logical Volume (LV)  | Logical partition format which can be accessed like a block device to hold file systems and data.                      |

## Preparing the cache device

First the available disks need to be determined. This can easily be achieved with `fdisk`:

~~~bash
fdisk -l
~~~

To start the actual partitioning process start `cfdisk` and point it to the **disk** you wish to partition:

<p class="callout danger"><strong>WARNING:</strong> Make sure to select your actually desired device!</p>
    
~~~bash
cfdisk /dev/nvme0n1
~~~

Partition the disk in the following way:

| FS Type | Size        | Mount Point | Comment    |
|---------|-------------|-------------|------------|
| vfat    | 1G          | /boot       | EFI System |
| LVM     | (remaining) |             | Linux LVM  |

## Preparing the origin device

Partition the disk by starting `cfdisk` and pointing it to the **disk** for the origin device:

<p class="callout danger"><strong>WARNING:</strong> Make sure to select your actually desired device!</p>

~~~bash
cfdisk /dev/sda
~~~

Partition the disk in the following way:

| FS Type | Size  | Mount Point | Comment   |
|---------|-------|-------------|-----------|
| LVM     | (all) |             | Linux LVM |

## Creating physical volumes, volume group and logical volumes

To create physical volumes as the basis for the LVM setup, use `pvcreate` and point it to the **partitions** you created in the two previous steps:

~~~bash
pvcreate /dev/nvme0n1p2   # SSD
pvcreate /dev/sda1        # HDD
~~~

Continue by creating a volume group with `vgcreate` that spans both physical volumes you just created:

<p class="callout info"><strong>NOTE:</strong> <code>vg0</code> is used as an example here. Use whatever you like.</p>

~~~bash
vgcreate vg0 /dev/nvme0n1p2 /dev/sda1
~~~

Next, create logical volumes inside the volume group with `lvcreate`, using 100% of the available space on the HDD and specifying the cache pool on the SSD:

~~~bash
lvcreate -l 100%FREE -n lv_root vg0 /dev/sda1
lvcreate --type cache-pool -n lv_cache -l 100%FREE vg0 /dev/nvme0n1p2
~~~

Finally, link the cache pool to the origin device with `lvconvert`:

~~~bash
lvconvert --type cache --cachepool vg0/lv_cache vg0/lv_root
~~~

## Formatting devices

Format the partitions with the appropriate `mkfs` subcommand:

~~~bash
mkfs.fat -F 32 /dev/nvme0n1p1        # EFI System Partition
mkfs.btrfs /dev/mapper/vg0-lv_root   # Btrfs root file system
~~~

Mount the root Btrfs file system:

~~~bash
mount /dev/mapper/vg0-lv_root /mnt
~~~

Next, create the subvolumes with the `btrfs` user space tools:

~~~bash
btrfs subvolume create /mnt/@
btrfs subvolume create /mnt/@home
~~~

Unmount the root file system again:

~~~bash
umount -R /mnt
~~~

Mount the `@` subvolume at `/mnt`:

~~~bash
mount /dev/mapper/vg0-lv_root -o noatime,compress-force=zstd,space_cache=v2,subvol=@ /mnt
~~~

Create directories for subsequent mount points:

~~~bash
mkdir -p /mnt/{boot,home}
~~~

Mount the remaining file systems:

~~~bash
mount /dev/nvme0n1p1 /mnt/boot
mount /dev/mapper/vg0-lv_root -o noatime,compress-force=zstd,space_cache=v2,subvol=@home /mnt/home
~~~

# LVM on LUKS (encrypted, Laptop)

LUKS (Linux Unified Key Setup) is the standard for Linux hard disk encryption. By providing a standard on-disk-format, it does not only facilitate compatibility among distributions, but also provides secure management of multiple user passwords. LUKS stores all necessary setup information in the partition header, enabling to transport or migrate data seamlessly.

Management of LUKS encrypted devices is done via the [`cryptsetup`](https://gitlab.com/cryptsetup/cryptsetup) utility.

## Nomenclature

| Term                 | Description                                                                                                            |
|----------------------|------------------------------------------------------------------------------------------------------------------------|
| Physical Volume (PV) | On-disk partitioning format to be combined in a VG to a common storage pool                                            |
| Volume Group (VG)    | Grouping of one or more PVs to provide a combined storage pool from which storage can be requested in the form of LVs. |
| Logical Volume (LV)  | Logical partition format which can be accessed like a block device to hold file systems and data.                      |

## Partitioning Setup

<div class="callout info">
  <p><strong>NOTE:</strong> This partitioning scheme does <strong>NOT</strong> include an LVM cache device.</p>
  <p>While it is technically possible to add an LVM cache device to this setup, it is not advised to do so, <em><strong>as this will leak plain text contents of the unlocked LUKS container into the cache,</strong></em> which can be read in a hex editor by opening the raw device file directly — entirely defeating the purpose of encrypting the disk!</p>
  <p>A <a href="https://wiki.sebin-nyshkim.net/books/arch-linux/page/partitioning-luks-on-lvm">LUKS on LVM</a> setup is recommended instead.</p>
</div>

LVM on LUKS has the benefit of being able to encrypt an entire drive (useful for laptops with encrypted swap for resume) while only needing to provide a single passphrase to unlock it entirely for simplicity.

However, since the LVM container resides inside the LUKS container it cannot span multiple disks, as it is confined by the boundaries by the parent LUKS container.

This guide assumes the following:
* This is used on a laptop computer with resume capabilities (Swap partition)
* There is only one drive: `/dev/nvme0n1`
* The root file system will be btrfs, with subvolumes for `/` and `/home`
* To tighten security, this setup assumes a [unified kernel image](https://wiki.archlinux.org/title/Systemd-boot#Preparing_a_unified_kernel_image) and booting via [EFISTUB](https://wiki.archlinux.org/title/EFISTUB), with the _ESP_ mounted at `/efi`. [Extra](/books/arch-linux/page/boot-loader) [steps](/books/arch-linux/page/secure-boot) will be necessary to make the machine bootable.

### Preparing the drive
1. List available disks
    ~~~bash
    fdisk -l
    ~~~
1. Start partitionaing tool for primary disk _(`cfdisk` is a little easier to use as it has a nice TUI)_ 
    <p class="callout danger"><strong>WARNING:</strong> Make sure to select your actually desired device!</p>
    
    ~~~bash
    cfdisk /dev/nvme0n1
    ~~~
1. Partition with the following scheme

| FS Type | Size        | Mount Point | Comment           |
|---------|-------------|-------------|-------------------|
| vfat    | 1G          | `/efi`      | EFI System        |
| LUKS    | (remaining) |             | Linux file system |

### Creating the LUKS container
1. Create the LUKS container and enter a passphrase
    <p class="callout danger"><strong>WARNING:</strong> Do <strong>NOT</strong> forget your passphrase! In case of loss you won't be able to access the data inside the container anymore!</p>

    ~~~bash
    cryptsetup luksFormat /dev/nvme0n1p2
    ~~~
1. Open the newly created LUKS container
    <p class="callout info"><strong>NOTE:</strong> <code>cryptlvm</code> is used as an example here. Use whatever you like.</p>

    ~~~bash
    cryptsetup open /dev/nvme0n1p2 cryptlvm   
    ~~~

### Creating LVM inside the LUKS container
1. Create an LVM physical volume inside LUKS container
    ~~~bash
    pvcreate /dev/mapper/cryptlvm
    ~~~
1. Create the volume group:
    ~~~bash
    vgcreate vg0 /dev/mapper/cryptlvm
    ~~~
1. Create the logical volumes
    <p class="callout info"><strong>NOTE:</strong> When using resume, make <code>lv_swap</code> as large as RAM. In this example the machine has <strong>16 GB</strong> of RAM.</p>

    ~~~bash
    lvcreate -L 16G -n lv_swap vg0       # Swap as big as RAM (16 GB)
    lvcreate -l 100%FREE -n lv_root vg0  # Root file system
    ~~~

### Formatting devices
1. Create partitions
    ~~~bash
    mkfs.fat -F 32 /dev/nvme0n1p1        # EFI System Partition
    mkfs.btrfs /dev/mapper/vg0-lv_root   # Btrfs root volume
    mkswap /dev/mapper/vg0-lv_swap       # Swap space
    ~~~
1. Create Btrfs subvolumes
    ~~~bash
    # First, mount the root file system
    mount /dev/mapper/vg0-lv_root /mnt
    
    # Create subvolumes
    btrfs subvolume create /mnt/@
    btrfs subvolume create /mnt/@home
    ~~~
1. Mount partitions
    ~~~bash
    # Unmount the root file system
    umount -R /mnt

    # Mount the @ subvolume
    mount /dev/mapper/vg0-lv_root -o noatime,compress-force=zstd,space_cache=v2,subvol=@ /mnt

    # Create mountpoints
    mkdir -p /mnt/{efi,home}

    # Mount the remaining partitions/subvolumes
    mount /dev/nvme0n1p1 /mnt/efi
    mount /dev/mapper/vg0-lv_root -o noatime,compress-force=zstd,space_cache=v2,subvol=@home /mnt/home
    
    # Activate swap
    swapon /dev/mapper/vg0-lv_swap
    ~~~

# LUKS on LVM (encrypted, cached, Desktop)

LUKS (Linux Unified Key Setup) is the standard for Linux hard disk encryption. By providing a standard on-disk-format, it does not only facilitate compatibility among distributions, but also provides secure management of multiple user passwords. LUKS stores all necessary setup information in the partition header, enabling to transport or migrate data seamlessly.

Management of LUKS encrypted devices is done via the [`cryptsetup`](https://gitlab.com/cryptsetup/cryptsetup) utility.

## Nomenclature

| Term                 | Description                                                                                                            |
|----------------------|------------------------------------------------------------------------------------------------------------------------|
| Physical Volume (PV) | On-disk partitioning format to be combined in a VG to a common storage pool                                            |
| Volume Group (VG)    | Grouping of one or more PVs to provide a combined storage pool from which storage can be requested in the form of LVs. |
| Logical Volume (LV)  | Logical partition format which can be accessed like a block device to hold file systems and data.                      |
| Cache device         | Fast storage used for caching reads/writes to slow storage                                                             |
| Origin device        | Slow primary storage holding the actual data                                                                           |

## Partitioning Setup

LUKS on LVM has the benefit of a LUKS container being able to span multiple disks, thanks to the machanisms of the underlying LVM. This, however, comes with the downside that if you want to have multiple volumes (e.g. for your root volume and a separate home volume or encrypted SWAP) you will have to take extra steps to unlock these volumes during the boot process.

<p class="callout info"><strong>NOTE:</strong> If you want to utilize LVM cache this is the desired partioning scheme to use, as the encrypted LUKS container will reside inside an LVM LV and the LVM caching mechanism will cache the LV instead of the unlocked LUKS container, thus not leaking any secrets into the cache.</p>

This guide assumes the following:

* This is used on a desktop computer without the need to resume (no SWAP partition)
* There are multiple drives: `/dev/nvme0n1` (SSD) and `/dev/sda` (HDD)
* The HDD will be cached by the SSD
* The root file system will be btrfs, with subvolumes for `/` and `/home`
* To tighten security, this setup assumes a [unified kernel image](https://wiki.archlinux.org/title/Systemd-boot#Preparing_a_unified_kernel_image) and booting via [EFISTUB](https://wiki.archlinux.org/title/EFISTUB), with the _ESP_ mounted at `/efi`. [Extra](/books/arch-linux/page/boot-loader) [steps](/books/arch-linux/page/secure-boot) will be necessary to make the machine bootable.

### Preparing partition layout

Start by listing available disks:

~~~bash
fdisk -l
~~~

Create a partition layout with `cfdisk` by pointing it to the first disk, e.g. `/dev/nvme0n1`:

<p class="callout warning"><strong>ATTENTION:</strong> <code>cfdisk</code> expects a device file, not a partition.</p>

~~~bash
cfdisk /dev/nvme0n1
~~~

If `cfdisk` asks you about the partition table scheme to use, select `gpt`.

Create the following partition layout:

| FS Type | Size        | Mount Point | Comment    |
|---------|-------------|-------------|------------|
| vfat    | 1G          | /efi        | EFI System |
| LVM     | (remaining) |             | Linux LVM  |

Start `cfdisk` for the second disk, e.g. `/dev/sda`:

~~~bash
cfdisk /dev/sda
~~~

Create the following partition layout:

| FS Type | Size  | Mount Point | Comment   |
|---------|-------|-------------|-----------|
| LVM     | (all) |             | Linux LVM |

### Setting up LVM

Start by creating LVM PVs on the partitions we just laid out:

~~~bash
pvcreate /dev/nvme0n1p2   # SSD
pvcreate /dev/sda1        # HDD
~~~

Next, create a VG spanning both PVs:

<p class="callout info"><strong>NOTE:</strong> <code>vg0</code> is used as an example here. Name your VG whatever you like.</p>

~~~bash
vgcreate vg0 /dev/nvme0n1p2 /dev/sda1
~~~

Create an LV inside `vg0`, using 100% of the available space on the PV at `/dev/sda1` and label it `lv_root`:

~~~bash
lvcreate -l 100%FREE -n lv_root vg0 /dev/sda1
~~~

Create an LV inside `vg0`, using 100% of the available space on the PV at `/dev/nvme0n1p2` and label it `lv_cache`:

~~~bash
lvcreate -l 100%FREE -n lv_cache --type cache-pool vg0 /dev/nvme0n1p2
~~~

Finally, link both LVs together so that the LV on the HDD is being cached by the pool on the SSD:

~~~bash
lvconvert --type cache --cachepool vg0/lv_cache vg0/lv_root
~~~

### Creating the LUKS container

Create the LUKS container inside the LV of the origin device:

<p class="callout danger"><strong>WARNING:</strong> Do <strong>NOT</strong> forget your passphrase! In case of loss you won't be able to access the data inside the container anymore!</p>

~~~bash
cryptsetup luksFormat /dev/mapper/vg0-lv_root
~~~

Open the newly created LUKS container and supply the passphrase you just set:

<p class="callout info"><strong>NOTE:</strong> <code>cryptroot</code> is used as an example here. Use whatever you like.</p>

~~~bash
cryptsetup open /dev/mapper/vg0-lv_root cryptroot
~~~

### Formatting and mounting partitions

Create file systems for the ESP and the root file system:

~~~bash
mkfs.fat -F 32 /dev/nvme0n1p1
mkfs.btrfs /dev/mapper/cryptroot
~~~

Mount the root btrfs file system and create the subvolumes:

~~~bash
mount /dev/mapper/cryptroot /mnt

btrfs subvolume create /mnt/@
btrfs subvolume create /mnt/@home
~~~

Unmount the root btrfs file system:

~~~bash
umount -R /mnt
~~~

Mount the `@` subvolume:

~~~bash
mount /dev/mapper/cryptroot -o noatime,compress-force=zstd,space_cache=v2,subvol=@ /mnt
~~~

Create mount points for `/efi` and `/home`:

~~~bash
mkdir -p /mnt/{efi,home}
~~~

Mount the remaining partitions and subvolumes:

~~~bash
mount /dev/nvme0n1p1 /mnt/efi
mount /dev/mapper/cryptroot -o noatime,compress-force=zstd,space_cache=v2,subvol=@home /mnt/home
~~~