Skip to content

🗄️ Storage & File Systems

Current Use-Case: Maximum Capacity & Flexibility over Redundancy (RAID-0 style).

Mechanism: LVM (Logical Volume Manager) + Atomic Moves.

Instead of traditional hardware RAID, I utilize LVM to aggregate 2 x physical NVMe drives into a single, resizeable storage pool + a 500GB SSD as a backup disk.


Physical Layout

My current system manages three distinct storage tiers:

Drive Type Capacity Role
NVMe 1 M.2 NVMe 1 TB Host OS + LVM Pool Member
NVMe 2 M.2 NVMe 1 TB LVM Pool Member
SSD 1 SATA SSD 500 GB "VM Lab/Backups" (Crucial SSD). Dedicated playground for VMs and Dockerapps Backups

Logical Volume Management (LVM)

I aggregate the 2 x NVMe drives into a single Volume Group (vg_pool01). This allows me to create Logical Volumes (LVs) that span across physical disks, creating a unified namespace.

Volume Group: vg_pool01

LV Name Size Mount Point File System Purpose
lv_dockerapps 60 GB /mnt/pool01/homelab/services ext4 Stores Compose files, SQLite databases, and configs.
lv_games 300 GB /mnt/pool01/games ext4 Steam/Lutris/Heroic libraries. Separated to prevent fragmentation of media files.
lv_media ~1.6 TB /mnt/pool01/media ext4 Stores downloads and library content. Enables Atomic Moves.

Why LVM

  • Flexibility: If lv_dockerapps runs out of space, I can shrink lv_games and extend lv_dockerapps on the fly without rebooting.

  • Expansion: When I add a SATA HDD later, I can simply add it to vg_pool01 and extend lv_media to consume it.


The "Atomic Move" Architecture

This is the single most critical concept for the Media Stack. To prevent double-copying files (which wastes space and IOPS), we utilize Hardlinks.

Requirement: The source (Downloads) and destination (Library) must be on the same physical filesystem.

  • The Setup:

    • We mount /mnt/pool01/media to /media inside all containers.
  • The Flow:

    1. qBittorrent writes to /media/downloads/file.mkv.
    2. Radarr imports to /media/movies/file.mkv.
    3. Linux sees both paths share the same Inode.
    4. Result: The move is instant (Atomic) and consumes 0 bytes of extra space.

See Story Mode for the technical walkthrough.

Monitoring Integration with Beszel

Monitoring LVM volumes inside Docker is tricky. Standard mounts report Storage Usage correctly, but fail to report Disk I/O (Read/Write speeds) because Docker abstracts the underlying device.

To fix this, we map the specific Device Mappers into the Beszel Agent.

Volume Kernel Device Beszel Mount
lv_dockerapps /dev/dm-0 /extra-filesystems/dm-0__DockerApps
lv_media /dev/dm-2 /extra-filesystems/dm-2__Media

Warning: Kernel device IDs (dm-0) can change if LVs are created in a different order on a new OS. Always verify with ls -l /dev/mapper before deployment.*

Mounting & Persistence (/etc/fstab)

The system mounts volumes at boot. We use nofail to ensure the OS still boots even if a drive dies.

# /etc/fstab
# LVM Logical Volumes
UUID=<UUID_DOCKERAPPS>  /mnt/pool01/homelab/services  ext4  defaults,nofail  0  2
UUID=<UUID_MEDIA>       /mnt/pool01/media       ext4  defaults,nofail  0  2
UUID=<UUID_GAMES>       /mnt/pool01/games       ext4  defaults,nofail  0  2

# Backup Drive Crucial SSD
UUID=<UUID_CRUCIAL>     /mnt/Crucial500           ext4  defaults,nofail  0  2

Permissions Architecture

To avoid permission hell ("Access Denied" in Docker), all storage tiers are owned by the primary user (UID 1000), matching the PUID environment variable in all containers.

# Apply Ownership
sudo chown -R 1000:1000 /mnt/pool01
sudo chown -R 1000:1000 /mnt/Crucial500

# Apply Directory Permissions (775 = User/Group Write, Others Read)
sudo chmod -R 775 /mnt/pool01