sysadmin · July 3, 2026 · 15 min read

NFS, end to end.
export, firewall, SELinux, and a persistent mount.

nfsadmin needs a shared workspace reachable from any node. Stand up an NFS server, export it to the internal subnet, open the firewall, mount it as a client, and persist it with the flag people forget.

// what we’re getting into
  1. The ticket
  2. Concept review. The pieces and the exports syntax.
  3. Build the share directory
  4. Export it and start the server
  5. Firewall and SELinux
  6. Mount it as a client
  7. Persist the mount with _netdev
  8. Exam questions
  9. Final checklist: confirm everything works

The nfsusers team wants one workspace they can reach from any box instead of copying files back and forth. So server1 becomes an NFS server: a /srv/nfsshare directory owned by the nfsusers group, exported read-write to the internal subnet 192.168.50.0/24, mounted from a client to prove it works, and set to come back after a reboot. SELinux stays enforcing the whole way through.

NFS is four parts that all have to agree at once, and if any one of them disagrees you get a mount that hangs or a permission you cannot explain: the export line, the nfs-server service, the firewall, and SELinux. The fifth trap is the fstab entry, which needs _netdev, or the box tries to mount a network share before the network exists and the boot stalls.

The ticket

bash
id nfsadmin
getent group nfsusers
sudo dnf install -y nfs-utils

Concept review. The pieces and the exports syntax.

bash
man exports
man exportfs

The nfs-server service serves the shares. /etc/exports defines what is shared and to whom. exportfs refreshes exports without a restart. firewalld’s nfs service opens the ports. And showmount -e against a host lists its shares from the client side.

/etc/exports syntax
/srv/nfsshare 192.168.50.0/24(rw,sync,root_squash)

# rw / ro        read-write or read-only
# sync           writes commit before returning, safer
# root_squash    client root maps to nobody, the safe default
# no_root_squash rare, lets client root stay root
warning: There is no space between the subnet and the opening parenthesis. 192.168.50.0/24(rw) means read-write for that subnet. 192.168.50.0/24 (rw), with a space, means read-only for that subnet and read-write for everyone else. The space changes the meaning entirely.

Build the share directory

Owned by the group, with SGID so new files inherit that group, exactly like the shared-folder ticket earlier in the series.

bash
sudo mkdir -p /srv/nfsshare
sudo chown root:nfsusers /srv/nfsshare
sudo chmod 2775 /srv/nfsshare
echo "shared data" | sudo tee /srv/nfsshare/welcome.txt

Export it and start the server

bash
sudo cp /etc/exports /etc/exports.bak 2>/dev/null || sudo touch /etc/exports
echo '/srv/nfsshare 192.168.50.0/24(rw,sync,root_squash)' | sudo tee -a /etc/exports

sudo exportfs -arv        # -a all, -r re-export, -v verbose
sudo systemctl enable --now nfs-server
systemctl is-active nfs-server

Firewall and SELinux

NFS needs three firewalld services open. SELinux usually just works, but the boolean below lets NFS read and write any path if you need it.

bash
sudo firewall-cmd --permanent --add-service=nfs
sudo firewall-cmd --permanent --add-service=rpc-bind
sudo firewall-cmd --permanent --add-service=mountd
sudo firewall-cmd --reload

sudo setsebool -P nfs_export_all_rw on

Mount it as a client

Discover the share, then mount it. On the exam you treat localhost as the client.

bash
sudo showmount -e localhost
# Export list for localhost:
# /srv/nfsshare 192.168.50.0/24

sudo mkdir -p /mnt/nfs_test
sudo mount -t nfs localhost:/srv/nfsshare /mnt/nfs_test
cat /mnt/nfs_test/welcome.txt    # shared data
mount | grep nfs_test
# localhost:/srv/nfsshare on /mnt/nfs_test type nfs4 (rw,relatime,...)

Persist the mount with _netdev

The fstab entry needs _netdev, which tells systemd this is a network mount and to wait for the network before trying. Without it, boot can hang or the mount fails because the network is not ready.

bash
sudo cp /etc/fstab /etc/fstab.bak.$(date +%Y%m%d)
echo 'localhost:/srv/nfsshare /mnt/nfs_test nfs defaults,_netdev 0 0' | sudo tee -a /etc/fstab

sudo umount /mnt/nfs_test
sudo mount -a
mount | grep nfs_test

Exam questions

Write the command first.

Q1. Display all currently exported NFS shares.

Q2. Export /share to 192.168.1.0/24 read-write, and apply it without restarting NFS.

Q3. Persistently mount nfs.server.com:/data at /data so it survives reboots and waits for the network.

Answers.

bash
# A1
sudo exportfs -v          # server side
showmount -e localhost    # client side
# A2
echo '/share 192.168.1.0/24(rw,sync)' | sudo tee -a /etc/exports
sudo exportfs -arv
# A3
sudo mkdir -p /data
echo 'nfs.server.com:/data /data nfs defaults,_netdev 0 0' | sudo tee -a /etc/fstab
sudo mount -a

Final checklist: confirm everything works

If every check passes, the ticket is done.

bash
# 1. share exists with SGID and the export line present
ls -ld /srv/nfsshare; sudo exportfs -v

# 2. nfs-server running, firewall services open
systemctl is-active nfs-server; sudo firewall-cmd --list-services

# 3. client mount works and survives mount -a with _netdev
mount | grep nfs_test

# 4. all three exam commands written from scratch
# 5. tracker entry checked off

Reply to nfsadmin: NFS is up. /srv/nfsshare exported read-write to 192.168.50.0/24, service enabled, firewall open, SELinux boolean set. Client mount at /mnt/nfs_test persisted via fstab with _netdev.

next post
autofs: mounting NFS on demand with indirect and direct maps