From 9dee75e2a03a2a66c5023c76a661c5d568aa50c3 Mon Sep 17 00:00:00 2001 From: poprhythm Date: Sat, 14 Mar 2026 02:10:52 +0000 Subject: [PATCH] docs: update NAS connection strategies for Immich and nas_library/nas_family mounts --- NAS-CONNECTION-STRATEGIES.md | 123 +++++++++++++++++++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 NAS-CONNECTION-STRATEGIES.md diff --git a/NAS-CONNECTION-STRATEGIES.md b/NAS-CONNECTION-STRATEGIES.md new file mode 100644 index 0000000..02e6af7 --- /dev/null +++ b/NAS-CONNECTION-STRATEGIES.md @@ -0,0 +1,123 @@ +# NAS Connection Strategies + +Summary of how each service connects to the unRAID NAS (192.168.1.192 / 192.168.1.4). + +--- + +## Strategy Overview + +| Strategy | Services | Reliability | Notes | +|----------|----------|-------------|-------| +| Docker NFS named volume | Plex, qBittorrent, Audiobookshelf | ⚠️ Fragile | Soft mount; stale handles on drive spin-down | +| systemd permanent mount + bind | Calibre (import), oCIS, Immich | ✅ Solid | Hard NFS; permanent; no idle cycling | +| systemd automount + bind | Backrest, Filebrowser-Colleen-HD | ✅ Solid | CIFS; TimeoutIdleSec=0; never unmounts once triggered | +| Local SSD + lsyncd sync | Calibre (library) | ✅ Best | SQLite never touches NFS; NAS copy is read-only replica | + +--- + +## Per-Service Details + +### Plex +- **Strategy**: Docker NFS named volume (`nas_media`, external) +- **NAS path**: `192.168.1.192:/mnt/user/media` +- **Mount options**: `nfsvers=4,soft,nolock,timeo=14,rsize=8192,wsize=8192` +- **Container path**: `/data` +- **Risk**: `soft` + `timeo=14` means EIO after 14 retries × 0.1s = ~1.4s timeout. If unRAID drives spin down, Plex gets I/O errors. Works in practice because Plex opens/closes file handles per-request rather than holding them open. + +### qBittorrent (all 3 instances: open, vpn, vpn2) +- **Strategy**: Docker NFS named volume (`nas_media`, same as Plex) +- **NAS path**: `192.168.1.192:/mnt/user/media` +- **Container path**: `/data` +- **Risk**: Same as Plex. Active downloads write continuously — more exposure to spin-down EIO than Plex. + +### Calibre +- **Strategy (library)**: Local SSD + lsyncd one-way sync to NAS + - Local: `/srv/calibre/library` → container `/config/Calibre Library` + - lsyncd syncs to `/mnt/nas_books/calibre/Calibre Library` within ~15 seconds + - SQLite (`metadata.db`, `notes.db`) never touches NFS — eliminates `apsw.IOError` +- **Strategy (import)**: systemd permanent NFS mount + Docker bind mount + - Host: `/mnt/nas_books/calibre-import` (permanent NFS, no automount) + - Container: `/calibre-import` + - NAS path: `192.168.1.192:/mnt/user/media/books` +- **Mount options**: `nfsvers=3,hard,rw,noatime` +- **Why permanent (not automount)**: Docker bind mounts snapshot the mount reference at container start. If autofs cycles between restarts, the container gets a stale handle. Permanent mount avoids this entirely. +- **Manual book add** (when auto-add fails): + ```bash + docker stop calibre + docker run --rm \ + -v /srv/calibre/library:/config/Calibre\ Library \ + -v /mnt/nas_books/calibre-import:/calibre-import \ + lscr.io/linuxserver/calibre:latest \ + sh -c "cd /opt/calibre && LD_LIBRARY_PATH=/opt/calibre ./calibredb add \ + --library-path '/config/Calibre Library' '/calibre-import/' 2>&1" + docker start calibre + ``` + +### Filebrowser-Colleen-HD +- **Strategy**: systemd CIFS automount + Docker bind mount +- **Host mount**: `/mnt/nas_backup` (CIFS `//192.168.1.192/backup`) +- **Container path**: `/folder` +- **Mount options**: `vers=3.0,uid=0,gid=0,noperm,noserverino,soft,TimeoutIdleSec=0` +- **Why CIFS**: Container runs as root; CIFS with `uid=0,gid=0` maps all files to root +- **TimeoutIdleSec=0**: Never auto-unmounts once triggered; safe for persistent container access + +### Backrest (backup service) +- **Strategy**: systemd CIFS automount + Docker bind mount (same mount as Filebrowser) +- **Host mount**: `/mnt/nas_backup/nas-docker-data` +- **Container path**: `/backup-export` +- **Same CIFS mount as Filebrowser-Colleen-HD** (`mnt-nas_backup`) + +### oCIS (ownCloud Infinite Scale) +- **Strategy**: systemd permanent NFS mount + Docker bind mount +- **Host mount**: `/mnt/nas_owncloud` (permanent NFS, no automount) +- **Container path**: `/var/lib/ocis/storage/users` +- **NAS path**: `192.168.1.192:/mnt/user/owncloud` +- **Mount options**: `nfsvers=3,hard,rw,noatime` +- **Local config/index**: `/srv/ocis/data` and `/srv/ocis/config` stay on local SSD + +### Immich +- **Strategy**: systemd permanent NFS mount + Docker bind mount +- **Host mount**: `/mnt/nas_family` (NFS `192.168.1.192:/mnt/user/family`) +- **Container path**: `/usr/src/app/upload` → `/mnt/nas_family/immich-library` +- **Mount options**: `nfsvers=3,hard,rw,noatime` +- **Why permanent (not automount)**: Drive doesn't spin down; permanent mount avoids any autofs cycling risk +- **Database**: PostgreSQL on local SSD (`/var/lib/postgresql/data`) — correct, not on NFS + +### Audiobookshelf +- **Strategy**: Docker NFS named volume +- **NAS path**: `192.168.1.192:/mnt/user/media/audiobooks` +- **Container path**: `/audiobooks` +- **Config/metadata**: Local SSD (`/srv/audiobookshelf/`) — correct + +--- + +## systemd Mount Units + +| Unit | Type | Share | Status | +|------|------|-------|--------| +| `mnt-nas_books.mount` | NFS permanent | `:/mnt/user/media/books` | enabled, always up | +| `mnt-nas_owncloud.mount` | NFS permanent | `:/mnt/user/owncloud` | enabled, always up | +| `mnt-nas_family.mount` | NFS permanent | `:/mnt/user/family` | enabled, always up | +| `mnt-nas_library.mount` | CIFS automount | `//192.168.1.192/library` | triggered on access, TimeoutIdleSec=0 | +| `mnt-nas_library.automount` | CIFS automount | — | enabled | +| `mnt-nas_backup.mount` | CIFS automount | `//192.168.1.192/backup` | triggered on access, TimeoutIdleSec=0 | +| `mnt-nas_backup.automount` | CIFS automount | — | enabled | + +--- + +## Known Issues & Gotchas + +- **Docker NFS named volumes**: The `nas_media` volume uses `soft,nolock,timeo=14,nfsvers=4`. This is fragile — short timeout means EIO on spin-up. Plex/qBittorrent survive because they open/close handles per request. If either starts having I/O errors, migrate to the systemd permanent mount pattern. +- **Docker bind mounts + automount = stale handles**: Docker snapshots the mount reference at container start. If autofs cycles (unmount + remount) between container restarts, the container's bind mount goes stale while the host sees the path fine. Fix: use permanent `.mount` (no `.automount`) for any share that Docker containers bind-mount. +- **SQLite on NFS**: Never store SQLite databases (calibre `metadata.db`/`notes.db`, any app DB) on NFS. Even with `hard` mounts and NLM locking, transient NFS errors cause `SQLITE_IOERR`. Keep SQLite on local SSD; sync non-SQLite files to NAS if sharing is needed. +- **CIFS vs NFS for file permissions**: CIFS enforces server-side ACLs based on the SMB user. Files created via NFS by uid=99 with mode 600 are inaccessible via CIFS. Use NFS when container needs uid-mapped access to NFS-created files. +- **lsyncd for NAS sync**: inotify-based, syncs within ~15 seconds of any write. Config at `/etc/lsyncd/lsyncd.conf.lua`. Log at `/var/log/lsyncd.log`. + +--- + +## Recommended Migration (future) + +Plex, qBittorrent, Immich, and Audiobookshelf all use the fragile Docker NFS named volume pattern. The safer pattern is systemd permanent mount + Docker bind mount (as used by calibre-import and oCIS). This would involve: +1. Create `/mnt/nas_media` systemd permanent mount unit +2. Update compose files to use bind mounts instead of the external `nas_media` volume +3. `docker volume rm nas_media` after redeployment