Use cases
This page collects practical configuration patterns. Each snippet is a complete mounts: block — paste it into /etc/pfs/pfs.yaml (replacing the example that ships with the package) and adjust the paths to match your disks.
# /etc/pfs/pfs.yaml — paste one of the configs below here
fuse:
allow_other: true # required if Plex/Jellyfin run as a different user
mounts:
# ... paste a use-case config here
1) Media library (SSD for fast writes, HDD for capacity)
Goal: Put new writes on SSDs, keep reads/listings unified, then move older files to HDDs.
About indexed storage
indexed: true is optional and only affects metadata operations (like directory listing). File content reads still come from the owning disk, and maintenance jobs will spin disks by design.
Recommended folder layout
Keep the same relative layout on every disk:
/mnt/ssd1/media/ingest/...
/mnt/ssd2/media/ingest/...
/mnt/ssd1/media/library/{movies,tv,music}/...
/mnt/ssd2/media/library/{movies,tv,music}/...
/mnt/hdd1/media/library/{movies,tv,music}/...
/mnt/hdd2/media/library/{movies,tv,music}/...
/mnt/hdd3/media/library/{movies,tv,music}/...
This pairs well with path_preserving: true.
Suggested config:
mounts:
media:
storage_paths:
- { id: ssd1, path: /mnt/ssd1/media, indexed: false }
- { id: ssd2, path: /mnt/ssd2/media, indexed: false }
- { id: hdd1, path: /mnt/hdd1/media, indexed: true }
- { id: hdd2, path: /mnt/hdd2/media, indexed: true }
- { id: hdd3, path: /mnt/hdd3/media, indexed: true }
storage_groups:
ssds: [ssd1, ssd2]
hdds: [hdd1, hdd2, hdd3]
routing_rules:
- match: 'library/music/**'
targets: [ssds]
write_policy: most_free
path_preserving: true
- match: 'library/{movies,tv}/**'
read_targets: [ssds, hdds]
write_targets: [ssds]
write_policy: most_free
path_preserving: true
- match: 'ingest/**'
read_targets: [ssds, hdds]
write_targets: [ssds]
write_policy: most_free
path_preserving: true
- match: '**'
read_targets: [ssds, hdds]
write_targets: [ssds]
write_policy: most_free
path_preserving: true
mover:
enabled: true
jobs:
- name: ssd-to-hdd
trigger:
type: usage
threshold_start: 80 # start moving when any source disk reaches 80% full
threshold_stop: 70 # stop moving when all source disks are below 70% full
source:
groups: [ssds]
patterns: ['library/{movies,tv}/**']
destination:
groups: [hdds]
policy: most_free
path_preserving: true
conditions:
min_age: 24h
Tips: Mark HDDs as indexed: true to reduce metadata disk touches. Use mover thresholds so the SSDs do not fill up.
2) NVR / CCTV storage (tiered retention)
Goal: Write new footage to SSD (IOPS), then archive to HDD after a minimum age.
Suggested config:
mounts:
nvr:
storage_paths:
- { id: ssd1, path: /mnt/ssd1/nvr, indexed: false }
- { id: ssd2, path: /mnt/ssd2/nvr, indexed: false }
- { id: hdd1, path: /mnt/hdd1/nvr, indexed: false }
- { id: hdd2, path: /mnt/hdd2/nvr, indexed: false }
storage_groups:
ssds: [ssd1, ssd2]
hdds: [hdd1, hdd2]
routing_rules:
- match: 'clips/**'
targets: [ssds]
write_policy: most_free
path_preserving: true
- match: 'exports/**'
targets: [ssds]
write_policy: most_free
path_preserving: true
- match: 'recordings/**'
read_targets: [ssds, hdds]
write_targets: [ssds]
write_policy: most_free
path_preserving: true
- match: '**'
read_targets: [ssds, hdds]
write_targets: [ssds]
write_policy: most_free
path_preserving: true
mover:
enabled: true
jobs:
- name: archive-footage
trigger:
type: manual
source:
groups: [ssds]
patterns: ['recordings/**']
destination:
groups: [hdds]
policy: most_free
path_preserving: true
conditions:
min_age: 24h
Tips: Start with trigger.type: manual and run pfs move <mount> --job archive-footage from a timer you control (e.g., systemd timer, cron, etc.).
3) Mergerfs alternative (simplest starting point)
Goal: Present multiple disks as a single mountpoint, while distributing writes. If you're new to PolicyFS, start here — no indexing, no tiering, just pooling.
Suggested config:
mounts:
media:
storage_paths:
- { id: d1, path: /mnt/disk1/media, indexed: false }
- { id: d2, path: /mnt/disk2/media, indexed: false }
- { id: d3, path: /mnt/disk3/media, indexed: false }
storage_groups:
disks: [d1, d2, d3]
routing_rules:
- match: '**'
read_targets: [disks]
write_targets: [disks]
write_policy: most_free
path_preserving: true