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.

Table of Contents#

  1. Overview
  2. NFSv3 vs NFSv4 Comparison
  3. Server Installation
  4. Server Configuration
  5. Export Parameters
  6. Firewall Configuration
  7. Client Configuration
  8. Client Mount Options
  9. Kerberos Authentication
  10. Performance Tuning
  11. Troubleshooting
  12. See Also
  13. Sources

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#