Proxmox ZFS Tuning: ARC, L2ARC, and SLOG Guide

Learn how to tune ZFS ARC size, add L2ARC SSD caching, and configure SLOG on Proxmox for maximum storage performance. Practical tips for homelabs and production.

Proxmox Pulse Proxmox Pulse
9 min read
Layered NVMe SSDs and hard drives glowing with blue and amber light in a dark server rack, visualizing ZFS storage tiers.

ZFS is one of the best reasons to run Proxmox in your homelab or on-prem infrastructure. It gives you data integrity, snapshots, compression, and replication out of the box. But the default configuration is conservative — designed to work on anything from a Raspberry Pi to a enterprise server. If you have spare RAM, an NVMe drive, or a dedicated write log device, you're leaving significant performance on the table.

This guide walks through the three most impactful ZFS tuning levers on Proxmox: the Adaptive Replacement Cache (ARC), the Level 2 ARC (L2ARC), and the Separate Intent Log (SLOG). We'll cover what each one does, when to use it, and exactly how to configure it.

Understanding ZFS Caching Architecture

Before touching any settings, it helps to understand how ZFS uses storage tiers.

ZFS has a layered caching model:

  • ARC — In-RAM read cache. The fastest tier. All ZFS reads go through ARC first.
  • L2ARC — A secondary read cache on a fast SSD or NVMe. Spills over from ARC.
  • SLOG (ZIL device) — A dedicated write log device that accelerates synchronous writes. Does not cache reads.
  • Main pool — Your actual storage (HDDs, SSDs, or mixed).

Getting these right for your hardware profile is the difference between sluggish VM disk I/O and a snappy, responsive system.

Tuning the ARC (Adaptive Replacement Cache)

The ARC lives entirely in RAM. ZFS will use as much memory as you allow — by default, up to half of total system RAM on Linux. On a Proxmox host with 64 GB RAM, that means ZFS might claim 32 GB just for disk cache.

For a pure storage node, that's fine. For a Proxmox hypervisor running VMs and containers, you need to leave RAM headroom for your guests.

Check Current ARC Usage

bash arc_summary

If arc_summary isn't installed:

apt install sysstat
# Or read directly:
cat /proc/spl/kstat/zfs/arcstats | grep -E '^(size|c_max|c_min|hits|misses)'

The key values:

  • size — Current ARC size in bytes
  • c_max — Maximum allowed ARC size
  • hits / misses — Cache hit ratio (aim for >90% hits)

Set ARC Min and Max Limits

Create a ZFS module config file:

nano /etc/modprobe.d/zfs.conf

Add limits in bytes. For a 64 GB host running VMs, a reasonable starting point:

Minimum ARC size: 4 GB

options zfs zfs_arc_min=4294967296

Maximum ARC size: 16 GB

options zfs zfs_arc_max=17179869184

Calculate your values:

# 8 GB in bytes
python3 -c "print(8 * 1024**3)"
# Output: 8589934592

Apply without rebooting:

echo 17179869184 > /sys/module/zfs/parameters/zfs_arc_max
echo 4294967296 > /sys/module/zfs/parameters/zfs_arc_min

Update initramfs so the setting persists after reboot:

update-initramfs -u -k all

ARC Sizing Guidelines

Host RAM VMs/Containers Recommended ARC Max
16 GB 2–4 VMs 4–6 GB
32 GB 4–8 VMs 8–12 GB
64 GB 8–16 VMs 16–24 GB
128 GB 16+ VMs 32–48 GB

If your workload is read-heavy (NFS, media streaming, databases with warm caches), push ARC higher. If it's write-heavy or mostly fresh data, extra ARC won't help much.

Adding L2ARC: SSD Read Cache

L2ARC extends your read cache onto a fast SSD or NVMe. When data gets evicted from RAM-based ARC, ZFS can promote it to L2ARC instead of discarding it. Subsequent reads pull from L2ARC rather than hitting spinning disks.

When L2ARC Actually Helps

L2ARC is worth adding if:

  • Your pool uses HDDs and you have a spare SSD
  • Your working dataset is larger than your ARC
  • Read hit rates are below 80% under normal load
  • You're running databases, media servers, or NFS with repeated access patterns

L2ARC does not help if:

  • Your entire working dataset fits in ARC already
  • Your workload is 100% sequential writes (backups, logging)
  • Your pool is already all-SSD or NVMe

L2ARC RAM Overhead

This is the part most guides skip: L2ARC metadata is stored in ARC. Each GB of L2ARC consumes roughly 70–100 MB of ARC for its index headers. A 500 GB L2ARC device could eat 35–50 GB of your RAM just for metadata.

For homelabs, keep L2ARC devices under 200–300 GB unless you have abundant RAM.

Add a Device as L2ARC

Identify your SSD:

lsblk -o NAME,SIZE,ROTA,MODEL
# ROTA=0 means SSD/NVMe

Add it to your pool as a cache device:

# Replace 'rpool' and '/dev/sdc' with your pool and device
zpool add rpool cache /dev/sdc

Verify it's active:

zpool status rpool

You should see a cache section listing the device.

Monitor L2ARC Hit Rate

cat /proc/spl/kstat/zfs/arcstats | grep l2

Key fields:

  • l2_hits — Reads served from L2ARC
  • l2_misses — Reads that missed L2ARC and went to disk
  • l2_size — Current L2ARC data size

After a warm-up period (hours to days depending on workload), aim for L2ARC hit rates above 50% on top of your ARC hits.

Remove L2ARC Device

L2ARC is not a persistent cache — removing it loses no data, only cached reads:

zpool remove rpool /dev/sdc

Configuring SLOG (ZFS Intent Log)

SLOG is commonly misunderstood. It does not cache reads. It accelerates synchronous writes by providing a fast, durable landing zone for ZFS's intent log.

How the ZFS Intent Log Works

Every synchronous write (NFS, iSCSI, databases, VMs with sync=always) waits for data to be committed to stable storage before returning. Without a SLOG, ZFS writes to the main pool and then acknowledges the write — slow on spinning rust.

With a SLOG, ZFS writes to the fast SLOG device and immediately acknowledges the write. The data is later flushed to the main pool in bulk. The SLOG only needs to survive a power loss long enough to replay on next boot.

When SLOG Helps

  • NFS shares with sync=on (the default)
  • iSCSI targets
  • Database VMs with sync I/O (PostgreSQL, MySQL, etc.)
  • Any workload where iostat shows high await on write operations

SLOG does not help for:

  • Async workloads (VMs with sync=disabled in Proxmox storage config)
  • Pure read workloads
  • Workloads already bottlenecked by CPU or RAM

SLOG Device Requirements

The SLOG device must have:

  1. Low write latency — Under 100µs is ideal (NVMe optane, enterprise SSD)
  2. Power-loss protection — Consumer NVMe drives without capacitor-backed cache can corrupt SLOG on power loss. Use enterprise drives or a UPS.
  3. High endurance — SLOG gets hammered with writes. Use a drive rated for high write workloads (DWPD ≥ 3).

A cheap consumer SSD as SLOG can be worse than no SLOG if it lacks power-loss protection.

Add a SLOG Device

# Add mirrored SLOG for redundancy (recommended for production)
zpool add rpool log mirror /dev/nvme1n1 /dev/nvme2n1

Single SLOG (acceptable with UPS protection)

zpool add rpool log /dev/nvme1n1

Always mirror SLOG in production. A failed SLOG doesn't cause data loss, but it will stall synchronous writes until you replace it.

Verify:

zpool status rpool
# Should show a 'logs' section

Proxmox Storage Sync Setting

In the Proxmox web UI under Datacenter → Storage, the sync option controls whether VM disk writes are synchronous:

  • sync=standard — Inherits from ZFS dataset default (usually async)
  • sync=always — Forces sync writes (maximum safety, needs SLOG)
  • sync=disabled — Async writes (fastest, risk on power loss)

For VMs on a pool with a good SLOG and UPS, sync=always gives you both safety and performance.

ZFS Compression: Free Performance

While not strictly caching, ZFS compression deserves mention here because it effectively increases both I/O throughput and ARC capacity.

# Enable lz4 compression on a dataset (fast, high ratio)
zfs set compression=lz4 rpool/data

Check current compression ratio

zfs get compressratio rpool/data

lz4 is the right default for most workloads — CPU overhead is negligible on modern processors and ratios of 1.5x–3x are common for VM images and text-heavy data.

zstd compresses better but uses more CPU. Use it for archival datasets, not active VM storage.

Proxmox-Specific ZFS Recommendations

Separate OS and VM Storage Pools

Don't run your Proxmox OS root (rpool) and VM storage on the same pool unless you have no choice. Noisy VM disk I/O degrades system responsiveness.

Create a dedicated pool for VMs:

# Example: 4-drive raidz2 for VM storage
zpool create vmdata raidz2 /dev/sda /dev/sdb /dev/sdc /dev/sdd

Add it as storage in Proxmox

Datacenter → Storage → Add → ZFS → select vmdata

Adjust recordsize for VM Workloads

The default ZFS recordsize is 128K. VM disk images with random 4K I/O perform better with smaller records:

# For VM disk datasets
zfs set recordsize=64K vmdata/vm-disks

For databases (PostgreSQL, MySQL)

zfs set recordsize=8K vmdata/db-storage

For NFS media / backups (large sequential)

zfs set recordsize=1M vmdata/backups

This only affects new writes — existing data uses the recordsize it was written with.

Disable atime for VM Storage

zfs set atime=off vmdata

Access time updates on every file read generate unnecessary write amplification. There's no reason to track atime on VM disk images.

Monitoring and Ongoing Tuning

ZFS tuning isn't set-and-forget. Monitor regularly:

# Real-time pool I/O stats
zpool iostat -v 2

ARC stats summary

arc_summary | head -40

Check for slow disks or errors

zpool status -v

Install zfs-auto-snapshot if you want automated snapshots without manual scheduling:

apt install zfs-auto-snapshot

And periodically run scrubs to verify data integrity:

# Start a scrub
zpool scrub vmdata

Check scrub status

zpool status vmdata

Schedule weekly scrubs with a cron job:

echo "0 2 * * 0 root /sbin/zpool scrub vmdata" > /etc/cron.d/zfs-scrub

Conclusion

ZFS tuning on Proxmox comes down to matching your configuration to your hardware and workload. Set ARC limits so VMs have the RAM they need. Add L2ARC only when your working dataset exceeds available RAM and you have a spare SSD. Deploy SLOG on enterprise-grade NVMe when synchronous write latency matters — and back it with a UPS. Enable lz4 compression everywhere, tune recordsize to your I/O pattern, and scrub regularly.

Start with ARC tuning since it's zero-cost and immediately impactful. Add caching tiers incrementally, measure before and after with zpool iostat and arc_summary, and resist the urge to throw hardware at a problem that's actually a misconfiguration. With the right settings, a ZFS pool on Proxmox can rival enterprise SAN performance at a fraction of the cost.

Share
Proxmox Pulse

Written by

Proxmox Pulse

Sysadmin-driven guides for getting the most out of Proxmox VE in production and homelab environments.

Related Articles

View all →