/etc/exports syntax¶
/etc/exports is the primary configuration file for the Linux NFS server (knfsd). Each line defines one export: a local directory path, one or more client specifications, and the options that control access. The file is parsed by exportfs(8), which loads the entries into the kernel's export table.
This page covers the file format, client specification types, option syntax rules, and the management commands. For the meaning of individual options (root_squash, no_subtree_check, sec=, etc.), see Export options.
General format¶
Each export entry follows this pattern:
- Path: an existing local directory (or file, though directory exports are the norm)
- Client: who is allowed to mount this export (hostname, IP, network, wildcard, or netgroup)
- Options: comma-separated list inside parentheses, controlling security and behavior
Multiple clients can share one path on the same line, each with their own option set. Blank lines and lines starting with # are ignored. Long lines can be continued with a trailing backslash (\).
# Simple export: one directory, one client, read-write
/srv/nfs/data 192.168.1.10(rw,sync,no_subtree_check)
# Same directory exported to two clients with different options
/srv/nfs/shared 10.0.0.0/24(rw,sync) 10.0.1.0/24(ro,sync)
Path rules¶
The exported path must be an existing directory on the server. It does not need to be a mount point -- you can export any subdirectory. However, exporting a subdirectory of a filesystem (rather than the filesystem root) has security implications:
- With
no_subtree_check(the default since Linux 2.6.25), the kernel does not verify that file handles fall within the exported subtree. A handle pointing to any inode on the same filesystem is accepted, even if it is outside the exported directory. This is the foundation of export escape attacks (F-2.1). - With
subtree_check, the kernel verifies that each file handle resolves to an inode within the exported subtree, but this has performance costs and causesESTALEerrors when files are renamed.
Exporting a subdirectory is not a security boundary
Exporting /srv/nfs/share on an ext4 partition does not prevent clients from accessing the rest of the partition. With no_subtree_check (the default), any valid file handle on that filesystem is accepted by the kernel. An attacker who constructs a handle pointing to inode 2 (the ext4 root) gains access to the entire filesystem. See F-2.1.
Client specifications¶
The client field determines which hosts are allowed to mount the export. There are five formats:
Single host¶
A hostname or IP address. Only that one machine can mount the export.
Hostname resolution
When you use a hostname, exportfs resolves it to IP addresses at load time. If DNS changes after loading, the old IP remains in the export table until you run exportfs -ra. For security-sensitive exports, prefer IP addresses to avoid DNS-based attacks.
Network (CIDR or netmask)¶
A network range in CIDR notation or with an explicit subnet mask. All hosts in the range can mount.
Both forms are equivalent. CIDR notation is more common and less error-prone.
Wildcard¶
A pattern with * and ? characters, matched against the client's resolved hostname. Only works with reverse DNS -- if the client's IP does not have a PTR record, the match fails.
Wildcards depend on reverse DNS
Wildcard matching requires the server to reverse-resolve the client's IP address to a hostname. If reverse DNS is misconfigured or unavailable, clients that should match will be denied. If an attacker controls reverse DNS for their IP, they can forge a matching hostname.
Netgroup¶
An NIS netgroup, prefixed with @. All hosts listed in the netgroup can mount.
Netgroups require a functioning NIS/NIS+ or LDAP-backed nsswitch.conf configuration. If the netgroup source is unreachable, exportfs may fail silently or deny all access.
Anonymous (everyone)¶
A bare * matches every client. This is almost never what you want in production.
Anonymous exports are visible to every host on the network
An export with * as the client is accessible from any IP address that can reach the NFS server. Combined with rw or no_root_squash, this is a full compromise. Even with ro,all_squash, it exposes directory listings and file contents to anyone. See F-7.1.
GSS security prefix¶
The gss/krb5p prefix restricts access to clients using Kerberos privacy-level authentication. The general form is gss/<mechanism>, where <mechanism> is one of krb5, krb5i, or krb5p. Only clients that authenticate with the specified (or stronger) GSS mechanism can mount the export.
This is distinct from the sec= option: gss/krb5p as a client specifier controls who can mount, while sec=krb5p controls which authentication flavors are accepted for file operations. In practice, both are used together for consistent enforcement.
Options syntax¶
Options are comma-separated inside parentheses, attached directly to the client specification with no space between the client and the opening parenthesis.
There are no spaces around commas, no spaces inside the parentheses, and, critically, no space before the opening parenthesis.
The space-before-paren trap¶
The most common NFS misconfiguration
A space between the client and the opening parenthesis changes the meaning of the line entirely:
# INTENDED: export /data to 192.168.1.0/24 with rw
/data 192.168.1.0/24(rw,sync)
# ACTUAL EFFECT of adding a space before the paren:
/data 192.168.1.0/24 (rw,sync)
The second line does not export /data to 192.168.1.0/24 with rw,sync. It parses as two separate entries:
/dataexported to192.168.1.0/24with default options (which includero)/dataexported to everyone (*implied) withrw,sync
The result: the intended network gets read-only access, and every other host on the network gets read-write access. This single space character has caused production data breaches.
You can verify this by checking the effective export table:
# After: /data 192.168.1.0/24 (rw,sync)
$ exportfs -v
/data 192.168.1.0/24(ro,wdelay,root_squash,no_subtree_check,...)
/data <world>(rw,wdelay,root_squash,no_subtree_check,...)
The <world> entry is the giveaway -- it means the orphaned (rw,sync) was applied to an anonymous client specification.
Always verify with exportfs -v
After editing /etc/exports, always run exportfs -v and look for <world> entries. If any appear and you did not intend them, you have the space-before-paren bug.
Example exports file¶
# Read-only public data -- squash all users to anonymous
/srv/nfs/pub *(ro,sync,all_squash,anonuid=65534,anongid=65534)
# Home directories -- specific subnet, root squashed
/home 192.168.1.0/24(rw,sync,root_squash,no_subtree_check)
# Build artifacts -- two dev teams, different options
/srv/builds 10.10.1.0/24(rw,sync,no_root_squash) 10.10.2.0/24(ro,sync)
# Kerberos-secured export -- require krb5p (privacy)
/srv/secure *.corp.example.com(rw,sync,sec=krb5p,no_subtree_check)
# Database server -- single host, no root squash for backup agent
/var/backups db01.internal(rw,sync,no_root_squash,no_subtree_check)
# ISO images -- read-only, all users squashed, world-accessible
/srv/iso *(ro,sync,all_squash)
# Development -- wide open for lab use (NOT for production)
/srv/dev 10.0.0.0/8(rw,sync,no_root_squash,no_subtree_check,insecure)
# NFSv4-only export with fsid for pseudo-FS root
/export 192.168.0.0/16(rw,sync,fsid=0,no_subtree_check)
# Nested export under the pseudo-FS root
/export/data 192.168.0.0/16(rw,sync,no_subtree_check)
# Restricted: only Kerberos integrity or privacy, no AUTH_SYS
/srv/restricted 10.20.0.0/16(rw,sync,sec=krb5i:krb5p,no_subtree_check)
# Cross-mount visibility: crossmnt exposes nested filesystems
/srv/shared 10.0.0.0/24(rw,sync,crossmnt,no_subtree_check)
Security review of the example
Several entries above are intentionally insecure for illustration:
/srv/devwithno_root_squash+insecure+/8network is a full compromise vector/srv/buildswithno_root_squashfor10.10.1.0/24allows any machine on that subnet to write files as root/srv/nfs/pubwith*is accessible from any host/srv/sharedwithcrossmntmay expose filesystems mounted under/srv/sharedthat were not intended to be exported
Run nfswolf scan and nfswolf analyze against your server to identify these issues automatically.
Managing exports with exportfs¶
The exportfs command loads, modifies, and inspects the kernel's export table. You never need to restart nfs-server.service to apply export changes; exportfs updates the kernel table live.
Common operations¶
# Re-read /etc/exports and apply all changes (add new, update changed, remove deleted)
exportfs -ra
# Re-read with verbose output showing what changed
exportfs -rav
# Show the current effective export table (with all defaults expanded)
exportfs -v
# Show exports in /var/lib/nfs/etab format (machine-readable)
exportfs -s
# Unexport a specific entry
exportfs -u 192.168.1.0/24:/srv/nfs/data
# Temporarily add an export without editing /etc/exports
exportfs -o rw,sync,no_subtree_check 192.168.1.50:/tmp/scratch
# Unexport everything
exportfs -ua
exportfs -ra vs restarting the NFS server
exportfs -ra only updates the export table. It does not drop existing client connections, invalidate file handles, or interrupt in-progress I/O. Restarting nfs-server.service does all of those things. In production, always prefer exportfs -ra over a full restart.
Verifying changes¶
After any change to /etc/exports, verify the result:
# Step 1: Apply changes
exportfs -rav
# Step 2: Check for unintended <world> entries
exportfs -v | grep '<world>'
# Step 3: Confirm specific export options
exportfs -v | grep '/srv/nfs/data'
Sample exportfs -v output
/srv/nfs/pub <world>(ro,wdelay,root_squash,no_subtree_check,sec=sys,
all_squash,anonuid=65534,anongid=65534)
/home 192.168.1.0/24(rw,wdelay,root_squash,no_subtree_check,
sec=sys,anonuid=65534,anongid=65534)
/srv/builds 10.10.1.0/24(rw,wdelay,no_root_squash,no_subtree_check,
sec=sys,anonuid=65534,anongid=65534)
/srv/builds 10.10.2.0/24(ro,wdelay,root_squash,no_subtree_check,
sec=sys,anonuid=65534,anongid=65534)
Note how exportfs -v fills in defaults you did not specify: wdelay, root_squash (or no_root_squash), sec=sys, and the anonymous UID/GID mapping. These defaults are active even though they do not appear in /etc/exports.
Troubleshooting with rpcdebug¶
When exports are not behaving as expected, kernel-level RPC and NFS debugging can reveal what the server is actually doing with incoming requests.
# Enable NFS server debug output (appears in dmesg / journalctl -k)
rpcdebug -m nfsd -s all
# Enable only export-related debugging
rpcdebug -m nfsd -s exp
# Enable RPC auth debugging (credential checks)
rpcdebug -m nfsd -s auth
# Disable all NFS server debugging
rpcdebug -m nfsd -c all
# Enable MOUNT daemon debugging (client mount requests)
rpcdebug -m nfsd -s proc
# Watch the debug output live
journalctl -kf | grep nfsd
Debug flags for common issues
| Issue | Debug flag | What it shows |
|---|---|---|
| Client denied mount | exp |
Export lookup, ACL matching |
| Permission denied on file ops | auth |
Credential mapping, squash behavior |
| Stale file handle errors | fh |
Handle verification, inode resolution |
| General procedure tracing | proc |
Every NFS procedure call and result |
Drop-in export files
In addition to /etc/exports, the NFS server loads any file matching /etc/exports.d/*.exports (note the .exports extension requirement). These drop-in files use the same syntax and are processed after the main file. This is useful for configuration management tools that need to add exports without modifying the main file.
# /etc/exports.d/backups.exports
/var/backups backup01.internal(rw,sync,no_root_squash)
/var/backups backup02.internal(rw,sync,no_root_squash)
Run exportfs -rav after adding or modifying drop-in files.
Quick reference¶
| Element | Syntax | Example |
|---|---|---|
| Single host | hostname or IP | server1(rw) |
| Network (CIDR) | IP/prefix |
10.0.0.0/24(rw) |
| Network (mask) | IP/mask |
10.0.0.0/255.255.255.0(rw) |
| Wildcard | *.domain |
*.example.com(ro) |
| Netgroup | @name |
@trusted(rw) |
| Everyone | * |
*(ro,all_squash) |
| Comment | # text |
# This is a comment |
| Continuation | trailing \ |
host1(rw) \ |
| Options | client(opt1,opt2) |
host(rw,sync,no_root_squash) |
Next steps¶
- Export options -- what each option does and its security impact
- Hardening checklist -- a prioritized list of changes to make
- Firewall rules -- network-level controls for NFS services