Host reboot broke the nas_media Docker NFS volume for 6+ hours (plex, qbittorrent x3, navidrome, sonarr, jellyfin all down): nfs-utils 2.6.4 no longer accepts the ":/path" + addr= device split, and separately Docker's local volume driver can't do nfsvers=4 (calls mount(2) directly, skipping the mount.nfs helper's version negotiation). Volume recreated with an explicit host:/path device and vers=3. Updating docs/comments to match the corrected live config so the next person doesn't recreate the same broken volume from the stale example.
8.6 KiB
8.6 KiB
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=14means 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 Librarywithin ~15 seconds - SQLite (
metadata.db,notes.db) never touches NFS — eliminatesapsw.IOError
- Local:
- 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
- Host:
- 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):
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/<book>' 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=0maps 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/dataand/srv/ocis/configstay on local SSD
Immich
- Strategy: systemd permanent NFS mount + Docker bind mount
- Host mount:
/mnt/nas_family(NFS192.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_mediavolume usessoft,nolock,timeo=14,vers=3. 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's local NFS volume driver requires
host:/pathindevice, and can't do NFSv4: it callsmount(2)directly rather than shelling out to themount.nfs/mount.nfs4userspace helper. Two consequences, both hit in the 2026-09-16 outage below: (1) nfs-utils 2.6.4 (Ubuntu 24.04) rejects the olddevice=":/path"+ separateo=addr=<ip>split form - the host must be in the device string itself now (device=<ip>:/path); (2)vers=4/nfsvers=4fails with "protocol not supported" via the raw syscall even though a manualmount -t nfs -o nfsvers=4 ...succeeds (that goes throughmount.nfs, which does version negotiation the raw syscall skips). Stick tovers=3for any Docker-managed NFS volume on this host. - 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 withhardmounts and NLM locking, transient NFS errors causeSQLITE_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.
Self-Healing (nas_family / nas_books / nas_owncloud)
As of 2026-08-25, stale-handle recovery for immich, calibre, and oCIS is
automated instead of requiring manual systemctl restart + docker restart:
<container>-mount-ready.service(immich-mount-ready,calibre-mount-ready,ocis-mount-ready) —BindsTo=mnt-nas_*.mount, restarts the container whenever its mount unit restarts.nfs-mount-heal.timer(every 5 min) →nfs-mount-heal.sh— does a nested file read (not justlsthe mount root, which can succeed even when nested handles are stale) against each of the three mounts. On failure it force- remounts the mount unit, which cascades into the container restart above, then sends a Telegram alert.
Host files backed up at system-config/nfs-self-heal/ in this repo (see its
README for the reinstall procedure — these are systemd units, not
docker-compose, so they don't redeploy via Portainer).
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:
- Create
/mnt/nas_mediasystemd permanent mount unit - Update compose files to use bind mounts instead of the external
nas_mediavolume docker volume rm nas_mediaafter redeployment