37.2 Setting Up an NFS Server: /etc/exports and exportfs
Alright, let’s get our hands dirty. Setting up an NFS server isn’t rocket science, but it’s one of those tasks where the devil is absolutely in the details. Get one tiny syntax error in the config file and you’ll be staring at Permission denied errors until you question your life choices. I’ve been there. We’re going to avoid that.
The heart and soul of your NFS server is the /etc/exports file. This is where you declare which directories on your server you want to share (to “export”) and exactly which clients are allowed to mount them, and with what permissions. The syntax is deceptively simple, which is why it bites so many people.
The Anatomy of an /etc/exports Line
A typical line looks like this:
/directory/to/share client1(option1,option2) client2(option3)
Let’s break down a real, useful example. Say you want to share /srv/data to a client with IP 192.168.1.100 and give it read-write access.
/srv/data 192.168.1.100(rw,sync,no_subtree_check)
See those options? This is where most people mess up. Let’s autopsy them:
rw: Read-write. The obvious one. The alternative isro(read-only), which is safer if you can use it.sync: This is crucial. It tells NFS to only reply to a client’s request after the changes have been committed to stable storage (your disk). It’s safer. The opposite isasync, which is faster but dangerous—if your server crashes, you can lose data. The NFS gods deprecatedasyncfor a reason. Just usesync.no_subtree_check: This disables a validation that causes more problems than it solves, especially if a file is renamed while a client is using it. It’s a relic. Always use it. Your life will be simpler.
Here’s a more complex example for a whole network segment, with some safety options for a public-facing share:
/public /24(ro,sync,no_subtree_check,all_squash,anonuid=1000,anongid=1000)
all_squash: This maps all user IDs from connecting clients, including root, to a specific anonymous user on the server. This is a fantastic security measure for public or untrusted networks. It prevents clients from pretending to be each other or root.anonuid/anongid: This pairs withall_squashoranonuidto define which user and group the client’s users get squashed to. Here, we’re mapping everyone to the user and group with ID 1000 (usually the first created user on the server). This is how you get consistent file permissions.
Actually Making It Work: exportfs
So you’ve edited /etc/exports. Pat yourself on the back, but you’re not done. The NFS server doesn’t automatically re-read this file. This is where exportfs comes in—the tool that manages the kernel’s actual table of exported file systems.
After editing /etc/exports, you must run:
sudo exportfs -rva
The flags are -r for “re-export everything,” -v for “verbose” (so it tells you what it’s doing), and -a for “all.” This command is your best friend. It will pick up your changes and make them live without needing a full service restart (which would kick everyone off).
To see what you’ve actually exported right now, which is often different than what’s just sitting in your config file, use:
sudo exportfs -v
The Gotchas That Will Haunt You
Permissions and Ownership: This is the big one. NFS trusts the client to tell it who is making a request (user and group IDs). If your UID on the client is 1000 but that UID belongs to
janeon the client andjohnon the server… well, the server will believe you’rejohn. Your best bet is to standardize UIDs/GIDs across all systems or use a central directory like LDAP. Theall_squashoption we discussed is a great band-aid for this.Root Squashing: By default, the
root_squashoption is enabled. This means the client’s root user (UID 0) gets mapped to the server’s “nobody” user. This is a good security practice! It prevents a client root from having root access to your server’s filesystem. If you really need it, you can override it withno_root_squash, but please, for the love of all that is holy, only use this on a trusted network and only for specific clients. It’s a massive security hole.Firewalls: NFS is a complicated beast that uses multiple daemons and random ports. Your firewall will block it. The modern way to handle this is to use
rpcbindand the NFS service file withfirewalld:sudo firewall-cmd --permanent --add-service=nfs sudo firewall-cmd --permanent --add-service=mountd sudo firewall-cmd --permanent --add-service=rpc-bind sudo firewall-cmd --reloadIf you’re on an older distro using iptables, you have my condolences. You’ll need to lock down ports 2049 (NFS) and 111 (rpcbind), and dynamically open the ports used by
mountdandrpc.statd. It’s a mess. Usefirewalld.
There you have it. Edit /etc/exports with care, use exportfs -rva religiously, and always, always think about user IDs and security. Now go share something.