feat: replace SMB with NFS, mount NFS inside Podman VM#82
Merged
smartwatermelon merged 19 commits intomainfrom Mar 17, 2026
Merged
feat: replace SMB with NFS, mount NFS inside Podman VM#82smartwatermelon merged 19 commits intomainfrom
smartwatermelon merged 19 commits intomainfrom
Conversation
Default is volume2 (verified via showmount -e). Used by mount-nas-media.sh to construct the NFS export path: hostname:/volumeN/sharename Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
NFS eliminates VirtioFS/SMB oplock conflict that caused .smbdelete ghost files when deleting torrents while Podman VM is running. - mount_smbfs → mount -t nfs with resvport,rw,soft,bg,intr,rsize/wsize=65536 - Removed credential embedding (NFS uses host-based auth) - Added NAS_VOLUME template variable for Synology export path - Updated test_mount to check NFS mount table Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Renamed setup_persistent_smb_mount → setup_persistent_nfs_mount - Removed credential retrieval/embedding (NFS uses host-based auth) - Added NAS_VOLUME template substitution for export path - Updated troubleshooting output with NFS-specific commands Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Unlike mount_smbfs which has a user-level helper, NFS mount on macOS requires root privileges. The mount script now uses sudo for mount and umount commands, paired with a sudoers rule deployed during setup. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Use /sbin/mount_nfs and /sbin/umount (full paths required for sudoers match) - Validate NAS_SHARE_NAME against safe character pattern before sudoers write - Document wildcard as intentional (allows variable mount options, both users admin) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
VirtioFS (Podman container) writes files through a separate NFS client connection than the host scripts that read them. Without actimeo=2, the NFS attribute cache (default 30s) causes find to return empty results for newly written files, breaking transmission-done's media detection. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When Transmission completes a download, VirtioFS holds file descriptors on all files it served to the container. If the host-side post-done script tries to rename/move files while those FDs are open, NFS creates .nfs.* silly-rename files that block directory operations. Fix: the container-side post-done script now removes the completed torrent from Transmission via RPC (keeping files on disk) before writing the trigger file. This makes Transmission close its file handles, allowing VirtioFS to release the FDs, so the host-side script can freely rename and move files. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Transmission's RPC returns HTTP 409 for session ID retrieval. With curl -f (fail on HTTP error) and set -euo pipefail, the script dies before writing the trigger file. Changed to -s (silent only) with || true guards on all RPC pipelines. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
macOS TCC blocks LaunchAgent-spawned processes from reading NFS-mounted files unless the process has Network Volume access. The noowners flag (same as SMB had) makes macOS ignore UID mapping and treat all files as owned by the current user, bypassing TCC restrictions. Without this, transmission-done gets "Operation not permitted" on ls/find. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The torrent-remove was a workaround for VirtioFS FD caching. With NFS mounted directly inside the VM (bypassing VirtioFS), it's no longer needed — and it prevented seeding. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Bypasses VirtioFS for NFS-backed data, eliminating Apple Virtualization framework FD caching that caused .nfs.* silly-rename files on the host. - Add Section 2b: creates systemd .mount unit inside VM via SSH - Change compose volume from VirtioFS passthrough to VM-internal NFS - Add NFS mount check to podman-machine-start.sh before compose up - Validate NAS config variables (non-empty + safe characters) - Quote all variables in SSH command strings - Exit on NFS mount failure (don't continue with broken mount) The VM now mounts the NAS share directly. The host-side NFS mount remains for Plex, Finder, and FileBot. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
podman-compose validates that volume source paths exist on the macOS host. The NFS data volume (/var/mnt/DSMedia) only exists inside the VM, causing compose to fail. podman run bypasses this and correctly bind-mounts from the VM filesystem. - Remove podman-compose install (no longer needed) - Replace compose up with podman run in startup wrapper and initial start - Quote all variable expansions in heredoc wrapper script - Document variable dependencies in wrapper heredoc - Keep compose.yml as reference documentation only Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Convert **Step N:** bold text to ### headings (MD036) and disambiguate duplicate heading names (MD024). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
These variables were only set inside the compose template conditional, but are also used by the podman run command and startup wrapper. If the compose template was missing, they'd be empty. Post-push loop iteration 2: address Sentry finding at line 575. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Consistent with NAS_HOSTNAME and NAS_SHARE_NAME validation — NAS_VOLUME is also used in SSH commands and systemd unit files. Post-push loop iteration 3: address Sentry finding at line 339. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- chown root:wheel on sudoers file before mv (sudo ignores non-root-owned files) - Add watch directory to container mkdir block (podman run doesn't auto-create) Post-push loop iteration 3: address Sentry findings. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
5 tasks
smartwatermelon
added a commit
that referenced
this pull request
Mar 17, 2026
## Summary Updates all active documentation to reflect the SMB→NFS migration and podman compose→podman run changes from PR #82. - **7 files updated**, 22 edits across README, plex-setup docs, operator guide, keychain docs, environment variables, container proposal, and prerequisites - Historical docs (plans/, code-review) left as-is — they document decisions at a point in time - Key theme: NFS uses host-based auth (no credential embedding), mount scripts use `sudo mount_nfs` with `noowners` ## Test plan - [x] No stale SMB references in active docs (verified via grep) - [x] `NAS_VOLUME` documented in environment-variables.md - [x] Container proposal §5.1 and §5.2 reflect current VirtioFS bypass architecture - [x] Troubleshooting commands updated (mount_nfs, showmount -e) - [x] markdownlint passes 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Code Bot <claude-code@smartwatermelon.github> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
.smbdelete*ghost files when VirtioFS held file descriptors open..nfs.*silly-rename files.podman composewithpodman runbecausepodman-composevalidates that volume source paths exist on the macOS host, but the VM-internal NFS mount (/var/mnt/DSMedia) only exists inside the VM.What changed
config/config.conf.templateNAS_VOLUMEfor NFS export pathapp-setup/templates/mount-nas-media.shmount_nfswithnoowners,actimeo=2) + sudoers for root mountapp-setup/plex-setup.shNAS_VOLUMEsubstitution, deploy sudoers ruleapp-setup/containers/transmission/compose.yml__NFS_MOUNT_POINT__:/data(reference only)app-setup/podman-transmission-setup.shpodman run, validate NAS configapp-setup/templates/transmission-post-done.shArchitecture
Synology NFS export requirements
/volume2/DSMediato10.0.12.0/22Test plan
.nfs.*remnants🤖 Generated with Claude Code