How to Set Up an NFS Server on Linux: Fast Local File Sharing and Mount Tuning

Linux tutorial - IT technology blog
Linux tutorial - IT technology blog

Quick Start (5 Min)

Six months ago, our development team needed shared storage for a CI pipeline spitting out 80GB of Docker images and build artifacts daily. Instead of wrestling with Ceph or GlusterFS, I stood up a dedicated NFS node. That single server now handles roughly 65,000 file transactions an hour without a hitch. Here is how to get a clean server-client pair running in five minutes.

Step 1: Install and Configure the NFS Server

First, install the server daemon. On Ubuntu or Debian:

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

On AlmaLinux, Rocky Linux, or RHEL:

sudo dnf install -y nfs-utils
sudo systemctl enable --now nfs-server

Create your export directory. For testing, grant ownership to the unprivileged nobody user:

sudo mkdir -p /srv/nfs/shared
sudo chown -R nobody:nogroup /srv/nfs/shared
sudo chmod 755 /srv/nfs/shared

Open your exports table:

sudo nano /etc/exports

Add this export definition at the bottom, swapping in your actual client subnet (such as 192.168.1.0/24):

/srv/nfs/shared 192.168.1.0/24(rw,sync,no_subtree_check,no_root_squash)

Export the shares and refresh the running daemon:

sudo exportfs -rav
sudo systemctl restart nfs-kernel-server

Step 2: Connect from the Client Node

Install the client utilities on the machine that needs the storage:

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

# RHEL / AlmaLinux
sudo dnf install -y nfs-utils

Query the server (assumed here at 192.168.1.50) to verify the export is visible:

showmount -e 192.168.1.50

Mount the export to a local directory:

sudo mkdir -p /mnt/shared
sudo mount 192.168.1.50:/srv/nfs/shared /mnt/shared

Confirm the connection by running df -h /mnt/shared. You should see the remote volume mounted with its total capacity.

Architecture and Export Directives

Modern Linux distros default to NFSv4 (specifically v4.1 or v4.2). Unlike NFSv3, which depended on rpcbind and sprayed traffic across arbitrary high ports, NFSv4 talks exclusively over TCP port 2049. That simplifies firewall rules immediately. It also introduces stateful sessions, pseudo-filesystems, and optional Kerberos authentication.

Inside the /etc/exports Directives

The flags in /etc/exports control where your system lands on the safety-versus-speed spectrum:

  • rw vs ro: Allows read-write or forces read-only access.
  • sync: Acknowledges write requests only after data hits physical disk. It prevents data loss during sudden power outages. Always keep this on production storage.
  • async: Acknowledges writes as soon as data reaches the server’s RAM buffer. Throughput jumps dramatically, but a power cut will corrupt unwritten files.
  • no_subtree_check: Skips parent directory checks when accessing subfolders. Leaving this enabled causes timeouts during heavy file renames. Always disable it.
  • root_squash: Maps client UID 0 (root) to the nobody account. This keeps a compromised client root user from wiping your server’s root filesystem. Use no_root_squash only when clients run trusted backup jobs requiring full root access.
  • all_squash with anonuid / anongid: Collapses every client connection into one specified user ID. This pattern is ideal for shared application uploads.

Tuning Client Mount Options for Throughput

Default mounts work fine for light text editing. But run sequential backups over a 10GbE interface, and defaults will cap your speeds well below wire rate. Adjusting transfer buffers and caching flags fixes that.

High-Performance Mount Command

Here is the mount command we run across our production nodes:

sudo mount -t nfs -o proto=tcp,nfsvers=4.2,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2,noatime 192.168.1.50:/srv/nfs/shared /mnt/shared

Why These Flags Matter

  • rsize=1048576,wsize=1048576: Increases chunk sizes to 1MB. Older defaults often negotiate 32KB or 64KB buffers. In our lab testing, bumping 64KB buffers to 1MB jumped large-file transfer rates on a 1GbE connection from 48 MB/s straight to the line limit of 112 MB/s.
  • noatime: Stops the server from recording an access timestamp every time a process reads a file. In build pipelines reading thousands of source files repeatedly, this cuts disk metadata IOPS by roughly 30%.
  • hard vs soft: Never use soft on data volumes. If the network drops, a soft mount throws I/O errors straight to the application, which corrupts SQLite databases and binary files. A hard mount pauses process I/O until connectivity returns, safeguarding your data.
  • timeo=600 and retrans=2: Sets a 60-second recovery window (specified in tenths of a second) before resending unacknowledged packets. It gives busy NVMe and SATA arrays enough breathing room during flush spikes.

Persistent Mounts via /etc/fstab

Append the mount to /etc/fstab so it reconnects after system restarts. The _netdev parameter is essential: it instructs systemd to delay the mount until network interfaces are fully up.

192.168.1.50:/srv/nfs/shared  /mnt/shared  nfs  nfsvers=4.2,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2,noatime,_netdev  0  0

Test your fstab syntax immediately without rebooting:

sudo umount /mnt/shared
sudo mount -a
findmnt /mnt/shared

Field Notes and Troubleshooting

1. Lock Down the Firewall

Standard NFS traffic is unencrypted. Restrict access strictly to private IP ranges. If you use UFW on Ubuntu:

sudo ufw allow from 192.168.1.0/24 to any port 2049 proto tcp
sudo ufw status numbered

For firewalld on RHEL or AlmaLinux:

sudo firewall-cmd --permanent --zone=internal --add-service=nfs
sudo firewall-cmd --permanent --zone=internal --add-source=192.168.1.0/24
sudo firewall-cmd --reload

2. Aligning UIDs and GIDs

Mismatched user IDs cause 90% of all NFS permission headaches. NFS passes raw numeric IDs across the wire, not usernames. If user runner has UID 1002 on your client, the server enforces permissions for whichever account owns UID 1002 locally. You have two ways to resolve this:

  • Create matching UIDs and GIDs across both systems.
  • Force all traffic into a shared user in /etc/exports with all_squash,anonuid=1001,anongid=1001.

3. Clearing Stale File Handles

If an exported disk is rebooted or an export path changes, client commands stall with Stale file handle. Standard unmounts hang indefinitely. Break the lock with a lazy unmount:

sudo umount -l /mnt/shared

Once detached, remount with sudo mount -a.

4. Tracking Bottlenecks with nfsstat

Find out whether latency comes from network retransmissions or slow server disks using nfsstat:

# Client statistics
nfsstat -c

# Server statistics
nfsstat -s

Check the retrans metric under client RPC. If that number climbs during active transfers, your switch or NIC is dropping frames.

Share: