NFS (Network File System)

An In-Depth Retrospective on NFS Mounting Issues & Establishing a Standard Workflow

Foreword

This document aims to provide a retrospective on the troubleshooting process for a persistent Connection refused error encountered when mounting an Ubuntu NFS server (Anastasia-Ubuntu) from an embedded BusyBox system (david host). The issue was ultimately resolved by adding the -o nolock mount option. This article will analyze the root cause of the failure in detail and establish a standard workflow for configuring NFS servers and clients, providing a reliable reference for future deployments.


Part 1: Deep Dive into the “Connection refused” Issue

1.1 Problem Description

When attempting to mount a shared directory from an NFS server (192.168.3.164) on an embedded client (david) using standard commands, the operation consistently failed with a Connection refused error, even after multiple syntax corrections.

Key Log of the Failure:

[root@david:/mnt]# mount -t nfs 192.168.3.164:/mnt/nfs/rv1126 /mnt/nfs
mount: mounting 192.168.3.164:/mnt/nfs/rv1126 on /mnt/nfs failed: Connection refused

However, upon adding the -o nolock option, the mount operation succeeded immediately.

Log of the Successful Mount:

[root@david:/mnt]# mount -t nfs 192.168.3.164:/mnt/nfs/rv1126 -o nolock /mnt/nfs
[root@david:/mnt]# df -h
Filesystem                Size      Used Available Use% Mounted on
/dev/root                 2.9G    751.8M      2.1G  26% /
...
192.168.3.164:/mnt/nfs/rv1126
                        453.2G    236.4G    193.8G  55% /mnt/nfs

1.2 The Troubleshooting Journey: Ruling Out the Usual Suspects

Before identifying -o nolock as the solution, we conducted an exhaustive investigation, systematically eliminating all common causes of NFS failures.

  • NFS Service on the Server: By running rpcinfo -p localhost on the server, we confirmed that NFS v3, NFS v4, and the mountd service were all running correctly and listening on the network ports. Key Log Evidence:
    james@Anastasia-Ubuntu:~$ rpcinfo -p localhost
       program vers proto   port  service
       ...
        100005    3   tcp  13025  mountd
        100003    3   tcp   2049  nfs
        100003    4   tcp   2049  nfs
       ...
    
  • Server Firewall: The sudo ufw status command confirmed the firewall was inactive, meaning no network traffic was being blocked.
  • Server Export Configuration (/etc/exports): We verified that the shared directory was correctly exported to all clients (*) and that the insecure option was added for compatibility with embedded devices.
  • Client Kernel Support: By running cat /proc/filesystems on the client, we confirmed that the kernel was compiled with nfs filesystem support. Key Log Evidence:
    [root@david:/root]# cat /proc/filesystems
    ...
    nodev   nfs
    ...
    
  • Client Functionality Integrity: A decisive clue was that the “client david could successfully mount NFS shares from other PCs,” proving that the client’s own NFS functionality was not at fault.

All standard checks indicated a “perfectly normal” setup, yet the problem persisted. This forced us to shift our focus to the deeper, more subtle details of the NFS protocol.

1.3 Root Cause Analysis: Why -o nolock is the Key?

The root cause was a communication failure between the client and server regarding the “file locking” feature.

  1. File Locking Mechanism: To ensure data consistency when multiple clients access the same file, the NFS protocol implements a file locking mechanism. This mechanism relies on a separate protocol known as NLM (Network Lock Manager). A standard NFS mount process involves not only establishing a data transfer channel but also requires the client to perform a “handshake” with the server’s lock manager service (nlockmgr) to handle future file lock requests.

  2. Client’s Missing Functionality: Our client, david, is a highly stripped-down BusyBox system. To minimize its footprint, its NFS client implementation likely includes only the core data read/write functionality, while the complete NLM file locking client module has been omitted.

  3. The Failed “Handshake”: When david attempted to mount, it followed the standard procedure. After the data channel was established, it tried to communicate with the server’s nlockmgr service. However, due to its incomplete functionality, the request it sent was likely malformed or unrecognizable to the server. The server’s RPC system, upon receiving this invalid request, could not process it and therefore actively refused the connection (Connection refused), causing the entire mount operation to fail.

  4. The Role of -o nolock: This option is an instruction to the client’s kernel: “For this mount, completely disable the NLM file locking protocol.” Upon receiving this directive, the kernel skips the step of communicating with the server’s nlockmgr service. Because this guaranteed-to-fail step was bypassed, the rest of the mount process completed smoothly, resulting in a successful mount.

Conclusion: The Connection refused error did not stem from a network or permission issue, but rather from the stripped-down embedded client’s inability to complete the “file lock negotiation” step of the standard NFS mount procedure, leading to rejection by the fully-featured server. The -o nolock option is the key to resolving such mount failures caused by incomplete client functionality.


Part 2: A Standard Workflow for NFS Setup

The following workflow is designed to provide a reliable and reproducible process for setting up an NFS environment, with special considerations for using embedded devices as clients.

2.1 Server-Side Configuration - Ubuntu/Debian Example

Step 1: Install NFS Services

sudo apt update
sudo apt install nfs-kernel-server

Step 2: Create and Prepare the Shared Directory

# Example: Create a shared directory named nfs_share
sudo mkdir -p /mnt/nfs_share
# Assign permissive ownership to ensure client access
sudo chown nobody:nogroup /mnt/nfs_share
sudo chmod 777 /mnt/nfs_share

Step 3: Configure the Exports File /etc/exports (Core Step)

Open /etc/exports with a text editor:

sudo nano /etc/exports

Add a line to define the sharing rule. The following is a recommended configuration that is highly compatible and suitable for development environments:

$$/mnt/nfs_share *(rw,sync,no_subtree_check,no_root_squash,insecure)$$

Options Explained & Notes:

  • /mnt/nfs_share: The absolute path of the directory to be shared.
  • *: Allows access from any IP address. For better security, this can be replaced with a specific subnet, e.g., 192.168.3.0/24.
  • rw: Allows read and write operations.
  • sync: Writes data synchronously (as opposed to async), which is safer and less prone to data loss.
  • no_subtree_check: Disables subtree checking, which can improve performance and reliability.
  • no_root_squash: Allows the root user on the client to have root privileges on the mount point. This is crucial for embedded development and debugging.
  • insecure: [Note 1: Critical for Embedded Device Compatibility] Allows connections from clients using non-privileged ports (>1024). Many stripped-down embedded NFS clients cannot use privileged ports, and the absence of this option is a common cause of Connection refused errors.

Step 4: Apply the Configuration

After every modification to /etc/exports, you must run this command to refresh the configuration:

sudo exportfs -ra

To ensure all services are updated, you can also restart the NFS service:

sudo systemctl restart nfs-kernel-server

Step 5: Check the Firewall (If Active)

If the server’s firewall (ufw) is active, you need to add an inbound rule:

# Check status
sudo ufw status

# Allow NFS connections from the entire local network (recommended)
sudo ufw allow from 192.168.3.0/24 to any port nfs

2.2 Client-Side Configuration - Using the Embedded david as an Example

Step 1: Verify Basic Client Capabilities

Before mounting, perform a quick diagnosis to ensure prerequisites are met.

# 1. Check if the kernel supports NFS
cat /proc/filesystems | grep nfs
# Expected output should include a line with "nfs"

# 2. Check network connectivity
ping <server_ip_address>

Step 2: Create a Local Mount Point

mkdir -p /mnt/server_share

Step 3: Execute the Mount Command (Core Step)

Use the full command with compatibility options to perform the mount:

mount -t nfs -o nfsvers=3,nolock <server_ip>:<absolute_path_on_server> <local_mount_point>

Specific Example:

mount -t nfs -o nfsvers=3,nolock 192.168.3.164:/mnt/nfs_share /mnt/server_share

Options Explained & Notes:

  • -t nfs: Explicitly specifies the filesystem type as NFS.
  • -o nfsvers=3: [Note 2: Critical for Embedded Device Compatibility] Explicitly specifies the use of the NFSv3 protocol. Embedded clients may have incomplete support for the more complex NFSv4 protocol; forcing v3 can prevent many authentication and protocol negotiation issues.
  • -o nolock: [Note 3: Critical for Embedded Device Compatibility] Disables the file locking (NLM) protocol. As demonstrated in this retrospective, stripped-down clients may lack NLM functionality, and this option is the ultimate solution for resolving Connection refused errors caused by a failed lock negotiation.

Step 4: Verify the Mount

# 1. Check the list of mounted filesystems
df -h
# or
mount

# 2. Check if you can read/write files
ls /mnt/server_share
touch /mnt/server_share/test_file_from_david

Step 5: Configure Automatic Mounting on Boot (Optional)

To have the device mount the share automatically at boot, edit the /etc/fstab file. Warning: On embedded systems, /etc/fstab may not exist or may be on a read-only partition. Proceed with caution.

$$# : nfs 0 0 192.168.3.164:/mnt/nfs_share /mnt/server_share nfs nfsvers=3,nolock,auto,bg 0 0$$
  • auto: Mount automatically at system startup.
  • bg: If the initial mount fails (e.g., network not ready), retry in the background. This is very useful for embedded devices.

END