Say you have a few machines that all need to read and write the same set of files: a couple of web servers sharing one document root, a small Kubernetes or compute cluster that needs shared storage, or just a home lab where you want one box to hold all your media and configs. Copying files around by hand gets old fast. NFS (Network File System) solves this by letting one machine share a directory over the network so other machines can mount it as if it were a local folder.
The machine that shares the directory is the NFS server, and any machine that mounts it is an NFS client. This tutorial covers the server side: installing the service, exporting a directory safely, verifying it works, and fixing it when it doesn’t. The client side (mounting the share) is covered in a separate tutorial, linked at the end.
This guide is aimed at developers, sysadmins, and home-lab tinkerers running at least one Ubuntu machine that needs to hand out shared storage. You only need basic command-line skills to follow along, and everything here works on any modern Ubuntu LTS release.
Prerequisite
- Ubuntu (any modern LTS release)
Sudo Privileges
Before we start, let’s switch to root so we don’t hit permission issues during configuration.
sudo su
Install Dependencies
First, install the NFS server package.
apt update && apt install nfs-kernel-server
Create a Directory to Share
This is the directory that will be shared with the NFS clients. In Linux this is called the export directory. You can put it anywhere; for example:
mkdir -p /nfs/share
Now we need to decide who owns the files inside it. When a client writes a file, the NFS server has to map that write to some local user. By default the server squashes the client’s root user down to a low-privilege anonymous account so a remote root can’t act as root on your server’s files. On Ubuntu that anonymous account is nobody:nogroup. So we set the directory ownership to match:
chown -R nobody:nogroup /nfs/share
You will see a lot of older tutorials tell you to also run chmod -R 777 /nfs/share. That makes the directory world-writable, which is the quickest way to get a share working for a throwaway test, but it’s bad practice for anything you actually care about, because anyone who can reach the share can read, change, or delete every file in it. Use it only for a quick test:
chmod -R 777 /nfs/share # test boxes only, not production
For a real setup, leave the permissions sane (for example chmod -R 755 /nfs/share, or 775 if a specific group needs to write) and control access through the export options below instead. That’s the correct place to decide who gets to write.
Configure NFS Server
The NFS server needs to know which directories it’s allowed to share and who’s allowed to connect. That’s configured in the /etc/exports file.
nano /etc/exports
Add a line in this format:
/nfs/share <client ip/subnet>(rw,sync,no_subtree_check)
Replace <client ip/subnet> with the IP address or subnet you expect clients to connect from. For example, to allow any host in the 192.168.5.0/24 subnet:
/nfs/share 192.168.5.0/24(rw,sync,no_subtree_check)
You can also use a wildcard (*) to accept connections from anywhere, but don’t do this on an untrusted network, since it lets any machine that can reach the server mount the share:
/nfs/share *(rw,sync,no_subtree_check)
What those options mean
It’s worth knowing what you’re turning on rather than copying the line blindly. Per the exports(5) man page:
- rw — allow both read and write requests. The default is read-only, so you need this if clients should be able to write.
- sync — reply to a write request only after the data has actually been committed to disk. This is the default, and it’s safer than
asyncbecause it protects against data loss if the server crashes. - no_subtree_check — disable subtree checking. This is the default on modern systems; it’s slightly less strict but more reliable, especially for directories where files get renamed often.
Locking down write access
The export options above control who can connect, but the squash options control what permissions their writes get. The relevant ones, again from exports(5):
- root_squash — map requests from the client’s root (uid/gid 0) to the anonymous account. This is the default, and you should keep it. It stops a remote root from owning files as root on your server.
- no_root_squash — turn root squashing off. Avoid this unless you have a very specific reason (it’s mainly meant for diskless clients).
- all_squash — map every client user, not just root, to the anonymous account. This is handy for a shared directory where you don’t care about individual user identity and just want everything owned by
nobody:nogroup. - anonuid / anongid — set exactly which uid/gid the squashed users map to. By default this is
65534(thenobody/nogroupaccount on Ubuntu). Use these if you want squashed writes to be owned by a specific local user instead.
So a more locked-down export that forces every client write to land as the anonymous user looks like this:
/nfs/share 192.168.5.0/24(rw,sync,no_subtree_check,all_squash,anonuid=65534,anongid=65534)
After editing the file, apply the exports:
exportfs -a
And restart the NFS server so everything is in a clean state:
systemctl restart nfs-kernel-server
Share Multiple Directories and Clients
The /etc/exports file isn’t limited to one line. You can export several directories, and you can give more than one client access to the same directory by listing each client (with its own options) separated by spaces. For example:
/nfs/share 192.168.5.0/24(rw,sync,no_subtree_check)
/nfs/readonly 192.168.5.10(ro,sync,no_subtree_check) 192.168.5.11(ro,sync,no_subtree_check)
/srv/media *.lab.local(rw,sync,no_subtree_check)
Reading these top to bottom: the first shares a directory read-write to a whole subnet, the second shares a directory read-only (ro) to two specific hosts, and the third uses a hostname wildcard to share to every machine in the lab.local domain.
There’s one easy-to-miss trap here, and the exports(5) man page calls it out specifically: do not put a space between a client and its opening parenthesis. 192.168.5.10(rw) grants read-write to that host, but 192.168.5.10 (rw) is read as two separate things — that host with default (read-only) options, and rw access for everyone else. A single stray space can quietly open your share to the world, so double-check the spacing after editing.
Whenever you change /etc/exports, re-read it so the changes take effect:
exportfs -ra
Verify the Export
Before touching a client, confirm the server is actually sharing what you think it is. List the active exports with their options:
exportfs -v
You should see your directory and the client/subnet you configured, for example:
/nfs/share 192.168.5.0/24(sync,wdelay,hide,no_subtree_check,sec=sys,rw,secure,root_squash,...)
If your directory shows up here with the right client and rw, the export is live.
Also make sure the service itself is running:
systemctl status nfs-kernel-server
Look for active (exited) or active (running) in the output. If it’s failed or inactive, the share won’t work no matter what’s in /etc/exports — see the troubleshooting section below.
Keep the Server Running After a Reboot
Your exports live in /etc/exports, which the NFS server reads automatically every time it starts, so the shares themselves survive a reboot without any extra work. The one thing worth confirming is that the service is set to start at boot in the first place:
systemctl is-enabled nfs-kernel-server
Installing the package usually enables it for you, so this should print enabled. If it prints disabled instead, turn it on so your shares come back after the next reboot:
systemctl enable nfs-kernel-server
Troubleshooting
Most server-side NFS problems fall into one of these three buckets.
A firewall is blocking NFS
If the export looks correct on the server but clients time out or can’t connect, a firewall is the usual culprit. Modern NFSv4 (the default on current Ubuntu) communicates over TCP port 2049, so that port needs to be open between the server and its clients. If you’re running ufw, allow it for your client subnet:
ufw allow from 192.168.5.0/24 to any port 2049 proto tcp
Note that if you (or a client) are forced to use the older NFSv3, it also relies on rpcbind on port 111 plus a few helper services on dynamic ports, which makes it much more firewall-unfriendly — another reason to stick with NFSv4 where you can.
The service won’t start
If systemctl status nfs-kernel-server shows failed, the most common cause is that you have no valid exports yet — the service has nothing to do and exits. Check your /etc/exports has at least one valid line, then look at the full error with:
journalctl -u nfs-kernel-server
This shows the actual reason the unit failed to start instead of guessing.
Exports not taking effect after editing the file
If you edited /etc/exports but clients still can’t see the change (or still see the old options), the export table is stale — editing the file alone doesn’t reload it. Re-read the file and resync the export table:
exportfs -ra
Then confirm with exportfs -v that the new options are showing. If exportfs prints a warning about your line, it usually points right at a typo in the path or the options, so read it carefully.
Best Practices
A few habits keep an NFS server safe and predictable once it’s in use:
- Export the narrowest scope you can. Share the specific directory a client needs, not a parent that contains more than it should, and use
ro(read-only) for any client that only needs to read. The fewer machines that can write, the fewer ways things go wrong. - Avoid
*on anything but a fully trusted private network. A wildcard lets any host that can reach the server mount the share. Prefer a specific subnet or hostname. - Keep
root_squashon. It’s the default for a reason — it stops a remote root from acting as root on your files. Only reach forno_root_squashif you genuinely need it. - Remember that standard NFS has no real user authentication. With the default
sec=syssecurity, the server trusts the client’s IP address and whatever user ID the client claims to be. That makes your export list and your network boundary the actual security layer, so keep NFS traffic on a private network or VPN and never expose it directly to the internet.
Conclusion
You now have a working NFS server: you installed nfs-kernel-server, created and correctly owned an export directory, wrote a sensible /etc/exports line while understanding what each option does, verified the share from the server side, and learned how to diagnose the most common server-side failures. Just as importantly, you know why chmod 777 and an open * export are shortcuts to avoid, and what to do instead.
From here, the natural next step is to actually use the share by mounting it from another machine, which I cover in a separate tutorial: How to Mount Directory to NFS Server.