Skip to content

Storage Layout

Independent data disks unioned by mergerfs, protected by a single SnapRAID parity disk, with an NVMe cache for appdata and in-progress usenet downloads. SABnzbd stages incomplete/ on the NVMe (/mnt/cache/data/usenet) so par2/unpack I/O stays off the spinning disks; post-processing then writes the finished file straight to the array, so there is no mover and none is needed. Completed downloads and all torrents live on the array: SnapRAID has no realtime parity penalty, the data disks stay spinning anyway, and torrents need to share a filesystem with the media for hardlinks. Torrents seed indefinitely, so they can never stage on cache.

Sanitized: serial numbers and filesystem UUIDs are intentionally not committed. Disks are addressed by /dev/disk/by-id/ (model + serial) on the host; the real IDs live in the host's /etc/fstab and snapraid.conf, not in this repo.

Disks

Role Capacity Model Filesystem Mount
parity 28 TB Seagate Exos XFS holds the SnapRAID parity file
disk1 28 TB Seagate Exos XFS /mnt/disk1
disk2 28 TB Seagate Exos XFS /mnt/disk2
disk3 12 TB Seagate Exos XFS /mnt/disk3
disk4 12 TB Seagate Exos XFS /mnt/disk4
disk5 12 TB Seagate Exos XFS /mnt/disk5
cache 2 TB Samsung NVMe btrfs /mnt/cache
  • The five data disks (/mnt/disk1../mnt/disk5) are unioned by mergerfs into /mnt/user.
  • The parity disk sits outside the mergerfs pool. Its capacity must be at least as large as the largest data disk (so 28 TB here). It is touched only by writes and snapraid sync/scrub, never by reads, which is why it is the one disk that spins down.
  • The cache is not part of the parity set; appdata is backed up separately.
  • The cache NVMe is the reused Unraid cache pool (single-device btrfs): it is mounted as-is on Debian, never reformatted, so appdata carries over on the device itself. It also holds Kometa assets. The Docker data root is not here: it stayed at the default /var/lib/docker on the OS SSD (see host-facts.md §Docker; the Unraid-era system/docker share is gone). The Debian OS lives on its own SSD, outside this layout.

Addressing convention

Always reference disks by /dev/disk/by-id/ rather than /dev/sdX, because sdX names are not stable across reboots. Find them with ls -l /dev/disk/by-id/.

Operational notes

  • SATA link power management is forced to max_performance by the host hardening config to avoid link resets on the SATA data disks.
  • Historical disk3/disk5 ATA read timeout/link reset events observed during the Debian cutover period were investigated and treated as resolved. Future hard resetting link or kernel I/O errors for those disks should be handled as a recurrence, not as an open known issue.

Templated /etc/fstab (data disks)

Replace each REPLACE-WITH-BY-ID with the real /dev/disk/by-id/ata-... entry on the host.

# data disks (XFS), by-id, nofail so a missing disk does not block boot
/dev/disk/by-id/REPLACE-WITH-BY-ID-1  /mnt/disk1  xfs  defaults,nofail  0 0
/dev/disk/by-id/REPLACE-WITH-BY-ID-2  /mnt/disk2  xfs  defaults,nofail  0 0
/dev/disk/by-id/REPLACE-WITH-BY-ID-3  /mnt/disk3  xfs  defaults,nofail  0 0
/dev/disk/by-id/REPLACE-WITH-BY-ID-4  /mnt/disk4  xfs  defaults,nofail  0 0
/dev/disk/by-id/REPLACE-WITH-BY-ID-5  /mnt/disk5  xfs  defaults,nofail  0 0
# parity disk (XFS)
/dev/disk/by-id/REPLACE-WITH-BY-ID-P  /mnt/parity xfs  defaults,nofail  0 0
# cache (btrfs)
/dev/disk/by-id/REPLACE-WITH-BY-ID-C  /mnt/cache  btrfs defaults,nofail 0 0

# mergerfs pool over the data disks
# (no use_ino/nonempty: both are deprecated in modern mergerfs (>=2.40 on Debian 13).
# use_ino is superseded by inodecalc, nonempty does nothing; per upstream docs
# "they should not be used". These exact options are confirmed working on the live host.)
# x-systemd.requires-mounts-for: without it the pool can mount BEFORE the branch
# disks at boot (the glob happily matches the empty mountpoint dirs) and present
# an empty pool. nofail keeps a dead disk from blocking boot, so ordering must be
# stated explicitly.
/mnt/disk*  /mnt/user  fuse.mergerfs  allow_other,category.create=mfs,minfreespace=200G,fsname=mergerfs,nofail,x-systemd.requires-mounts-for=/mnt/disk1,x-systemd.requires-mounts-for=/mnt/disk2,x-systemd.requires-mounts-for=/mnt/disk3,x-systemd.requires-mounts-for=/mnt/disk4,x-systemd.requires-mounts-for=/mnt/disk5  0 0

Templated snapraid.conf (skeleton)

A reference skeleton. Match content-file locations and exclusions to the live host. See man snapraid.

# one parity disk protects the data disks
parity /mnt/parity/snapraid.parity

# content files: keep several copies on different disks
content /var/snapraid/snapraid.content
content /mnt/disk1/.snapraid.content
content /mnt/disk3/.snapraid.content

# data disks (the mergerfs branches)
data disk1 /mnt/disk1
data disk2 /mnt/disk2
data disk3 /mnt/disk3
data disk4 /mnt/disk4
data disk5 /mnt/disk5

# exclusions
exclude *.unrecoverable
exclude /tmp/
exclude lost+found/
exclude .Trash-*/
exclude incomplete/