NFS HOWTO: an NFSv4 file server and clients on Linux today
NFS lets Linux and Unix machines mount a directory from a server as if it were a local disk. Use NFS version 4 (4.2 on current systems): it needs only TCP port 2049, works through firewalls, and can use Kerberos for real security. This page sets up a server, mounts it on clients, and covers the mistakes that cause most NFS trouble.
The classic Linux NFS-HOWTO (2002, NFSv2/v3 era) is kept by the Linux Documentation Project at tldp.org. The steps below are for current releases as of November 2026.
What changed since NFSv3
- One port. An NFSv4 client talks to port 2049 directly, without first asking
rpcbind(the old portmapper). NFSv3 also needsrpcbind(port 111),mountdand the lock and status daemons on changing ports. - Version negotiation. With no
vers=option, the Linux client tries 4.2 first and steps down until the server agrees. - Kernel server. The NFS server runs in the kernel (
nfsd); the user-space parts come from thenfs-utilsproject. - Security flavors. The default,
sec=sys, trusts the user and group numbers the client sends.krb5,krb5iandkrb5padd Kerberos authentication, integrity checks and encryption.
Set up the server
# Debian / Ubuntu
sudo apt install nfs-kernel-server
# Fedora / RHEL / Alma / Rocky
sudo dnf install nfs-utils
Create the directory to share. Here members of a projects group may write, and new files inherit the group because of the setgid bit (the leading 2):
sudo groupadd -g 2000 projects
sudo mkdir -p /srv/nfs/projects
sudo chgrp projects /srv/nfs/projects
sudo chmod 2770 /srv/nfs/projects
Add one line per share to /etc/exports. Each line is a path, then one or more clients, each with options in brackets and no space before the bracket:
/srv/nfs/projects 192.168.1.0/24(rw,sync,no_subtree_check)
/srv/nfs/iso 192.168.1.0/24(ro) backup.example.lan(rw,no_root_squash)
Load the exports and start the server (the same unit name, nfs-server, works on both families):
sudo exportfs -ra
sudo exportfs -v
sudo systemctl enable --now nfs-server
exportfs -ra re-reads /etc/exports and any *.exports files in /etc/exports.d/; run it after every change. exportfs -v lists what is actually exported, with all defaults filled in.
| Option | Meaning | Default |
|---|---|---|
rw / ro | Read-write or read-only | ro |
sync / async | sync commits writes to disk before replying; async is faster but can lose data if the server crashes | sync |
root_squash | Requests from root (UID 0) on the client act as an anonymous user | On; no_root_squash turns it off |
all_squash | Map every user to the anonymous user (anonuid/anongid) | Off |
no_subtree_check | Skip the subtree check, which causes problems when files are renamed | Already the default; writing it silences a warning |
sec= | Allowed security flavors, for example sec=krb5p | sys |
Watch the space. /srv/nfs/iso 192.168.1.0/24(ro) gives that network read-only access. /srv/nfs/iso 192.168.1.0/24 (ro), with a space, gives that network the defaults and gives every host in the world read-only access.
Make it NFSv4-only
If no clients need NFSv3, switch it off so that only port 2049 stays open. Red Hat documents this procedure for RHEL 10; Fedora and Debian 13 use the same unit names and the same /usr/sbin/rpc.mountd path. Edit /etc/nfs.conf (or add a file in /etc/nfs.conf.d/):
[nfsd]
vers3=n
Then stop the NFSv3 helpers and tell rpc.mountd not to listen on the network. Create /etc/systemd/system/nfs-mountd.service.d/v4only.conf:
[Service]
ExecStart=
ExecStart=/usr/sbin/rpc.mountd --no-tcp --no-udp
sudo systemctl mask --now rpc-statd.service rpcbind.service rpcbind.socket
sudo systemctl daemon-reload
sudo systemctl restart nfs-mountd nfs-server
cat /proc/fs/nfsd/versions
The last command shows which versions are on, for example -3 +4 +4.1 +4.2.
Firewall
# firewalld, NFSv4 only
sudo firewall-cmd --permanent --add-service=nfs
# firewalld, if you still serve NFSv3
sudo firewall-cmd --permanent --add-service={nfs,rpc-bind,mountd}
sudo firewall-cmd --reload
# ufw (Ubuntu), NFSv4 only
sudo ufw allow from 192.168.1.0/24 to any port 2049 proto tcp
The firewalld commands change the default zone; if that zone faces the Internet, add --zone= with the zone that holds your LAN. NFSv3 also needs the lock and status daemons on fixed ports: set port= under [lockd] and [statd] in /etc/nfs.conf and open those ports as well, as Red Hat's NFSv3 procedure shows.
Mount on a client
# Debian / Ubuntu
sudo apt install nfs-common
# Fedora / RHEL
sudo dnf install nfs-utils
sudo mkdir -p /mnt/projects
sudo mount -t nfs nfs1.example.lan:/srv/nfs/projects /mnt/projects
findmnt -t nfs4
nfsstat -m
findmnt and nfsstat -m show the version and options the client actually negotiated, such as vers=4.2,proto=tcp,hard,sec=sys. Mount by the full path the server exports, as Red Hat’s procedure does.
Mount at boot with fstab and systemd
For a share that should appear when first used, rather than holding up boot, add this line to /etc/fstab:
nfs1.example.lan:/srv/nfs/projects /mnt/projects nfs rw,_netdev,nofail,x-systemd.automount,x-systemd.idle-timeout=10min 0 0
sudo systemctl daemon-reload
sudo systemctl restart remote-fs.target
ls /mnt/projects # triggers the mount
systemd turns each fstab line into units. x-systemd.automount creates an automount point that mounts on first access; x-systemd.idle-timeout=10min unmounts it again after ten idle minutes; _netdev marks it as needing the network; nofail lets boot continue if the server is down. The autofs package is still available for large map-driven setups, but systemd automount covers most needs.
Keep the default hard mount. Without options, NFS retries forever (hard), so programs wait while a server reboots and then carry on. The nfs(5) manual warns that soft can silently corrupt data; use it only where a quick error matters more than your data.
Users, IDs and permissions
With sec=sys, the server checks permissions using the numeric UID and GID the client sends. User alice must have the same UID on every machine, or she will see other people’s files. Keep accounts in one place (LDAP, FreeIPA or Samba AD, see the LDAP HOWTO) rather than in each /etc/passwd. If files show up owned by nobody, check that the NFSv4 ID-mapping domain (Domain= in /etc/idmapd.conf) matches on server and client.
Because sec=sys trusts the client, anyone with root on an allowed client can act as any user except root. Limit exports to the hosts or subnets that need them, and never export to * with rw.
Kerberos and TLS
For untrusted networks, use Kerberos: export with sec=krb5p and mount with -o sec=krb5p. Each machine needs a host key in /etc/krb5.keytab, the server needs an nfs/ service principal, and the client runs rpc-gssd. A FreeIPA domain sets most of this up for you; RHEL’s file-services guide has a full procedure. In return, the server checks a real Kerberos ticket instead of trusting UIDs, and krb5p encrypts all traffic. Recent kernels and nfs-utils also support NFS over TLS (the tlshd service and the xprtsec=tls mount option), documented for RHEL 10.
Troubleshooting
- “access denied by server while mounting”: the client address does not match the export line, or you forgot
sudo exportfs -ra. Checksudo exportfs -v. - Mount hangs: port 2049 is blocked or the server is down. Test with
nc -zv nfs1.example.lan 2049. showmount -efails: normal on an NFSv4-only server;showmountasksmountd, which no longer listens.- Root cannot write: that is
root_squashworking. Use a normal user, orno_root_squashfor a trusted backup host only. - “Stale file handle”: the exported directory was replaced or moved on the server. Unmount and mount again.
- Logs:
journalctl -u nfs-server -u nfs-mountdon the server andjournalctl -b | grep -i nfson the client.
Sources
- exports(5), man7.org
- nfs(5), man7.org
- nfs.conf(5), man7.org
- systemd.mount(5), man7.org
- NFS documentation index, kernel.org
- Configuring and using network file services (NFSv4-only server, Kerberos, TLS), RHEL 10 documentation
- nfs-kernel-server package file list, Debian 13
- firewalld service definitions (nfs, rpc-bind, mountd), firewalld source