Getting started
Prerequisites
- Linux
- systemd
- FUSE3 userspace tools
Installation
Debian/Ubuntu
Install FUSE3:
sudo apt-get update
sudo apt-get install -y fuse3
Install from the APT repo:
sudo curl -fsSL -o /usr/share/keyrings/policyfs-archive-keyring.gpg \
"https://repo.policyfs.org/apt/policyfs.gpg"
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/policyfs-archive-keyring.gpg] https://repo.policyfs.org/apt stable main" \
| sudo tee /etc/apt/sources.list.d/policyfs.list >/dev/null
sudo apt-get update
sudo apt-get install -y policyfs
Or download the .deb from GitHub Releases and install it:
curl -L -o "policyfs_amd64.deb" "https://github.com/hieutdo/policyfs/releases/latest/download/policyfs_amd64.deb"
sudo dpkg -i "./policyfs_amd64.deb"
RedHat/Fedora (DNF/YUM)
Install FUSE3:
sudo dnf install -y fuse3
Install from the RPM repo:
sudo curl -fsSL -o /etc/pki/rpm-gpg/RPM-GPG-KEY-policyfs \
"https://repo.policyfs.org/rpm/policyfs.gpg"
cat <<'EOF' | sudo tee /etc/yum.repos.d/policyfs.repo >/dev/null
[policyfs]
name=PolicyFS
baseurl=https://repo.policyfs.org/rpm/fedora/x86_64
enabled=1
gpgcheck=1
repo_gpgcheck=0
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-policyfs
EOF
sudo dnf makecache
sudo dnf install -y policyfs
EL9 (Rocky/Alma/CentOS Stream 9) uses:
baseurl=https://repo.policyfs.org/rpm/el/9/x86_64
Build from source
Packages are recommended (they install systemd units and a default config), but you can build PolicyFS from source.
You will need Go installed (see go.mod for the required version).
Install build dependencies:
Debian/Ubuntu
sudo apt-get update
sudo apt-get install -y --no-install-recommends \
git \
build-essential \
pkg-config \
libsqlite3-dev \
libfuse3-dev
Fedora/EL9
sudo dnf install -y \
git \
gcc \
make \
pkgconf-pkg-config \
sqlite-devel \
fuse3-devel
Build and install:
git clone https://github.com/hieutdo/policyfs.git
cd policyfs
CGO_ENABLED=1 go build -o ./bin/pfs ./cmd/pfs
sudo install -m 0755 ./bin/pfs /usr/local/bin/pfs
Optional validation before building:
# Full test suite (recommended when your environment is ready)
go test ./...
Create an example config:
sudo install -d /etc/pfs
if [ ! -f /etc/pfs/pfs.yaml ]; then
sudo install -m 0644 packaging/config/pfs.example.yaml /etc/pfs/pfs.yaml
fi
Then continue with the configuration steps below.
If you want to manage PolicyFS via systemd, the unit files live under packaging/systemd/ (see systemd.md).
Note: packaged unit files reference /usr/bin/pfs. For source installs, choose one approach:
- Install your binary to
/usr/bin/pfs. - Or copy unit files to
/etc/systemd/system/and updateExecStart/ExecStopto/usr/local/bin/pfs.
Configure
Allow other users to access the mount
If apps like Plex or Jellyfin run as a different user (e.g. plex, jellyfin), they cannot access a FUSE mount started by root unless allow_other is enabled. This is required for most media server setups.
Enable it in /etc/fuse.conf:
sudo nano /etc/fuse.conf
Uncomment:
user_allow_other
Then set fuse.allow_other: true in /etc/pfs/pfs.yaml.
Configure a minimal mount
On first install, the package creates:
/etc/pfs/pfs.yaml(copied from the example)
Before enabling any systemd units, edit /etc/pfs/pfs.yaml:
- Pick your mount name under
mounts:(it does not have to bemedia). - Set
mountpointto a real path on your machine. - Set
storage_paths[].pathto your actual disk paths.
Here is an example configuration:
mounts:
media:
mountpoint: /mnt/pfs/media
storage_paths:
- { id: d1, path: /mnt/disk1/media, indexed: false }
- { id: d2, path: /mnt/disk2/media, indexed: false }
storage_groups:
disks: [d1, d2]
routing_rules:
- match: '**'
read_targets: [disks]
write_targets: [disks]
write_policy: most_free
path_preserving: true
Create directories
Create the mountpoint and storage path directories:
sudo mkdir -p /mnt/pfs/media
sudo mkdir -p /mnt/disk1/media /mnt/disk2/media
If these paths do not exist, pfs@<mount>.service will fail to start.
Indexed storage expectations
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.
Not sure what config to use?
See Use cases - pick the pattern closest to your setup and copy it as your starting point.
Enable a mount
Example: assuming your mount is called media:
sudo systemctl enable --now pfs@media.service
Verify it started:
sudo systemctl status pfs@media.service
sudo pfs doctor media
ls /mnt/pfs/media
(Optional) Enable indexing for archive disks
Indexing is optional. It reduces metadata-driven disk touches for storage paths with indexed: true.
- Metadata operations (like directory listing) can be served from SQLite after an index run.
- File content reads still come from the owning disk.
To use it:
- Set
indexed: trueon the storage paths you want indexed. - Run:
sudo systemctl start pfs-index@media.service
Run maintenance jobs
Test the jobs manually before scheduling them:
sudo systemctl start pfs-move@media.service
sudo systemctl start pfs-prune@media.service
sudo systemctl start pfs-index@media.service
If you want scheduling, enable the batched maintenance timer:
sudo systemctl enable --now pfs-maint@media.timer
This runs move → prune → index under one schedule (default: 00:30 nightly). See systemd for default schedules and how to override them.