---
title: "Retiring the QNAP TS-873: How I Built a Silent 92TB Unraid 7 NAS with 10GbE & Local AI"
date: "2026-11-03T10:00:00+01:00"
tags: ["Unraid", "NAS", "QNAP", "10GbE", "ZFS", "NFSv4", "Jellyfin", "Home Assistant", "KVM", "NVIDIA", "Homelab"]
categories: ["Hardware & HomeLab", "Storage & Virtualization"]
---

![Retiring the QNAP TS-873: How I Built a Silent 92TB Unraid 7 NAS with 10GbE and Local AI][1]

For several years, my 8-bay **QNAP TS-873** handled everything I threw at it: NFS shares, Time Machine backups, Jellyfin media, and a Home Assistant virtual machine. Eventually, the hardware hit a wall. The embedded AMD RX-421BD quad-core CPU choked during heavy I/O, QTS firmware updates took twenty minutes to reboot, all eight 3.5-inch drive bays were full, and the proprietary chassis had neither the physical clearance nor the PCIe power delivery to host a full-height NVIDIA GPU for local AI workloads.

In today's post, I will walk you through how I reused the **AMD Ryzen 7 5800X**, **ASUS ROG STRIX B550-E GAMING** motherboard, and **32 GB of DDR4-3600 RAM** left over from my recent AM5 workstation upgrade to build a silent, 12-drive **Unraid OS 7** server with a 4 TB ZFS SSD mirror, 10GbE networking, KVM virtualization, and an NVIDIA RTX 3060 12GB for local CUDA inference.

<!--more-->

## Step 1: Hardware Architecture & PCIe Slot Layout

Repurposing desktop components into a 24/7 storage server works well only if you plan your PCIe lanes, SATA controllers, and power delivery ahead of time.

| Component | Model / Specification | Role in the NAS |
| :--- | :--- | :--- |
| **Chassis** | Fractal Design Define 7 PCGH Edition | Sound-dampened full tower supporting up to 14× 3.5" HDD trays |
| **CPU** | AMD Ryzen 7 5800X (8C / 16T, Zen 3) | Compute for ZFS, KVM (HAOS), and high-speed parity calculations |
| **CPU Cooler** | Noctua NH Tower Cooler | Near-silent thermal dissipation under continuous load |
| **Motherboard** | ASUS ROG STRIX B550-E GAMING | Splits CPU lanes into **PCIe 4.0 x8 / x8** across Slots 1 & 2 |
| **Memory** | 32 GB (2× 16 GB) DDR4-3600 | Host OS, ZFS ARC cache, KVM VM, and RAM transcoding |
| **GPU (Slot 1)** | NVIDIA GeForce RTX 3060 12GB (Dual-Slot) | Local AI inference (`Ollama`, Wyoming `Whisper` STT & `Piper` TTS) |
| **10GbE NIC (Slot 2)** | Intel X540-AT2 Dual-Port 10GBase-T | 10GbE uplink to the core switch |
| **SATA HBA (Slot 4)** | ASM1166 6-Port SATA III 6Gb/s (PCIe x4) | Expands onboard 6× SATA to **12× SATA 6Gb/s ports** (all 12 populated) |
| **ZFS Cache Pool** | 2× SanDisk Ultra 3D 4 TB SSD | **4 TB ZFS Mirror** (`/mnt/cache`) for `appdata`, KVM `domains`, and `system` |
| **Hard Drives** | 10× 3.5" HDDs (`4× 16 TB`, `4× 12 TB`, `1× 8 TB`, `1× 4 TB`) | **92 TB** usable XFS data array (8 data drives) + **Dual 16 TB Parity** |
| **Power Supply** | be quiet! Pure Power 12 750W 80+ Gold (ATX 3.0) | Single high-current `+12V` rail for 10+ HDD spin-up surges + GPU |
| **UPS** | APC Back-UPS BX1200MI (1200 VA / 650 W) | USB `apcupsd` master protecting NAS, UDM Pro router, and MS-01 host |

![Fractal Design Define 7 PCGH in Storage Layout with stacked 3.5-inch HDD trays][2]

### Fitting Both a GPU and a 10GbE Card on a B550 Motherboard

Running a dedicated GPU alongside a 10GbE network card and a 6-port SATA expander on a consumer AM4 board requires careful attention to how the motherboard routes PCIe lanes. Cheap B550 motherboards wire the primary slot (`PCIEX16_1`) to the CPU at `x16` and route the second full-length slot (`PCIEX16_2`) through the B550 chipset at `PCIe 3.0 x4`, which often disables onboard SATA ports or the second M.2 slot when populated. The **ASUS ROG STRIX B550-E GAMING** avoids this by splitting the 16 PCIe 4.0 lanes from the Ryzen 7 5800X directly into **PCIe 4.0 x8 (`PCIEX16_1`) + PCIe 4.0 x8 (`PCIEX16_2`)**. That gives the RTX 3060 12GB PCIe 4.0 x8 bandwidth in Slot 1 while giving the Intel X540-AT2 10GbE card dedicated CPU lanes in Slot 2 without touching chipset I/O.

The bottom full-length slot (`PCIEX16_3`, wired to the chipset at PCIe 3.0 x4) hosts the **ASM1166 6-port SATA controller**. Unlike older enterprise SAS HBAs such as the LSI 9211-8i that burn 12–15W at idle and block deeper PCIe ASPM power states, the ASM1166 draws under 2W and works out of the box with the Linux `ahci` driver.

> **Gotcha:** Check the physical thickness of your GPU before buying! On the B550-E, `PCIEX16_1` and `PCIEX16_2` sit three slots apart. A 2.5-slot or 3-slot gaming GPU physically covers the second x16 slot, leaving nowhere to plug in the 10GbE card. Stick to a strict **2.0-slot** card, and mount an auxiliary fan across the lower PCIe zone because server NICs like the Intel X540-AT2 expect rackmount airflow and run very hot in a silent tower case.

![ASUS ROG STRIX B550-E GAMING with Noctua push-pull cooler, Intel X540-AT2 10GbE NIC in Slot 2, and ASM1166 6-port SATA HBA in Slot 4 before dropping in the Dual-Slot RTX 3060][3]

---

## Step 2: Storage Layout & The "Parity-Last" Migration Strategy

Moving away from QNAP's rigid RAID groups to Unraid 7 let me combine all 10 hard drives (`4× 16 TB`, `4× 12 TB`, `1× 8 TB`, and `1× 4 TB`) into a single pool protected by Dual Parity, backed by a 4 TB ZFS cache mirror (`2× SanDisk 4 TB SSD`) across all 12 SATA ports:

![Unraid 7.3.4 Main tab showing the 10-drive XFS array with Dual 16TB Parity and the 2x SanDisk 4TB ZFS Mirror cache pool][4]

```text
┌──────────────────────────────────────────────────────────────────────────────┐
│                        UNRAID 7.3.4 STORAGE ARCHITECTURE                     │
├──────────────────────────────┬───────────────────────────────────────────────┤
│ TIER / POOL                  │ CONFIGURATION & WORKLOADS                     │
├──────────────────────────────┼───────────────────────────────────────────────┤
│ Cache Pool (/mnt/cache)      │ 2× SanDisk 4 TB SSD (4 TB ZFS Mirror)         │
│   ├─ /mnt/user/appdata       │ Docker container configs & databases          │
│   ├─ /mnt/user/domains       │ KVM Virtual Machines (Home Assistant OS qcow2)│
│   └─ /mnt/user/system        │ Docker vDisk & libvirt images                 │
├──────────────────────────────┼───────────────────────────────────────────────┤
│ Main Data Array (/mnt/user)  │ 8× XFS Data Drives (92 TB Usable, High Water) │
│   ├─ Parity 1 + Parity 2     │ 2× 16 TB WD Red Pro (Dual Parity Protection)  │
│   └─ Data Disks 1 .. 8       │ 2× 16 TB, 4× 12 TB, 1× 8 TB, 1× 4 TB HDDs     │
└──────────────────────────────┴───────────────────────────────────────────────┘
```

### Migrate Your Bulk Data *Before* Assigning Parity Drives

> **Note:** Do not assign `Parity 1` and `Parity 2` before your initial data migration! If you enable parity on day one and start copying 60+ TB of data into the Unraid array, every write triggers a read-modify-write parity calculation across the spinning disks and throttles your transfer speeds to 60–90 MB/s.

Instead, run the migration in two steps:

1. **Stage 1 (Unprotected Bulk Ingest):** Assign only the 8 XFS data drives (`Disk 1` through `Disk 8`) with no parity drives installed. Run background `rsync` jobs over the 10GbE network from the old QNAP NAS and from external USB 3.2 staging drives (`22 TB Seagate Exos` + `14 TB WD Red Pro` mounted via the *Unassigned Devices* plugin):
   ```bash
   rsync -avh --no-inc-recursive --info=progress2 \
     /mnt/disks/USB_Staging_22TB/Media/ /mnt/user/Media/
   ```
   Without parity overhead, writes saturate the native mechanical speed of each target drive (`180–240 MB/s` per disk).
2. **Stage 2 (Enable Dual Parity):** Once the initial multi-terabyte ingest finishes, stop the array, assign both **16 TB WD Red Pro** drives to `Parity 1` and `Parity 2`, and start the array. Unraid builds Dual Parity sequentially from outer to inner cylinders at `210+ MB/s` in a single pass.

---

## Step 3: 10GbE Networking & Fixing NFSv4.2 `Stale File Handle` Errors

My compute layer runs on a separate **Minisforum MS-01** server (`Intel Core i9`, 96 GB RAM, CachyOS) hosting 70+ Docker containers, while bulk media and backups live on the Unraid NAS. Both servers connect via 10GbE (`Intel X710-DA2` on the MS-01 and `Intel X540-AT2` on Unraid) into the core 10GbE switch.

Sequential disk benchmarks over NFSv4.2 confirmed the storage stack was performing at full speed:

```text
# 1. Direct NFSv4.2 Sequential Write (Bypassing RAM Cache)
$ dd if=/dev/zero of=/mnt/nas_share/test_10g.bin bs=1M count=4096 oflag=direct
4294967296 bytes (4.3 GB, 4.0 GiB) copied, 6.96091 s, 617 MB/s

# 2. Cached Read Throughput (Linux Page Cache + ZFS ARC)
$ dd if=/mnt/nas_share/test_10g.bin of=/dev/null bs=1M
4294967296 bytes (4.3 GB, 4.0 GiB) copied, 0.394112 s, 10.9 GB/s
```

### Fixing `ESTALE` (`errno -116`) & I/O Freezes Under Heavy Load

The `dd` numbers looked great right until I ran a multi-terabyte background `rsync` while **Immich** was generating machine-learning thumbnails and **Jellyfin** and **Sonarr** were scanning libraries over NFSv4.2 from the MS-01 Docker host. Containers started crashing with:

```text
OSError: [Errno 116] Stale file handle
```

At the same time, directory listings (`ls /mnt/nas_share`) on the Docker host would freeze for 15–20 seconds. I spent several hours digging through Unraid (`6.12 LTS` kernel) and `shfs` behaviour to track down all three root causes so you don't have to:

1. **Unraid `shfs` inode eviction (`fuse_remember=330`):** When you export `/mnt/user/<share>` over NFS, clients talk to `rpc.nfsd` on top of Unraid's FUSE filesystem (`/usr/libexec/unraid/shfs`). By default, `shfs` caches inode mappings in RAM for only **330 seconds** (`-o remember=330`). Under heavy file-scanning load from Immich and Jellyfin, `shfs` evicts older inode entries from memory. The moment a remote NFSv4.2 client references an evicted file handle, `shfs` fails to resolve the inode and returns `ESTALE`.
2. **Linux dirty page writeback stalls (`vm.dirty_ratio=20`):** On a server with 32 GB of RAM, Linux's default `vm.dirty_ratio=20` and `vm.dirty_background_ratio=10` allow the kernel to buffer up to **6.4 GB of uncommitted writes** in RAM during an `rsync` job before forcing synchronous writeback. Flooding mechanical HDDs with a 6.4 GB flush blocks `shfs` and `nfsd` worker threads for 20+ seconds.
3. **Client-side `actimeo=1800` caching:** Many homelab guides suggest adding `actimeo=1800` to `/etc/fstab` to reduce metadata lookups. On a media server, caching directory attributes for 30 minutes means Sonarr, Radarr, and Jellyfin cannot see newly downloaded files created on the NAS for half an hour.

### The 4-Step Fix

1. **Disable `shfs` Inode Eviction & Enable Exclusive Shares:**
   In `/boot/config/share.cfg` on the Unraid USB flash drive, set `fuse_remember` to `-1` (never evict cached inodes until array stop), raise the file descriptor limit for `shfs` to `524288`, and enable **Exclusive Shares**. Cache-only ZFS shares (`appdata`, `domains`, `isos`, `system`, `backup`) then use direct kernel symlinks (`/mnt/user/appdata -> /mnt/cache/appdata`, shown as `▸ Cache` in the Unraid UI) and bypass FUSE completely, while bulk media and backup shares (`Cache ➔ Array`) stage incoming writes on the ZFS SSD pool before the nightly Mover flushes them to the XFS array:

   ![Unraid 7.3.4 User Shares showing Exclusive Shares (▸ Cache) for appdata, domains, and system alongside Cache -> Array staging for NFS media shares][6]

   ```ini
   # /boot/config/share.cfg
   shareUserExclusive="yes"
   shareFuseRemember="-1"
   shareNOFILE="524288"
   ```

   After restarting the array, verify that `shfs` is running with `-o remember=-1`:

   ```bash
   root@NAS:~# ps aux | grep shfs
   root  14210  2.1  0.4 845120 148200 ?  Ssl 12:14  0:42 /usr/libexec/unraid/shfs /mnt/user -disks 255 -o default_permissions,allow_other,noatime -o remember=-1
   ```

2. **Clamp Kernel Dirty Page Writeback in `/boot/config/go`:**
   To force the Linux kernel to flush writes continuously in smaller batches (~600 MB background, ~1.2 GB hard ceiling) instead of stalling the bus with multi-gigabyte flushes, persist `sysctl` rules in `/boot/config/go`:

   ```bash
   #!/bin/bash
   # Start the Management Utility
   /usr/local/sbin/emhttp

   # Tune Linux writeback cache for smooth NFSv4.2 / shfs latency
   sysctl -w vm.dirty_background_ratio=2
   sysctl -w vm.dirty_ratio=4
   ```

3. **Automate Unraid 7 Settings via `/var/run/emhttpd.socket`:**
   If you manage your server via SSH or Ansible, trying to `POST` configuration changes to Unraid's Nginx web interface (`http://localhost/update.htm`) fails unless you scrape session cookies and CSRF tokens. In Unraid 7, `emhttpd` listens directly on a local Unix domain socket at `/var/run/emhttpd.socket`. You can apply system changes or trigger array actions from the CLI with `curl --unix-socket`:

   ```bash
   # Programmatically updating a share or system setting via emhttpd Unix socket
   curl --unix-socket /var/run/emhttpd.socket \
     http://localhost/update.htm \
     -d "shareFuseRemember=-1&shareUserExclusive=yes&changeShare=Apply"
   ```

4. **Configure Clean Client `/etc/fstab` Mount Options on the Docker Host:**
   On the Minisforum MS-01 Docker host, mount the Unraid exports using **NFSv4.2** with 1 MB read/write blocks (`1048576`), `async`, `hard`, `noatime`, and without `actimeo`:

   ```fstab
   # /etc/fstab on Docker Host (MS-01)
   10.10.255.50:/mnt/user/Media   /mnt/nas_media   nfs4  nfsvers=4.2,rw,async,hard,intr,noatime,rsize=1048576,wsize=1048576,_netdev  0  0
   10.10.255.50:/mnt/user/Backup  /mnt/nas_backup  nfs4  nfsvers=4.2,rw,async,hard,intr,noatime,rsize=1048576,wsize=1048576,_netdev  0  0
   ```

   Verify the active kernel mount flags with `nfsstat -m` to confirm `vers=4.2,rsize=1048576,wsize=1048576` are negotiated. Since applying these four changes, I haven't seen a single `Stale file handle` error, even during simultaneous 10GbE `rsync` migrations and Immich ML indexing.

---

## Step 4: Home Assistant OS on KVM, Live `fsfreeze` Backups & Offsite Cloud Sync

On the old QNAP TS-873, my **Home Assistant OS (HAOS)** virtual machine ran inside QNAP Virtualization Station with noticeable UI lag during database purges. Migrating it to Unraid took less than fifteen minutes:

1. Copied the `qcow2` disk image directly onto the **4 TB ZFS Cache Mirror** (`/mnt/cache/domains/haos/vdisk1.qcow2`).
2. Created a KVM virtual machine (`OVMF` UEFI, VirtIO network bound to `br0` with its static VLAN IP, and USB passthrough for the **Sonoff Zigbee 3.0 USB Dongle Plus** `10c4:ea60`).
3. Paired with local **RTX 3060 12GB** containers on Unraid (`wyoming-whisper` for speech-to-text, `wyoming-piper` for text-to-speech, and `ollama` for local LLM intent processing), Home Assistant voice pipelines now respond in milliseconds while the VM idles at a load average of `0.08`.

![Home Assistant OS (haos) KVM virtual machine running on the Unraid 7.3.4 ZFS cache pool][5]

### Zero-Downtime VM Backups (18 GB in 102 Seconds)

Instead of shutting down Home Assistant every week to back up its virtual disk, I configured the **VM Backup** plugin to use the **QEMU Guest Agent (`fsfreeze`)** and `zstd` multithreaded compression:

- Before copying the vDisk, `libvirt` instructs the QEMU Guest Agent inside HAOS to freeze filesystem writes (`fsfreeze-freeze`), flushing SQLite and MariaDB transactions cleanly to disk.
- Unraid streams and compresses the **18 GB `qcow2` image** using `zstd` in **1 minute 42 seconds** (`102s`), then immediately thaws the filesystem (`fsfreeze-thaw`). Home Assistant stays online the entire time without dropping a Zigbee or MQTT packet.

### Nightly Offsite Sync to Google Cloud Storage (`rclone --size-only`)

For offsite backups, an Unraid **User Script** runs every night at `03:30` to sync critical shares (`Photos`, `Documents`, `Music`, and encrypted archives) to a **Google Cloud Storage (GCS)** bucket protected by customer-managed encryption keys (CMEK):

```bash
#!/bin/bash
# Unraid User Script: Nightly Offsite Sync to GCS (03:30)
rclone sync /mnt/user/Photos gcs-offsite:homelab-backup-archive/Photos \
  --size-only \
  --transfers=8 \
  --checkers=16 \
  --bwlimit=50M \
  --log-file=/var/log/rclone-gcs-nightly.log \
  --log-level=INFO
```

By default, `rclone` compares checksums or modification timestamps, which can force local disk reads or slow metadata comparisons across hundreds of thousands of photo files. Passing `--size-only` cut the nightly pre-sync check from hours to a few seconds.

---

## Step 5: The Shared APC UPS Setting That Almost Killed My Router (`KILLDELAY 0`)

During UPS failover testing, one setting in Unraid nearly took down my entire network. My **APC Back-UPS BX1200MI** powers three devices:

1. **UniFi Dream Machine Pro (UDM Pro)** and core PoE switches
2. **Minisforum MS-01** Docker host
3. **Custom Unraid NAS** (connected to the UPS via USB cable as the `apcupsd` master)

Because the Unraid NAS spins 10 mechanical hard drives, I configured it to start a clean shutdown early in a power outage, as soon as battery runtime drops below **15 minutes** or battery charge hits **35%**.

> **Gotcha:** In Unraid's **Settings -> UPS Settings**, watch out for **"Turn off UPS after shutdown"**. If you leave it set to **Yes**, Unraid instructs the APC UPS to cut AC output power (`killpower`) right after the NAS finishes shutting down. Because my UDM Pro and Minisforum MS-01 share that same UPS and draw far less power than 10 hard drives, sending `killpower` from Unraid cuts power to the router and Docker host while the battery still has 35% charge left! Always set **Turn off UPS after shutdown: `No`** (which writes `KILLDELAY 0` in `/etc/apcupsd/apcupsd.conf`) when your NAS shares a UPS with network gear.

---

## Summary

To sum it up, moving from the QNAP TS-873 to this custom Unraid 7 build solved every storage, noise, and compute bottleneck in my homelab. With all 10 drives mounted in the sound-dampened Fractal Define 7 PCGH and cooled by Noctua fans, the array sits at `37–39 °C` and the SanDisk ZFS pool runs at `28–31 °C` while remaining practically silent in the office.

Best of all, when the `92 TB` array eventually fills up, I don't need to rebuild a RAID group or buy a new appliance: I can just swap out the smallest disk (`4 TB` or `8 TB`) for another `16 TB` drive and let Unraid expand the filesystem in place.

[1]: /images/uploads/2026/11/custom-unraid-nas-qnap-migration.webp?v=6
[2]: /images/uploads/2026/11/fractal-define-7-storage-layout-hdd-trays.webp
[3]: /images/uploads/2026/11/asus-b550e-noctua-10gbe-sata-hba-layout.webp
[4]: /images/uploads/2026/11/unraid-7-main-array-zfs-cache-pool.webp
[5]: /images/uploads/2026/11/unraid-7-haos-kvm-virtual-machine.webp
[6]: /images/uploads/2026/11/unraid-7-user-shares-cache-to-array-exclusive.webp

