Skip to content

MergerFS Configuration Guide


Overview

MergerFS is a union filesystem designed to simplify storage management. It pools multiple storage devices (branches) into a single unified directory (mount point).

Unlike RAID, MergerFS does not stripe data across drives. Files exist intact on the individual backing drives.

  • Flexibility: Drives of different sizes, filesystems, and speeds can be mixed.
  • Resilience: If one drive fails, only the data on that specific drive is lost; the rest of the pool remains accessible.
  • Dynamic: Drives can be added or removed from the pool without rebuilding the array.

Installation

Debian 13 ships mergerfs 2.40 in its main repo, which is what my NAS runs. Upstream publishes newer .deb builds on GitHub if you need a feature from a later release.

# Standard installation (Debian 13)
sudo apt install mergerfs
mergerfs --version

For latest versions/source: GitHub Releases


Branch Setup & Preparation

Important

Never write data directly to the /mnt/disk1 folders if you can help it. Always write to /mnt/storage. If you modify files on the backing disks directly while MergerFS is running, you might see cache inconsistencies until you remount.

Before creating the pool, the underlying mount points (branches) should be organized and secured.

1. Naming Convention

Using a consistent naming structure helps identify drive types within the pool.

  • HDDs: /mnt/hdd/Serial-Number
  • SATA SSDs: /mnt/ssd/Serial-Number
  • NVMe: /mnt/nvme/Serial-Number
  • Remote Shares: /mnt/remote/Share-Name

Example Layout:

/mnt/
├── hdd/
│   ├── 10T-01234567
│   └── 20T-12345678
└── nvme/
    └── 1T-ABCDEFGH

2. Permissions & Locking (Critical)

mount points

To ensure the directory is only used as a point to mount another filesystem it is good to lock it down as much as possible. Be sure to do this before mounting a filesystem to it.

Run these commands for each underlying drive mount point:

# 1. Set ownership to root
sudo chown root:root /mnt/hdd/10T-XYZ

# 2. Remove read/write/execute permissions (makes it inaccessible directly)
sudo chmod 0000 /mnt/hdd/10T-XYZ

# 3. Mark as a specific branch mount (prevents mounting if drive is missing)
sudo setfattr -n user.mergerfs.branch_mounts_here /mnt/hdd/10T-XYZ

The extended attribute user.mergerfs.branch_mounts_here is used by the branches-mount-timeout option to recognize whether or not a mergerfs branch path points to the intended filesystem.

The chattr is likely to only work on EXT{2,3,4} filesystems but will restrict even root from modifying the directory or its content but is still able to be a mount point.

mounted filesystems

For those new to Linux, intending to be the primary individual logged into the system, or simply want to simplify permissions it is recommended to set the root of mounted filesystems like /tmp/ is set to. Owned by root, ugo+rwx and sticky bit set.

This must be done after mounting the filesystem to the target mount point.

$ sudo chown root:root /mnt/hdd/10T-SERIALNUM
$ sudo chmod 1777 /mnt/hdd/10T-SERIALNUM
$ sudo setfattr -n user.mergerfs.branch /mnt/hdd/10T-SERIALNUM

Pool Configuration

1. Create Pool Directory

Info

mergerfs quick-start settings

-cache.files=off

-category.create=pfrd

-func.getattr=newest

-dropcacheonclose=false

Create the unified mount point where applications will access the data.

sudo mkdir -p /mnt/storage

2. Command Line Test

Test the pool creation manually before making it persistent.

Note

These settings are just what I used. Your situation may benefit from different layout. Read the docs to see what would work best.

sudo mergerfs -o cache.files=per-process,cache.files.process-names='rtorrent|qbittorrent-nox|TorrentCreation',category.create=mspmfs,func.getattr=newest,dropcacheonclose=false,minfreespace=100G,moveonenospc=true /mnt/disk1:/mnt/disk2 /mnt/storage
sudo umount /mnt/storage   # done testing

3. Persistence (/etc/fstab)

Add the configuration to /etc/fstab to ensure the pool mounts at boot.

The options my pool runs with:

Option Why
cache.files=per-process + cache.files.process-names (the torrent client's process names, see the fstab line) Page caching only for the torrent client. Torrent clients use mmap, which needs the page cache; with cache.files=off they fail on the pool. Everything else (Plex, the *arrs, Samba) gets uncached I/O, so RAM isn't spent caching the same media twice. rtorrent and qbittorrent-nox are mergerfs's default list; TorrentCreation adds qBittorrent's "create torrent" job, which runs under its own process name.
category.create=mspmfs Most Shared Path, Most Free Space. Of the branches that already have the file's parent directory, write to the one with the most free space (falls back to most-free-space if the path is new). Keeps a show or album together on one disk, which keeps a disk failure's damage tidy.
func.getattr=newest Returns file attributes from the file with the newest mtime. Critical for apps like Plex/Kodi to detect changes.
dropcacheonclose=false Only matters with caching on; leave it off for the torrent client's sake.
minfreespace=100G A branch with less than 100 GB free gets no new files. When every branch is below it, new files fail, so alert on the pool's free space (see below).
moveonenospc=true If a write hits "no space" on one branch, move the file to a branch with room and carry on.
fsname=mergerfs Shows up as mergerfs in df and findmnt.

Add to /etc/fstab:

# <branches> <mountpoint> <type> <options> <dump> <pass>
/mnt/disk1:/mnt/disk2 /mnt/storage fuse.mergerfs cache.files=per-process,cache.files.process-names=rtorrent|qbittorrent-nox|TorrentCreation,category.create=mspmfs,func.getattr=newest,dropcacheonclose=false,minfreespace=100G,moveonenospc=true,fsname=mergerfs 0 0

Explicit branches vs a glob

/mnt/disk* works when the parity disk is mounted somewhere else (/mnt/parity1). If you mount every HDD under one directory (for example /mnt/hdd/<serial>), list the data branches explicitly, separated by :, or the parity disk ends up in the pool.

4. Verification

Reload the system daemon and mount the pool.

# Verify syntax
sudo findmnt --verify

# Reload fstab
sudo systemctl daemon-reload

# Mount all
sudo mount -a

# Check size (Should equal sum of all drives)
df -h

Docker Integration

When configuring containers, always use the unified /mnt/storage path. Never bind volumes to the individual disks (e.g., /mnt/disk1), as this bypasses MergerFS logic.

Example compose.yaml:

services:
  plex:
    image: linuxserver/plex   # pin a version tag in production
    volumes:
      # Correct: Uses the pool
      - /mnt/storage/media/movies:/movies
      - /mnt/storage/media/tv:/tv

      # INCORRECT: Do not map directly to backing disks
      # - /mnt/disk1/media/movies:/movies 

Maintenance & Troubleshooting

Handling Cached/Deleted Files

If a file is deleted from the pool but appears to persist (ghost file), or if changes aren't reflecting immediately, the filesystem cache may need clearing.

sync && echo 3 | sudo tee /proc/sys/vm/drop_caches
  • Note: Deleting a file from the pool removes it from the underlying drive.

Common Issues

  • Permissions: Run MergerFS as root. Running as a standard user can cause unpredictable behavior.
  • Pool filling up: watch free GiB on /mnt/storage, not percent. With minfreespace=100G the pool stops taking new files long before df says 100%. My check warns at 500 GiB and goes critical at 250 GiB free, and ignores the individual branches.
  • Missing Files: If directories appear missing or permissions seem erratic, ensure the permissions on the underlying physical drives are identical. Use mergerfs.fsck to audit synchronization.
  • Scanner Lag (Plex/Kodi): If media scanners miss new files, ensure func.getattr=newest is enabled in the options. This forces the pool to report the most recent modification time found across all branches.

Sources & further reading