Skip to content

14 - NFS Setup

Date: 2026-02-11 Updated: 2026-04-08 (replace SSHFS automount with systemd service) Updated: 2026-08-11 (add Windows SMB client section) Hostname: pve IP address: 192.168.0.109


Overview

NFS is used in both directions:

  • Proxmox → LAN: exports the media storage pool so Nobara (and others) can mount it
  • Nobara → Proxmox: exports a backup HDD so Proxmox can rsync LXC backups onto it

The Windows side of the dual-boot machine uses SMB instead - see Windows Client (SMB) below.


Proxmox as NFS Server

Proxmox exports its storage to the local network.

Install NFS server

apt update
apt install nfs-kernel-server

/etc/exports

/mnt/storage 192.168.0.0/24(rw,sync,no_subtree_check,no_root_squash,fsid=1)
/mnt/disk1 192.168.0.0/24(rw,sync,no_subtree_check,no_root_squash,fsid=2)
/mnt/disk2 192.168.0.0/24(rw,sync,no_subtree_check,no_root_squash,fsid=3)
/mnt/disk3 192.168.0.0/24(rw,sync,no_subtree_check,no_root_squash,fsid=4)
/mnt/disk4 192.168.0.0/24(rw,sync,no_subtree_check,no_root_squash,fsid=5)

Apply and verify

exportfs -a
systemctl restart nfs-server
exportfs -v

Enable NFS server at boot

systemctl enable nfs-server

Note: the service name is nfs-server (not nfs-kernel-server) on recent Debian/Proxmox versions.


Nobara PC as NFS Server (backup target)

Nobara exports its backup HDD so Proxmox can rsync LXC dump files onto it.

Nobara /etc/exports

/mnt/hdd/Backup 192.168.0.109(rw,sync,no_subtree_check,no_root_squash)

Only the Proxmox host (192.168.0.109) has access - not the whole LAN.

Apply on Nobara

sudo exportfs -ra

Proxmox mounts it in /etc/fstab

192.168.0.100:/mnt/hdd/Backup /mnt/pve/nobara-backup nfs soft,timeo=600,retrans=3,_netdev,x-systemd.automount 0 0
  • soft - an unreachable server returns an I/O error to the application instead of blocking it forever. hard would make rsync wait indefinitely, and a wedged NFS mount on pve can take the backup jobs with it
  • timeo=600 + retrans=3 - about six minutes of tolerance before that I/O error
  • x-systemd.automount - mounts on first access, not at boot

timeo is in deciseconds, which cost four weekly syncs

This line read timeo=30 until 2026-10-04, written in the belief that it meant 30 seconds. From man 5 nfs on the host itself:

timeo=n   The time in deciseconds (tenths of a second) the NFS client waits for a
          response before it retries an NFS request.

          For NFS over TCP the default timeo value is 600 (60 seconds).

So the value was 3 seconds, a twentieth of the TCP default, and with retrans=3 and linear backoff the mount handed rsync an Input/output error (5) after roughly 18 seconds of server silence. Against a 3.5-hour rsync onto a USB HDD on a KDE desktop that is nowhere near enough: 2026-09-07, 09-14, 09-20 and 09-27 all ended rc=1, each with the same signature in /var/log/nobara-sync.log:

rsync: [Receiver] ERROR: cannot stat destination "/mnt/pve/nobara-backup/proxmox-host/": Input/output error (5)
rsync: [receiver] close failed on ".../vzdump-lxc-109-2026_09_11-02_11_01.tar.zst.WWYvZe": Input/output error (5)
rsync error: error in socket IO (code 10) at io.c(849) [sender=3.4.1]

Two things that look like the cause and are not. The kernel logs nfs: server 192.168.0.100 not responding, timed out in bursts, which reads like a network fault - on 2026-10-04 those bursts fell between 08:06 and 08:07 and the desktop's own uptime showed it booted at 08:07, so they were the machine starting, not dropping. And the desktop has a known SSH freeze issue, which makes "it froze again" the easy answer - but its journal held no suspend, hibernate or resume event on any of the failure days.

Changing this does not affect the live mount. The options come from the systemd-generated unit, so after editing /etc/fstab:

systemctl daemon-reload
systemctl cat 'mnt-pve-nobara\x2dbackup.mount' | grep ^Options   # the new value
findmnt -o OPTIONS /mnt/pve/nobara-backup                         # still the old one

Only an unmount and remount switches the running mount over, which means waiting for any sync in flight to finish.

Outcome: the first rc=0 in four weeks

The sync running when the fstab was edited finished at 15:00:30 on 2026-10-04, four hours after its 11:00 start, with rc=0 - the first success since 2026-09-13. It still ran on the old 3-second mount from end to end, so it proves the backlog cleared, not that timeo=600 works; the first real test of that is the next run.

rc=0 on its own does not prove a complete mirror. It says rsync did not error. What says the backup exists is comparing both ends:

Source Destination
proxmox dumps 870G 870G
proxmox-host 26G 26G
files in dump/ 732 732

The deficit that produced the 230 GB estimate is gone, and the live mount now reports soft,timeo=600,retrans=3.

Two things worth copying from how this was done, and one worth not copying.

Swapping the mount needs a unit, not a shell. The remount could only happen after the sync finished, which turned out to be 82 minutes away - longer than an interactive wait. A transient unit on the host does it without depending on any session staying open:

systemd-run --unit=nobara-remount-after-sync --collect bash -c '
while kill -0 <sync-pid> 2>/dev/null; do sleep 30; done
umount /mnt/pve/nobara-backup && ls /mnt/pve/nobara-backup >/dev/null
findmnt -o OPTIONS -n /mnt/pve/nobara-backup' 

The same pattern, with the lock file instead of the mount, bridges the one run that predates the flock: a holder tied to the running script's PID keeps the lock until that PID exits, so the next slot is skipped rather than starting a second rsync against a destination the first is still writing.

What not to copy: an unescaped $(date) inside the unit's command. It is expanded by the shell that builds the systemd-run line, not by the unit when it eventually runs, so the log opened with the timestamp of unit creation - 82 minutes before the work it was labelling. The ordering had to be re-established from a different line in the same log, the one quoting Sync done (rc=0), which could only have been read after the sync wrote it. A log line that timestamps itself wrong is worse than one with no timestamp, because it is believed.


Nobara as NFS Client (mounts Proxmox storage)

Do not use /etc/fstab for these mounts. Hard fstab NFS entries freeze Nobara's boot if Proxmox is offline. Use systemd automount instead.

Install NFS client on Nobara

sudo dnf install nfs-utils

Create mount points

sudo mkdir -p /mnt/storage /mnt/disk1 /mnt/disk2 /mnt/disk3 /mnt/disk4

Create systemd mount + automount units

Run as root (sudo -i) to avoid heredoc indentation issues:

for share in storage disk1 disk2 disk3 disk4; do
cat > /etc/systemd/system/mnt-${share}.mount << EOF
[Unit]
Description=NFS /mnt/${share} from Proxmox
After=network-online.target
Wants=network-online.target

[Mount]
What=192.168.0.109:/mnt/${share}
Where=/mnt/${share}
Type=nfs
Options=noauto,nfsvers=4,soft,timeo=30,retrans=3,_netdev

[Install]
WantedBy=multi-user.target
EOF
cat > /etc/systemd/system/mnt-${share}.automount << EOF
[Unit]
Description=Automount /mnt/${share}

[Automount]
Where=/mnt/${share}
TimeoutIdleSec=600

[Install]
WantedBy=multi-user.target
EOF
done

Enable and start

systemctl daemon-reload
systemctl enable --now mnt-storage.automount mnt-disk1.automount mnt-disk2.automount mnt-disk3.automount mnt-disk4.automount

Verify

ls /mnt/storage
df -h | grep mnt/

The first ls triggers the automount. All 5 shares should appear in df -h.

How it works

  • The .automount unit watches the directory
  • First access triggers the mount automatically
  • After 600 seconds (10 min) of inactivity it unmounts
  • If Proxmox is offline: soft + timeo=30 + retrans=3 gives up after roughly 18 seconds, not the ~90 this document claimed until 2026-10-04 - timeo counts deciseconds, so 30 is 3 seconds (see the correction above)

This direction keeps the short timeout on purpose. The opposite mount, pve writing to the desktop, was raised to timeo=600 because an unattended rsync should wait out a busy server rather than abort after 18 seconds. Here a human is waiting on a file manager, so failing fast beats a window frozen for six minutes. Same option, opposite correct value, because the client is a person in one case and a batch job in the other.


Nobara SSHFS Mount (LXC 109 claude-mgmt)

Nobara mounts /root from LXC 109 (claude-mgmt) via SSHFS. NFS server cannot run inside an unprivileged LXC, so SSHFS is used instead.

Prerequisites

Root's SSH key on Nobara must be in LXC 109's authorized_keys. Since LXC 109 has password auth disabled, add it via the existing user key:

sudo ssh-keygen -t ed25519 -f /root/.ssh/id_ed25519 -N ""
sudo cat /root/.ssh/id_ed25519.pub | ssh root@192.168.0.204 "cat >> /root/.ssh/authorized_keys"

Also add LXC 109 to root's known_hosts on Nobara:

sudo ssh-keyscan 192.168.0.204 | sudo tee -a /root/.ssh/known_hosts

Create mount point

sudo mkdir -p /mnt/claudemgmt

Note: no hyphen in claudemgmt - systemd unit file names encode hyphens as \x2d which causes shell escaping issues.

Create systemd service

A service unit is used instead of mount+automount. The automount approach caused Dolphin and the KDE desktop to lag/freeze for up to 15 seconds on each access attempt when LXC 109 was offline (the mount unit blocked D-Bus via systemd-hostnamed). The service approach with reconnect mounts once and maintains the connection in the background.

sudo tee /etc/systemd/system/mnt-claudemgmt.service << 'EOF'
[Unit]
Description=SSHFS /root from LXC 109 claude-mgmt
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
ExecStartPre=/bin/bash -c 'ssh -o ConnectTimeout=3 -o BatchMode=yes root@192.168.0.204 exit 2>/dev/null'
ExecStart=/usr/bin/sshfs root@192.168.0.204:/root /mnt/claudemgmt \
    -f \
    -o allow_other \
    -o IdentityFile=/home/nex/.ssh/id_ed25519 \
    -o reconnect \
    -o ServerAliveInterval=5 \
    -o ServerAliveCountMax=2
ExecStop=/bin/fusermount3 -u /mnt/claudemgmt
Restart=on-failure
RestartSec=30

[Install]
WantedBy=multi-user.target
EOF

Enable and start

sudo systemctl daemon-reload
sudo systemctl enable --now mnt-claudemgmt.service
ls /mnt/claudemgmt

Should show: homelab learning youtube

How it works

  • At boot: ExecStartPre checks SSH connectivity (3s timeout). If LXC 109 is down, fails fast and retries every 30 seconds.
  • When connected: SSHFS runs in foreground (-f), systemd owns the process.
  • If LXC 109 goes down: reconnect + ServerAliveInterval=5, ServerAliveCountMax=2 detects the drop in 10 seconds, retries in background. File accesses return I/O errors immediately - no desktop freeze.
  • When LXC 109 comes back: reconnect re-establishes automatically, no manual intervention needed.
  • If reconnect gives up: process exits, Restart=on-failure restarts the service after 30 seconds.

Windows Client (SMB)

The dual-boot desktop (192.168.0.100) reaches the same storage over SMB when booted to Windows, not over NFS.

Why SMB and not NFS

The Windows "Client for NFS" is an optional feature, speaks NFSv2/v3 only, and has no real UID mapping - every write lands as an anonymous UID unless you set AnonymousUid/AnonymousGid in the registry. Samba was already running on the Proxmox host, so the Windows side needed no new server component at all.

Shares on the Proxmox host

Defined in /etc/samba/smb.conf:

Share Path Access
Storage /mnt/storage smbuser, normal permissions
Media /mnt/storage/media smbuser, normal permissions
Downloads /mnt/storage/media/downloads smbuser, normal permissions
AllDisks /mnt smbuser, runs as root - full access everywhere

The AllDisks share

The default shares cannot write everywhere: /mnt/storage itself is root:root 755, and the immich/ and syncthing/ subtrees are owned by UID 100000/101000 because unprivileged LXCs use a +100000 offset. AllDisks solves this by running as root:

[AllDisks]
   comment = All Disks (disk1-4, storage) - full access
   path = /mnt
   browseable = yes
   read only = no
   valid users = @users
   force user = root
   force group = root
   inherit owner = unix only
   create mask = 0664
   directory mask = 0775

inherit owner = unix only is the part that matters. Without it, everything created from Windows would be owned by host root, which appears as nobody inside an unprivileged container - the Immich and Syncthing containers could then read but not modify their own files. With it, new files inherit the parent directory's owner, so container-owned subtrees stay container-owned.

This is a root-equivalent share. Anyone who can authenticate as smbuser can overwrite or delete anything under /mnt, including .snapraid.content. It is LAN-only and guest access is denied (valid users blocks the map to guest = bad user fallback).

Mounting from Windows

In an Administrator prompt:

net use Z: \\192.168.0.109\AllDisks /user:smbuser <password> /persistent:yes

The password is not in this repository - it is in private/.env, which is gitignored. The same smbuser account is used by the Home Assistant backup job writing to \\192.168.0.109\Storage\backup\proxmox, so resetting it with smbpasswd -a smbuser requires updating the HA side too, or those backups fail silently.

To store the credential separately instead of passing it inline:

cmdkey /add:192.168.0.109 /user:smbuser /pass:<password>
net use Z: \\192.168.0.109\AllDisks /persistent:yes

If net use still prompts for a username after cmdkey, the stored credential was rejected - Windows falls back to prompting rather than reporting the failure. Clear it with cmdkey /delete:192.168.0.109 and verify the password from the Proxmox host before retrying:

smbclient //192.168.0.109/AllDisks -U "smbuser%<password>" -c "ls"

NT_STATUS_LOGON_FAILURE there means the password is wrong. To rule out a disabled account rather than a bad password, check the flags - [U] is a normal enabled user, [D] is disabled:

pdbedit -L -v smbuser | grep -i flags

Applying smb.conf changes

smbd re-reads smb.conf when a client opens a new connection, so a share edit takes effect on the next mount without a restart. systemctl reload smbd only matters for sessions that are already open. Validate first:

testparm -s

Notes

  • fsid= is required for non-root filesystem exports on Proxmox
  • no_root_squash allows root access from the client
  • Nobara runs NFSv4 only (no rpcbind) - showmount -e will fail from Proxmox, but mounts work fine
  • Nobara is not always on - the soft mount on Proxmox ensures it never freezes the host
  • If NFS shares stop working after a Proxmox reboot: check systemctl status nfs-server on the Proxmox host - it may need systemctl start nfs-server
  • Automount units must not have After=network-online.target - this creates an ordering cycle with local-fs.target on Fedora/Nobara and causes the unit to silently fail at boot. Network dependency belongs only in the .mount unit, not the .automount unit. Access-triggered automounts work correctly without it since the network is already up when the user first accesses the path.