NFS (Network File System) is a distributed filesystem protocol that allows clients to access files over a network as if they were on local storage.

Addresses below are RFC 5737 documentation ranges or placeholders - swap in your own.

1. Overview#

NFS enables transparent file sharing between UNIX/Linux systems using a server-client model. The server exports directories, and clients mount them over the network. NFS uses Remote Procedure Calls (RPC) for communication.

Key characteristics:

  • Kernel-level implementation - high performance with minimal userspace overhead
  • Stateless (v3) or stateful (v4) - NFSv4 tracks open files and locks
  • POSIX-compatible - supports standard file permissions, locking, and access semantics
  • Widely supported - available on virtually every Linux distribution, macOS, and Windows (via Services for NFS)

2. NFSv3 vs NFSv4 Comparison#

FeatureNFSv3NFSv4 / NFSv4.1 / NFSv4.2
StatefulnessStateless (separate lock protocol)Stateful (integrated locking)
PortsMultiple (2049, 111, dynamic mountd/lockd/statd)Single port 2049 only
Firewall friendlinessPoor - requires rpcbind + dynamic portsExcellent - only port 2049
AuthenticationAUTH_SYS (UID/GID), optional KerberosAUTH_SYS and Kerberos (native)
Pseudo-filesystemNo - clients mount specific pathsYes - single namespace with fsid=0 root
ACL supportPOSIX ACLs via sidebandNFSv4 ACLs (richer model, Windows-compatible)
DelegationNoYes - clients can cache aggressively when granted delegation
pNFS (parallel NFS)NoYes (v4.1+) - data striped across multiple servers
Server-side copyNoYes (v4.2) - copy_file_range offload
Mandatory lockingNoYes

Recommendation: Use NFSv4 or later unless legacy clients require NFSv3. NFSv4.2 adds server-side copy and sparse file support.

3. Server Installation#

Debian/Ubuntu#

sudo apt update
sudo apt install -y nfs-kernel-server

RHEL/CentOS/Rocky#

sudo dnf install -y nfs-utils

Arch Linux#

sudo pacman -S nfs-utils

Enable and Start#

sudo systemctl enable --now nfs-server

For NFSv4 only (disable NFSv3):

# /etc/nfs.conf
[nfsd]
vers3=n
vers4=y
vers4.1=y
vers4.2=y

4. Server Configuration#

Creating a Shared Directory#

# Create the export directory
sudo mkdir -p /srv/nfs/share

# Set ownership (nobody:nogroup for anonymous access)
sudo chown nobody:nogroup /srv/nfs/share
sudo chmod 755 /srv/nfs/share

Configuring Exports#

Edit /etc/exports to define shared directories and access rules:

# NFSv4 pseudo-root (recommended)
/srv/nfs        *(ro,fsid=0,no_subtree_check)

# Shared directory under the pseudo-root
/srv/nfs/share  192.0.2.0/24(rw,sync,no_subtree_check,no_root_squash)
/srv/nfs/share  198.51.100.0/24(ro,sync,no_subtree_check)

Apply changes:

sudo exportfs -rav

NFSv4 Pseudo-Filesystem#

NFSv4 uses a pseudo-filesystem rooted at the export with fsid=0. Clients mount relative to this root:

# Server exports:
/srv/nfs         *(fsid=0,ro,no_subtree_check)
/srv/nfs/share   192.0.2.0/24(rw,sync,no_subtree_check)
/srv/nfs/home    192.0.2.0/24(rw,sync,no_subtree_check)

# Client mounts the pseudo-root:
mount -t nfs4 server:/ /mnt/nfs

# Or mounts a specific sub-export:
mount -t nfs4 server:/share /mnt/share

5. Export Parameters#

ParameterDescription
fsid=0Marks the NFSv4 pseudo-root export
roRead-only access
rwRead-write access
syncWrite data to disk before replying (default, safe)
asyncReply before data is written to disk (faster, risk of corruption on crash)
no_subtree_checkDisables subtree checking (default, improves performance and reliability)
subtree_checkEnables subtree checking (not recommended)
root_squashMaps remote root (UID 0) to anonymous user (default)
no_root_squashAllows remote root to retain root privileges
all_squashMaps all remote users to anonymous
no_all_squashPreserves remote user UIDs/GIDs (default)
anonuid=<uid>UID for the anonymous user
anongid=<gid>GID for the anonymous user
crossmntAutomatically exports filesystems mounted under this export
sec=<mode>Security flavor: sys, krb5, krb5i, krb5p
insecureAllows connections from ports above 1024

6. Firewall Configuration#

NFSv4 (Port 2049 Only)#

sudo firewall-cmd --add-service=nfs --permanent
sudo firewall-cmd --reload

NFSv3 (Multiple Ports)#

NFSv3 requires additional services. Fix their ports for firewall rules:

# /etc/nfs.conf - pin auxiliary service ports
[mountd]
port=20048

[lockd]
port=32803
udp-port=32803

[statd]
port=32764
sudo firewall-cmd --add-service=nfs --permanent
sudo firewall-cmd --add-service=mountd --permanent
sudo firewall-cmd --add-service=rpc-bind --permanent
sudo firewall-cmd --add-port=32803/tcp --permanent
sudo firewall-cmd --add-port=32803/udp --permanent
sudo firewall-cmd --add-port=32764/tcp --permanent
sudo firewall-cmd --add-port=32764/udp --permanent
sudo firewall-cmd --reload

7. Client Configuration#

Installing the Client#

# Debian/Ubuntu
sudo apt install -y nfs-common

# RHEL/CentOS/Rocky
sudo dnf install -y nfs-utils

# Arch Linux
sudo pacman -S nfs-utils

Mounting an NFS Share#

# NFSv4 mount
sudo mount -t nfs4 server:/share /mnt/nfs

# NFSv3 mount (explicit version)
sudo mount -t nfs -o vers=3 server:/srv/nfs/share /mnt/nfs

Persistent Mounting via fstab#

# NFSv4
server:/share  /mnt/nfs  nfs4  defaults,_netdev  0 0

# NFSv3
server:/srv/nfs/share  /mnt/nfs  nfs  vers=3,defaults,_netdev  0 0

Automount with systemd#

# /etc/systemd/system/mnt-nfs.mount
[Unit]
Description=NFS Share Mount
After=network-online.target
Wants=network-online.target

[Mount]
What=server:/share
Where=/mnt/nfs
Type=nfs4
Options=defaults,_netdev

[Install]
WantedBy=multi-user.target
sudo systemctl enable --now mnt-nfs.mount

8. Client Mount Options#

OptionDescriptionRecommended Value
hardRetry NFS requests indefinitely (default)Yes for production
softReturn error after retrans retriesOnly for non-critical mounts
intrAllow signals to interrupt hung NFS operationsDeprecated in modern kernels
rsize=<bytes>Maximum read request size1048576 (1 MiB) for NFSv4
wsize=<bytes>Maximum write request size1048576 (1 MiB) for NFSv4
timeo=<decisec>Timeout before first retransmission (in 1/10 sec)600 (60 sec) for TCP
retrans=<n>Number of retransmissions before returning error (soft) or major timeout (hard)2 (default)
sec=<mode>Security mechanism: sys, krb5, krb5i, krb5psys (default) or krb5p
noatimeDo not update access timesRecommended for performance
nolockDisable NLM locking (NFSv3 only)Only for read-only or specific workloads
actimeo=<sec>Attribute cache timeout60 for low-change data
nconnect=<n>Number of TCP connections (kernel 5.3+, NFSv4.1+)4-16 for throughput
_netdevWait for network before mountingAlways use in fstab

9. Kerberos Authentication#

Kerberos provides strong authentication and optional encryption for NFS, replacing the trust-based AUTH_SYS (UID/GID) model.

Security Levels#

LevelAuthenticationIntegrityEncryptionPerformance
sec=sysUID/GID (no auth)NoNoFastest
sec=krb5Kerberos ticketNoNoMinimal overhead
sec=krb5iKerberos ticketYes (checksums)NoModerate overhead
sec=krb5pKerberos ticketYesYes (full payload)Significant overhead

Server-Side Setup#

# Install Kerberos client libraries
sudo apt install -y krb5-user  # Debian/Ubuntu
sudo dnf install -y krb5-workstation  # RHEL

# Create NFS service principal in your KDC
kadmin -q "addprinc -randkey nfs/<server-fqdn>@REALM"
kadmin -q "ktadd -k /etc/krb5.keytab nfs/<server-fqdn>@REALM"

# Export with Kerberos security
# /etc/exports
/srv/nfs/share  192.0.2.0/24(rw,sync,sec=krb5p,no_subtree_check)

# Enable and start gssproxy (or rpc.gssd)
sudo systemctl enable --now nfs-server gssproxy

Client-Side Setup#

# Install Kerberos client
sudo apt install -y krb5-user nfs-common

# Extract the keytab for the client
kadmin -q "addprinc -randkey nfs/<client-fqdn>@REALM"
kadmin -q "ktadd -k /etc/krb5.keytab nfs/<client-fqdn>@REALM"

# Enable gssproxy or rpc.gssd
sudo systemctl enable --now gssproxy

# Mount with Kerberos
sudo mount -t nfs4 -o sec=krb5p server:/share /mnt/nfs

ID Mapping (NFSv4)#

NFSv4 maps UIDs/GIDs to user@domain strings. Configure the domain on both server and client:

# /etc/idmapd.conf
[General]
Domain = example.com
sudo systemctl restart nfs-idmapd

10. Performance Tuning#

Read/Write Buffer Sizes#

Modern kernels negotiate up to 1 MiB by default. Verify and set explicitly:

sudo mount -t nfs4 -o rsize=1048576,wsize=1048576 server:/share /mnt/nfs

NFS Server Threads#

The default number of NFS server threads (8) is often too low for production:

# /etc/nfs.conf
[nfsd]
threads=64

Or at runtime:

echo 64 | sudo tee /proc/fs/nfsd/threads

Multiple TCP Connections (nconnect)#

Available in kernel 5.3+ with NFSv4.1. Opens multiple TCP connections to parallelize I/O:

sudo mount -t nfs4 -o nconnect=8 server:/share /mnt/nfs

Jumbo Frames#

If the network supports it, enable MTU 9000 on both server and client for reduced per-packet overhead:

sudo ip link set eth0 mtu 9000

Caching with CacheFS (cachefilesd)#

Local disk caching for NFS mounts reduces network traffic for read-heavy workloads:

sudo apt install -y cachefilesd
sudo systemctl enable --now cachefilesd

# Mount with fsc option
sudo mount -t nfs4 -o fsc server:/share /mnt/nfs

Monitoring NFS Performance#

# Client-side statistics
nfsstat -c

# Server-side statistics
nfsstat -s

# Per-mount statistics
mountstats /mnt/nfs

# Real-time RPC statistics
nfsstat -l

11. Troubleshooting#

IssueCauseSolution
mount.nfs: access denied by serverClient IP not in exports, or export options mismatchVerify /etc/exports; run exportfs -v to confirm active exports; check sec= matches client
Stale file handle (ESTALE)Server-side export was re-exported, filesystem was remounted, or inode table changedUnmount and remount on the client: umount -l /mnt/nfs && mount /mnt/nfs
mount.nfs: Connection timed outFirewall blocking NFS port, server not running, or wrong server addressVerify nfs-server is running; check port 2049 (NFSv4) or rpcbind (NFSv3); test with rpcinfo -p <server>
Permission denied on filesUID/GID mismatch between server and client, or root_squash in effectAlign UIDs/GIDs, use no_root_squash if appropriate, or configure idmapd for NFSv4
Very slow read/write performanceSmall rsize/wsize, single NFS thread, or high latencyIncrease rsize/wsize to 1048576; increase server threads; use nconnect= for parallelism
mount.nfs: requested NFS version or transport protocol is not supportedNFSv3 disabled on server but client requesting v3, or vice versaSpecify version explicitly: mount -o vers=4.2; check /etc/nfs.conf on server
Lock recovery taking too long after rebootNFSv3 NLM grace period (default 90 seconds)Reduce grace period in /etc/nfs.conf: [lockd] grace-period=30; use NFSv4 which has faster lease-based recovery
rpc.gssd: ERROR: No credentials foundMissing Kerberos keytab or expired ticketVerify /etc/krb5.keytab exists with correct principal; run kinit or check gssproxy
Client hangs on hard mountServer is down and client retries indefinitelyUse soft mount for non-critical data, or hard,timeo=600 to increase timeout; fix server availability

See Also#

Sources#