AWS EC2 NFS Server Setup: Step-by-Step Guide
π What is NFS?
Network File System (NFS) is a protocol developed by Sun Microsystems in 1984 that allows file sharing over a network. With NFS, you can access remote files as if they were local.
Key features of NFS:
- Cross-platform compatibility β works across Linux, Unix, Windows
- Transparency β remote files look like local files
- Centralized storage β one location, multiple clients
- Stateless protocol β the server does not track client states
When to choose Self-managed NFS:
- Need custom NFS configurations
- Cost optimization for large datasets
- Specific performance tuning requirements
- Integration with existing NFS infrastructure
When to choose EFS:
- Need multi-AZ availability
- Want fully managed solution
- Variable workloads
- Quick deployment requirement
π Common Use Cases
- Web servers β sharing static files
- Kubernetes β used as Persistent Volumes (PVs)
- Development environments β share source code and build artifacts
- Log management β centralized log collection
- Backups β automated backup and disaster recovery
ποΈ NFS Architecture on AWS EC2
π οΈ Prerequisites
AWS CLI v2 installed
- macOS:
brew install awscli - Linux: download zip from AWS β unzip β install
- AWS credentials configured
aws configure # Provide Access Key, Secret Key, Region, Output FormatInfrastructure Requirements:
- 2x EC2 Instances (Amazon Linux 2023, t3.micro is fine for testing)
- VPC + Subnet (default VPC is okay for labs)
- Security Groups
- SSH Key Pair for access
π Create SSH Key Pair (CLI)
aws ec2 create-key-pair \
--key-name nfs-lab-key \
--query 'KeyMaterial' \
--output text > nfs-lab-key.pem
chmod 400 nfs-lab-key.pemπ Security Group Rules
NFS Server SG
- Port 2049 (TCP/UDP) β Source: NFS Client SG
- Port 111 (TCP/UDP) β Source: NFS Client SG (rpcbind)
- Port 22 (TCP) β Source: Your IP
NFS Client SG
- Port 22 (TCP) β Source: Your IP
CLI Example (Server SG):
aws ec2 create-security-group \
--group-name nfs-server-sg \
--description "NFS Server SG"
aws ec2 authorize-security-group-ingress \
--group-name nfs-server-sg \
--protocol tcp --port 22 --cidr <YOUR_IP>/32
# NFS TCP and UDP (CORRECTED)
aws ec2 authorize-security-group-ingress \
--group-name nfs-server-sg \
--protocol tcp --port 2049 --source-group <CLIENT_SG_ID>
aws ec2 authorize-security-group-ingress \
--group-name nfs-server-sg \
--protocol udp --port 2049 --source-group <CLIENT_SG_ID>
# RPC TCP and UDP (CORRECTED)
aws ec2 authorize-security-group-ingress \
--group-name nfs-server-sg \
--protocol tcp --port 111 --source-group <CLIENT_SG_ID>
aws ec2 authorize-security-group-ingress \
--group-name nfs-server-sg \
--protocol udp --port 111 --source-group <CLIENT_SG_ID>π Step 1: Setup NFS Server
1.1 Launch EC2 (Server)
- AMI: Amazon Linux 2023
- Type: t3.micro
- Subnet: Public subnet
- SG: NFS Server SG
β οΈ Public Subnet Security Note: Since weβre using public subnet, ensure your security groups are properly configured to restrict access only to your client instances, not the entire internet.
CLI Option:
aws ec2 run-instances \
--image-id <AMI_ID> \
--count 1 \
--instance-type t3.micro \
--key-name nfs-lab-key \
--security-group-ids <SERVER_SG_ID> \
--subnet-id <SUBNET_ID> \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=NFS-Server}]'1.2 Install Packages
sudo dnf update -y
sudo dnf install -y nfs-utils1.3 Create Shared Directory
sudo mkdir -p /srv/nfs-share
echo "Hello NFS! Server time: $(date)" | sudo tee /srv/nfs-share/readme.txt
sudo chown -R nfsnobody:nfsnobody /srv/nfs-share
sudo chmod 755 /srv/nfs-share1.4 Configure Exports
For Lab/Testing Environment:
echo "/srv/nfs-share *(rw,sync,no_subtree_check,no_root_squash)" | sudo tee -a /etc/exportsFor Production Environment (RECOMMENDED):
echo "/srv/nfs-share 10.0.0.0/8(rw,sync,no_subtree_check,root_squash)" | sudo tee -a /etc/exports
sudo exportfs -rav
sudo exportfs -vβ οΈ Security Warning:
no_root_squashallows root access from clients - NEVER use in productionall_squashis recommended for maximum security- Always restrict by IP ranges, never use
*in production
sudo exportfs -rav
sudo exportfs -v1.5 Start Services
sudo systemctl enable --now nfs-server
sudo systemctl enable --now rpcbind
sudo systemctl status nfs-server
echo -e "[nfsd]\nthreads=16" | sudo tee -a /etc/nfs.conf
sudo systemctl restart nfs-server1.6 Verify Server
sudo ss -tlnp | grep :2049
showmount -e localhostπ Step 2: Setup NFS Client
2.1 Launch EC2 (Client)
- Same AMI & type
- Same VPC/subnet
- SG: NFS Client SG
CLI Option:
aws ec2 run-instances \
--image-id <AMI_ID> \
--count 1 \
--instance-type t3.micro \
--key-name nfs-lab-key \
--security-group-ids <CLIENT_SG_ID> \
--subnet-id <SUBNET_ID> \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=NFS-Client}]'2.2 Install Packages
sudo dnf update -y
sudo dnf install -y nfs-utils2.3 Mount the Share
sudo mkdir -p /mnt/nfs-share
# Production mount with performance tuning
sudo mount -t nfs -o rsize=32768,wsize=32768,hard,timeo=600 \
<NFS_SERVER_PRIVATE_IP>:/srv/nfs-share /mnt/nfs-shares-share
# Verify mount
mount | grep nfs
df -h /mnt/nfs-share2.4 Persistent Mount
echo "<NFS_SERVER_PRIVATE_IP>:/srv/nfs-share /mnt/nfs-share nfs defaults,_netdev,nofail 0 0" | sudo tee -a /etc/fstab
sudo umount /mnt/nfs-share
sudo mount -aπ§ Performance Tuning
Server-Side Tuning
# Increase NFS server threads (AL2023/RHEL9+)
echo -e "[nfsd]\nthreads=16" | sudo tee -a /etc/nfs.conf
sudo systemctl restart nfs-serverClient-Side Tuning
# Add to /etc/fstab for optimal performance
rsize=32768,wsize=32768,hard,intr,timeo=600,retrans=2Network Optimization
# On both server and client - increase network buffers
echo 'net.core.rmem_default = 262144' | sudo tee -a /etc/sysctl.conf
echo 'net.core.rmem_max = 16777216' | sudo tee -a /etc/sysctl.conf
echo 'net.core.wmem_default = 262144' | sudo tee -a /etc/sysctl.conf
echo 'net.core.wmem_max = 16777216' | sudo tee -a /etc/sysctl.conf
sudo sysctl -pπ‘οΈ Security Best Practices
1. Network Security
# For public subnet deployment, be extra careful with IP restrictions
# Option 1: Restrict to specific client IP
echo "/srv/nfs-share <CLIENT_PRIVATE_IP>/32(rw,sync,root_squash)" | sudo tee /etc/exports
# Option 2: Restrict to VPC range only
echo "/srv/nfs-share 10.0.0.0/16(rw,sync,root_squash)" | sudo tee /etc/exports
# NEVER use * wildcard in public subnets!
# β DANGEROUS: "/srv/nfs-share *(rw,sync,root_squash)"Public Subnet Additional Security:
# Block NFS access from internet using iptables
sudo iptables -A INPUT -s 10.0.0.0/16 -p tcp --dport 2049 -j ACCEPT
sudo iptables -A INPUT -s 10.0.0.0/16 -p udp --dport 2049 -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 2049 -j DROP
sudo iptables -A INPUT -p udp --dport 2049 -j DROP
# Save iptables rules
sudo iptables-save | sudo tee /etc/sysconfig/iptables2. Export Security
# Always use root_squash in production
root_squash # Maps root to anonymous user
all_squash # Maps all users to anonymous user (most secure)
no_root_squash # Preserves root access (β οΈ DANGEROUS)3. File System Security
# Set proper ownership
sudo chown -R nfsnobody:nfsnobody /srv/nfs-share
# Set appropriate permissions
sudo chmod 755 /srv/nfs-share # Directory
sudo chmod 644 /srv/nfs-share/* # Filesβ Comprehensive Testing
Basic Functionality Tests
# Read test (Client)
cat /mnt/nfs-share/readme.txt
# Write test (Client)
echo "Test file from client $(hostname) at $(date)" | sudo tee /mnt/nfs-share/client-test.txt
# Verify on server
cat /srv/nfs-share/client-test.txtPerformance Testing
# Write performance test
dd if=/dev/zero of=/mnt/nfs-share/testfile bs=1M count=100
# Read performance test
dd if=/mnt/nfs-share/testfile of=/dev/null bs=1M
# Clean up test file
rm /mnt/nfs-share/testfileMulti-Client Test
# Mount from multiple clients and test concurrent access
for i in {1..5}; do
echo "Client test $i from $(hostname)" | sudo tee /mnt/nfs-share/client-${i}.txt
doneπ¨ Troubleshooting Guide
Common Issues and Solutions
1. Mount Fails with βConnection Refusedβ
# Check if NFS service is running
sudo systemctl status nfs-server
# Check if ports are open
sudo ss -tlnp | grep -E ":2049|:111"
# Check exports
sudo exportfs -v
# Test connectivity
telnet <nfs-server-ip> 20492. βPermission Deniedβ Errors
# Check export permissions
sudo exportfs -v
# Check file ownership
ls -la /srv/nfs-share/
# Check client access
showmount -e <nfs-server-ip>
# Debug with verbose mount
sudo mount -v -t nfs <server-ip>:/srv/nfs-share /mnt/test3. βStale File Handleβ Errors
# Unmount and remount
sudo umount /mnt/nfs-share
sudo mount -a
# If persistent, restart NFS services
sudo systemctl restart nfs-server # on server
sudo systemctl restart nfs-client.target # on client4. Performance Issues
# Check network latency
ping <nfs-server-ip>
# Monitor NFS statistics
nfsstat -c # client stats
nfsstat -s # server stats
# Check mount options
mount | grep nfs
# Test with different buffer sizes
sudo mount -o remount,rsize=8192,wsize=8192 /mnt/nfs-share5. Service Wonβt Start
# Check system logs
sudo journalctl -u nfs-server -f# Check configuration syntax
sudo exportfs -rav# Check dependencies
sudo systemctl status rpcbind
sudo systemctl status nfs-server
Debug Commands Toolkit
# Server-side debugging
sudo exportfs -v # Show active exports
sudo rpcinfo -p # Show RPC services
sudo netstat -tlnp | grep nfs # Show NFS ports
sudo journalctl -u nfs-server # NFS logs
# Client-side debugging
showmount -e <server-ip> # Test server accessibility
sudo mount -v -t nfs <server>:<path> <mountpoint> # Verbose mount
rpcinfo -p <server-ip> # Test RPC connectivity
sudo tcpdump -i eth0 port 2049 # Network packet captureπ§Ή Clean-Up and Cost Optimization
When finished testing:
- Terminate EC2 instances
- Delete security groups
- Remove SSH key pairs
- Delete unused EBS volumes
- Review CloudWatch logs retention
π Additional Resources
β οΈ Important Notes:
- Never use
no_root_squashin production environments - Always use private subnets for NFS servers
- Implement proper monitoring and alerting
- Regular backup of NFS data is essential
- Consider AWS EFS for managed NFS solution
