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=1500for a simple shared directory.
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
hardmounts (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.