ZFS (highly experimental support)

ZFS provides a rich set of functionality but at a cost of extra resource usage. Currently ARC (Adaptive Replacement Cache) size is limited to min(256 MiB + 0.3% of total zpool size, 20% of system RAM) but at least 384 MiB. This RAM is included in the memory EVE reserves for its own operational needs. Thus would this memory not be available for allocation for applications.

If system does not have enough memory to satisfy the limit mentioned above, severe performance degradation can occur on random access workflows.

Performance tunings applied

primarycache=metadata - allows to keep arc cache size low. The primary use-case of zfs is zvol attached to a VM. And VM already does cache it's data, furthermore it knows better what to cache.

recordsize=16k - is a sweet spot between constantly having sub-blocks updates, and good compression ratio.

compression=zstd - is a less cpu intensive algorithm, with not too big compromise on compression ratio

redundant_metadata=most - will reduce the number of copies of the indirect blocks at the higher levels. This can greatly cut the amount of data that must be written, resulting in better performance.

Multiple tunables to better regulate how fast ZFS can accept requests. This along helped mediate maximum latency on parallel access with small ARC:

zfs_vdev_async_write_active_min_dirty_percent = 10
zfs_vdev_async_write_active_max_dirty_percent = 30
zfs_delay_min_dirty_percent = 40
zfs_delay_scale = 800000
zfs_dirty_data_max = 50% of zfs_arc_max
zfs_dirty_data_sync_percent = 15

The following tunables are hardcoded and are optimized values for SSD/NVMe based pools. Please note that these tunables may not work optimally for HDD based pools. WIP to dynamically adjust these parameters depending on the pool type.

zfs_vdev_sync_read_min_active = 35
zfs_vdev_sync_read_max_active = 35
zfs_vdev_sync_write_min_active = 35
zfs_vdev_sync_write_max_active = 35
zfs_vdev_async_read_min_active = 1
zfs_vdev_async_read_max_active = 10
zfs_vdev_async_write_min_active = 1
zfs_vdev_async_write_max_active = 10

Minimum recommended system requirements to install ZFS storage is 32GB memory and 3 physical disks set in eve_persist_disk. eve_install_skip_zfs_checks should be set in installation config to override the requirement check for experimental installs.

Storage maintenance

Pool TRIM (EVE-k only)

On EVE-k (kubevirt/Longhorn) nodes the persist pool is backed by NVMe devices. ZFS does not automatically notify the NVMe controller of freed block ranges; without periodic TRIM the device's garbage-collection table grows and write amplification increases.

EVE runs zpool trim persist at boot (to clear any backlog from the previous run) and then on a recurring cron schedule. The schedule is operator-configurable:

Config key Default Effect
timer.zfs.pool.trim.cron 0 3 * * 6,0 Sat/Sun at 03:00

Set to an empty string to disable the scheduled trim. The boot-time trim always runs regardless of this setting.

zpool trim returns immediately; the actual NVMe work runs in the background. Progress is visible via zpool status persist.

Vault fstrim (EVE-k only)

On EVE-k nodes /persist/vault is an ext4 filesystem on a ZFS zvol. When files are deleted, ext4 marks the blocks as free but does not notify ZFS. The zvol retains the blocks as allocated, inflating logicalused on the persist pool and causing EVE to overestimate dom0 disk usage. In severe cases this can incorrectly trigger maintenance mode.

EVE runs fstrim /persist/vault at boot (to drain any backlog) and then on a recurring cron schedule:

Config key Default Effect
timer.vault.trim.cron 0 2 * * 6,0 Sat/Sun at 02:00
timer.vault.trim.max.secs 1800 Timeout in seconds; 0 = unlimited

Set timer.vault.trim.cron to an empty string to disable the scheduled fstrim. The boot-time fstrim always runs regardless of this setting.

The two maintenance operations are staggered by default to avoid overlap:

Time Operation
02:00 Sat/Sun Vault fstrim
03:00 Sat/Sun Pool-level NVMe TRIM

Observing trim activity

Pubsub status (live and collect-info)

Trim state is published to pubsub and captured in collect-info bundles, making it available for post-mortem analysis without log grepping.

Vault fstrimvaultmgr publishes to VaultStatus:

cat /run/vaultmgr/VaultStatus/vaultmgr.json \
  | grep -A4 TrimStatus
Field Meaning
TrimStatus.LastStartTime fstrim start; zero if never run this boot
TrimStatus.LastEndTime When it completed; zero while in progress
TrimStatus.LastError Error string on failure; empty on success

ZFS pool trimzfsmanager publishes to ZFSPoolStatus:

cat /run/zfsmanager/ZFSPoolStatus/persist.json \
  | grep -A2 TrimStatus
Field Meaning
TrimStatus.LastStartTime When the most recent zpool trim was invoked

Pool trim end time is not published — zpool trim returns immediately and NVMe work continues in the background. Use zpool status persist to check completion:

zpool status persist | grep -A5 trim

Agent logs

Vault fstrim (vaultmgr agent, Notice level):

grep -i "TrimVault\|fstrim" /persist/newlog/agentlog/vaultmgr.log*
Log message Meaning
TrimVault: starting fstrim /persist/vault (timeout Xs) Run started
TrimVault: fstrim /persist/vault completed in Xs Success
TrimVault: fstrim /persist/vault failed after Xs: ... Failure
startVaultTrimSchedule: scheduled trim starting Cron tick fired

ZFS pool trim (zfsmanager agent, Notice level):

grep -i "runPoolTrimSchedule" /persist/newlog/agentlog/zfsmanager.log*
Log message Meaning
runPoolTrimSchedule: boot-time demand trim starting Boot trim started
runPoolTrimSchedule: zpool trim persist initiated Command accepted
runPoolTrimSchedule: scheduled trim starting Cron tick fired
runPoolTrimSchedule: zpool trim persist: <error> Failure