🗄️ 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_dockerappsruns out of space, I can shrinklv_gamesand extendlv_dockerappson the fly without rebooting. -
Expansion: When I add a SATA HDD later, I can simply add it to
vg_pool01and extendlv_mediato 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/mediato/mediainside all containers.
- We mount
-
The Flow:
- qBittorrent writes to
/media/downloads/file.mkv. - Radarr imports to
/media/movies/file.mkv. - Linux sees both paths share the same Inode.
- Result: The move is instant (Atomic) and consumes 0 bytes of extra space.
- qBittorrent writes to
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 withls -l /dev/mapperbefore 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