NFS (Network File System) lets Linux servers mount a directory from another server as if it were local. It’s a common way to share uploads between several web servers, give app servers access to a storage server, or centralise backups. NFS is simple and fast, but it was designed for trusted networks: on a VPS, run it only over a private network or a VPN.

Security first

Classic NFS (with sec=sys) trusts the client’s claimed user IDs, and sends data unencrypted. Therefore:

  • never expose NFS (port 2049) to the internet;
  • connect servers through a private network or a WireGuard/Tailscale tunnel, and export only to those addresses. See WireGuard and Tailscale;
  • for untrusted networks, use SFTP/SSHFS instead. See SSHFS.

The examples below assume the server is reachable at 10.8.0.1 over a VPN, and clients are in 10.8.0.0/24.

Server setup

sudo apt install nfs-kernel-server        # Debian/Ubuntu
sudo dnf install nfs-utils                # RHEL/AlmaLinux
sudo mkdir -p /srv/nfs/shared
sudo chown nobody:nogroup /srv/nfs/shared     # or a specific user (see UID notes)

Define exports in /etc/exports:

/srv/nfs/shared  10.8.0.0/24(rw,sync,no_subtree_check,root_squash)
Option Meaning
rw / ro Read-write / read-only
sync Write to disk before replying (safer; async is faster but risky)
root_squash Client root becomes nobody (default; keep it)
no_subtree_check Recommended for reliability
all_squash,anonuid=…,anongid=… Map all users to one UID (simple shared folders)

Apply and start:

sudo exportfs -ra
sudo exportfs -v
sudo systemctl enable --now nfs-server      # nfs-kernel-server on Debian/Ubuntu

Firewall, allowing only the client network:

sudo ufw allow from 10.8.0.0/24 to any port 2049 proto tcp

NFSv4 needs only TCP 2049. NFSv3 also uses rpcbind (111) and dynamic ports, which is another reason to prefer v4. See UFW.

Client setup

sudo apt install nfs-common               # Debian/Ubuntu
sudo dnf install nfs-utils                # RHEL family
sudo mkdir -p /mnt/shared
sudo mount -t nfs4 10.8.0.1:/srv/nfs/shared /mnt/shared
df -h /mnt/shared

Permanent mount in /etc/fstab:

10.8.0.1:/srv/nfs/shared  /mnt/shared  nfs4  defaults,_netdev,nofail,noatime  0 0

_netdev waits for the network, and nofail prevents a boot failure if the server is unreachable. Add x-systemd.automount to mount on first access. See mount options.

User and permission mapping

With default sec=sys, permissions are checked by numeric UID/GID. The same user must have the same UID on server and clients, or files show the wrong owner (or nobody). Options:

  • create the app user with a fixed UID everywhere (useradd -u 1500 app);
  • use all_squash,anonuid=1500,anongid=1500 for a simple shared directory.

See create a user on Linux.

Performance and reliability notes

  • NFS adds network latency to every file operation. It’s fine for uploads and shared assets, but poor for databases. Don’t put a database’s data directory on NFS.
  • Use hard mounts (the default) for data integrity. Processes wait (rather than corrupting data) if the server disappears.
  • For web apps on several servers, consider object storage (S3/MinIO) instead of NFS, for uploads. See MinIO.

Troubleshooting

Problem Check
access denied by server Client IP not in /etc/exports; run exportfs -ra
Mount hangs Firewall/VPN; showmount -e 10.8.0.1 (v3) or rpcinfo -p
Files owned by nobody UID mismatch, or root_squash (expected for root)
Stale file handle Export changed or server restarted; remount

Quick reference

Task Command
Exports /etc/exports → exportfs -ra
Show exports exportfs -v
Mount mount -t nfs4 server:/path /mnt
fstab … nfs4 defaults,_netdev,nofail 0 0
Port TCP 2049 (VPN/private network only)

Related: Samba server and storage VPS.

  • Linux
  • Storage
  • Networking