Beyond the Steam library iSCSI setup, I wanted to push the underlying storage architecture itself further — a proper high-performance ZFS pool with a dedicated caching tier, tuned specifically for sequential throughput over a 10GbE link rather than general-purpose NAS duties.
Hardware
TrueNAS host:
CPU: AMD Ryzen 9 9955HX
RAM: 64GB
Pool: ZFS stripe — Samsung 970 EVO Plus + Samsung 990 Pro (NVMe)
Cache: Intel Optane L2ARC
Network: 10GbE via M.2 ADT-Link riser
Client PC:
CPU: Ryzen 7 7800X3D
GPU: RTX 5090
Board: ROG STRIX B650E-I
NIC: 10GbE via M.2 ADT-Link riser
Switch: Ubiquiti USW-Pro-XG-8-PoE, Cat6a between both ends.
Why a striped pool
A stripe (RAID 0 equivalent in ZFS terms) across the two NVMe drives maximises throughput at the cost of redundancy — if either drive fails, the pool is gone. That trade-off is deliberate here: this pool holds a game library and performance-sensitive scratch data, not anything irreplaceable. Anything that actually matters lives on the redundant pool covered in the Synology and backup writeups instead.
zpool create -f gamepool -o ashift=12 \
striped /dev/nvme0n1 /dev/nvme1n1
ashift=12 aligns the pool to 4K sectors, which matches modern NVMe drives properly and avoids the read-modify-write penalty you’d get from a mismatched sector size. Getting this wrong is a one-way mistake — ashift is set at pool creation and can’t be changed afterward without destroying and recreating the pool, so it’s worth double-checking with zdb before committing any real data to it.
Tuning for throughput over safety
Since this pool is explicitly for replaceable, performance-sensitive data, a few default ZFS safety mechanisms were deliberately disabled to maximise throughput:
zfs set sync=disabled gamepool
zfs set compression=off gamepool
zfs set atime=off gamepool
sync=disabled means ZFS acknowledges writes before they’re guaranteed to be on stable storage — normally a risky setting, but acceptable here since a power loss mid-write would at worst corrupt a game install that can simply be redownloaded. compression=off avoids CPU overhead on data that’s already compressed (game assets rarely compress further, and forcing compression on incompressible data wastes CPU cycles for zero gain). atime=off stops ZFS updating access-time metadata on every single read, which adds up across thousands of small file reads during game loading.
This combination is not something I’d use on any dataset holding anything irreplaceable — it’s specifically scoped to this one pool, and worth stating explicitly in case anyone reading this is tempted to copy the settings onto a dataset holding actual documents or photos.
Optane L2ARC
ZFS’s ARC (Adaptive Replacement Cache) lives in RAM and handles the fastest read caching automatically. L2ARC is a second-tier cache that sits between RAM and the actual pool, backed by a dedicated fast device — in this case an Intel Optane drive, chosen specifically for its extremely low latency and high endurance under sustained random access, which suits it well to a caching role that general NVMe wears out faster under.
zpool add gamepool cache /dev/nvme2n1
With 64GB of RAM already providing a large primary ARC, the L2ARC mostly helps with the “warm but not hot” data that’s fallen out of RAM cache but is still being accessed regularly — a noticeable improvement when switching between several actively-played games rather than just one.
One thing worth knowing: L2ARC is populated gradually, not instantly — a freshly added cache device starts empty and fills in as data is read, governed by l2arc_write_max (the default rate ZFS feeds new data into it per interval). Benchmarking immediately after adding the L2ARC device will understate its real-world benefit; give it a day or two of normal use before judging the improvement.
Network path
Both ends connect via 10GbE M.2 risers rather than PCIe expansion cards — a space-saving choice on both the TrueNAS host and the compact ROG STRIX B650E-I ITX build, where a full PCIe slot for a 10GbE card wasn’t available or desirable.
Jumbo frames are enabled end-to-end (MTU 9000) on both NICs and the switch, verified with:
ping 10.0.10.1 -f -l 8972
All replies succeeding confirms no fragmentation is happening anywhere in the path — critical for sustaining high sequential throughput without unnecessary packet overhead. Jumbo frames only help if every hop in the path supports them consistently; a single device on the path with MTU 1500 silently fragments everything and erases the benefit without throwing an obvious error, which is why explicit verification with the -f (don’t fragment) flag matters more than just assuming the setting “took” because no error appeared in the switch or NIC config.
Results
With the striped NVMe pool, Optane L2ARC, and tuned dataset properties, sequential read from the gaming PC over iSCSI hits approximately:
Sequential read: ~1,237 MB/s
That’s genuinely faster than most consumer SATA SSDs, and comfortably saturates a meaningful fraction of the 10GbE link — for game installs and large sequential asset streaming, the storage is no longer the bottleneck. Random 4K performance wasn’t the focus of this build and sits noticeably lower than the sequential figure, which is expected given the striped NVMe configuration is specifically tuned for large sequential transfers rather than IOPS-heavy random access patterns.