Configuration
PolicyFS reads its config from /etc/pfs/pfs.yaml by default.
Override the path with the PFS_CONFIG_FILE environment variable or the --config flag.
The Debian package ships an example at /etc/pfs/pfs.yaml.example.
Full example
fuse:
allow_other: true
log:
level: info
format: json
file: /var/log/pfs/pfs.log
mounts:
media:
mountpoint: /mnt/pfs/media
statfs:
reporting: mount_pooled_targets
on_error: ignore_failed
storage_paths:
- id: ssd1
path: /mnt/ssd1/media
indexed: false
- id: hdd1
path: /mnt/hdd1/media
indexed: true
storage_groups:
ssds: [ssd1]
hdds: [hdd1]
routing_rules:
- match: '**'
read_targets: [ssds, hdds]
write_targets: [ssds]
write_policy: first_found
path_preserving: true
indexer:
ignore: ['**/.DS_Store', '**/Thumbs.db']
mover:
enabled: true
jobs:
- name: archive
trigger:
type: usage
threshold_start: 80
threshold_stop: 70
allowed_window:
start: '23:00'
end: '06:00'
finish_current: true
source:
groups: [ssds]
patterns: ['library/**']
ignore: ['**/.DS_Store']
destination:
groups: [hdds]
policy: most_free
path_preserving: true
conditions:
min_age: 7d
min_size: 100MB
delete_source: true
delete_empty_dir: true
verify: false
Top-level keys
fuse
| Field | Type | Default | Description |
|---|---|---|---|
allow_other |
bool | false |
Pass the allow_other FUSE mount option, letting non-root users access the mount. Requires user_allow_other in /etc/fuse.conf. |
log
| Field | Type | Default | Description |
|---|---|---|---|
level |
string | info |
Log level: debug, info, warn, error, or off. |
format |
string | json |
Log format: json or text. |
file |
string | (empty) | If set, structured logs are also written to this file. Override with --log-file or PFS_LOG_FILE. |
The Debian package example config sets log.file to /var/log/pfs/pfs.log.
mounts
A map of mount name to mount configuration. The mount name is a short identifier used in systemd unit names and CLI commands (e.g. media).
Mount configuration
Each mount under mounts.<name> has:
mountpoint
Absolute path where the FUSE filesystem is mounted (e.g. /mnt/pfs/media).
log
Optional mount-scoped logging overrides.
| Field | Type | Default | Description |
|---|---|---|---|
level |
string | (empty) | If set, overrides top-level log.level for this mount only (empty inherits). |
Changes to mounts.<name>.log.level can be applied to a running daemon with pfs reload <mount>.
statfs
Controls how PolicyFS answers statfs for this mount (used by df -h and some SMB clients).
For a deeper explanation of the trade-offs (why mount-wide reporting is the default, and why per-path reporting can differ by directory), see Statfs reporting.
| Field | Type | Default | Description |
|---|---|---|---|
reporting |
string | mount_pooled_targets |
Reporting mode: mount_pooled_targets, path_pooled_targets. |
on_error |
string | ignore_failed |
Error policy: ignore_failed, fail_eio, fallback_effective_target, fallback_loopback. |
Changes to mounts.<name>.statfs.* require restarting the daemon.
storage_paths
List of physical storage roots. Each entry:
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
id |
string | yes | - | Stable identifier used in logs, database, and config references. |
path |
string | yes | - | Absolute filesystem path. |
indexed |
bool | no | false |
When true, metadata is served from the SQLite index and mutations are deferred. |
min_free_gb |
float | no | 0 |
Minimum free space in GiB. Write targets below this threshold are skipped. |
storage_groups
Map of group name to list of storage path IDs. Groups are expanded wherever storage references appear (routing rules, mover source/destination).
storage_groups:
ssds: [ssd1, ssd2]
hdds: [hdd1, hdd2, hdd3]
routing_rules
List of routing rules evaluated top-to-bottom. First match wins.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
match |
string | yes | - | Glob pattern for virtual paths. ** matches any depth. |
targets |
list | no | - | Shorthand: sets both read_targets and write_targets. |
read_targets |
list | no | - | Storage IDs or group names for reads. |
write_targets |
list | no | - | Storage IDs or group names for writes. |
write_policy |
string | no | first_found |
Target selection: first_found, most_free, or least_free. |
path_preserving |
bool | no | false |
Prefer write targets where the parent directory already exists. |
Catch-all rule required
The last rule must be match: '**'. PolicyFS rejects configs without a catch-all rule, and only one catch-all is allowed.
Changes to mounts.<name>.routing_rules can be applied to a running daemon with pfs reload <mount>.
indexer
| Field | Type | Default | Description |
|---|---|---|---|
ignore |
list | [] |
Glob patterns for files/directories to skip during indexing. |
mover
Controls file movement jobs for this mount.
| Field | Type | Default | Description |
|---|---|---|---|
enabled |
bool | true |
Master switch for all mover jobs. |
jobs |
list | [] |
List of mover job definitions. |
delete_source is true by default
The mover deletes the source file after a successful copy. Before running pfs move for the first time, use pfs move <mount> --dry-run to see what would be moved.
Add `--force` if you want to bypass job triggers/conditions.
Mover job
Each job under mover.jobs[]:
| Field | Type | Default | Description |
|---|---|---|---|
name |
string | - | Unique job name (used in --job flag and logs). |
description |
string | - | Optional human-readable description. |
trigger |
object | - | When the job should run. |
source |
object | - | Where candidates come from. |
destination |
object | - | Where files are moved to. |
conditions |
object | - | Filters applied to candidates. |
delete_source |
bool | true |
Delete the source file after a successful move. Use --dry-run to preview before running for the first time. |
delete_empty_dir |
bool | true |
Remove empty parent directories after moving. |
verify |
bool | false |
Re-read the destination file after copying to verify integrity. |
trigger
| Field | Type | Default | Description |
|---|---|---|---|
type |
string | - | Trigger type: usage or manual. |
threshold_start |
int | 80 |
Start moving when any source storage usage exceeds this percent. |
threshold_stop |
int | 70 |
Stop moving when source storage usage drops below this percent. |
allowed_window |
object | - | Optional time window restriction (only valid for type: usage). |
trigger.allowed_window
| Field | Type | Default | Description |
|---|---|---|---|
start |
string | - | Window start time in HH:MM format (e.g. 23:00). |
end |
string | - | Window end time in HH:MM format (e.g. 06:00). Wraps past midnight. |
finish_current |
bool | true |
If true, a file being copied when the window closes is allowed to finish. |
source
| Field | Type | Description |
|---|---|---|
paths |
list | Storage path IDs to scan. |
groups |
list | Storage group names to scan (expanded to IDs). |
patterns |
list | Glob patterns to filter candidate files (e.g. library/**). |
ignore |
list | Glob patterns to exclude (e.g. **/.DS_Store). |
include_file |
string | Optional newline-delimited list file of paths/globs to include as candidates. If the file cannot be read, the job fails. |
ignore_file |
string | Optional newline-delimited list file of paths/globs to exclude (ignore wins). If the file cannot be read, the job fails. |
At least one of paths or groups must be provided.
include_file / ignore_file semantics: entries in these files are matched against the
virtual relative path (e.g. library/movies/A.mkv - forward slashes, no leading /), the same
as patterns and ignore. Lines starting with # and blank lines are ignored. Ignore always
wins: if a path matches ignore or ignore_file, it is skipped regardless of patterns or
include_file. At least one of patterns or include_file must be provided.
destination
| Field | Type | Default | Description |
|---|---|---|---|
paths |
list | - | Storage path IDs as destinations. |
groups |
list | - | Storage group names as destinations (expanded to IDs). |
policy |
string | most_free |
Target selection: most_free, least_free, or first_found. |
skip_if_exists_any |
bool | false |
If true, skip a candidate when the destination path already exists on any destination storage (avoids duplicates; may increase disk I/O). When a file is skipped, delete_source does not apply - the source file is kept. |
path_preserving |
bool | false |
Prefer destinations where the parent directory already exists. |
conditions
| Field | Type | Description |
|---|---|---|
min_age |
string | Minimum file age (e.g. 7d, 24h, 30m). |
min_size |
string | Minimum file size (e.g. 100MB, 1GB). |
max_size |
string | Maximum file size. |
Environment variables
| Variable | Default | Description |
|---|---|---|
PFS_CONFIG_FILE |
/etc/pfs/pfs.yaml |
Override the config file path. |
PFS_LOG_FILE |
(unset) | If set, enables structured log duplication to this file. Overrides log.file. |
PFS_STATE_DIR |
/var/lib/pfs |
Base directory for persistent state (index DB, event logs). |
PFS_RUNTIME_DIR |
/run/pfs |
Base directory for runtime data (locks, sockets). |
PFS_LOG_DISK_ACCESS |
- | Set to 1 to enable disk access logging for indexed storage. |
TZ |
system | Timezone for allowed_window evaluation. |