
August 22, 2026
9 min read
Table of Contents
By Kokil Thapa | Last reviewed: August 2026
Configuring NFS as Kubernetes Persistent Storage remains a pragmatic choice for teams needing shared read-write volumes without the complexity of distributed block storage. While cloud-native CSI drivers dominate managed environments, self-hosted NFS offers predictable costs and simplicity for internal tools, media assets, and legacy application migrations. This guide covers the exact configuration, security constraints, and operational realities I have encountered when deploying NFS-backed persistent volumes in production clusters.
Before diving into cluster configuration, ensure your underlying infrastructure is sound. Many storage issues actually stem from misconfigured Linux servers rather than Kubernetes itself. If you are managing your own bare metal or VPS, understanding server security and hardening fundamentals is essential before exposing NFS exports to your cluster network. A secure foundation prevents data leaks that no amount of YAML configuration can fix.
How do you configure NFS as Kubernetes Persistent Storage?
The most reliable method in 2026 uses the Container Storage Interface (CSI) driver rather than the deprecated in-tree NFS plugin. The CSI approach separates storage logic from the core Kubernetes codebase, receiving independent updates and better observability. Below is the proven sequence for setting up NFS as Kubernetes Persistent Storage using the official kubernetes-sigs/nfs-subdir-external-provisioner.
Install the NFS Subdir External Provisioner
Deploy the provisioner using Helm, which handles RBAC, deployment, and storage class creation in one step. This replaces manual YAML manifests and ensures compatibility with Kubernetes 1.30+.
helm repo add nfs-subdir-external-provisioner https://kubernetes-sigs.github.io/nfs-subdir-external-provisioner/
helm install nfs-provisioner nfs-subdir-external-provisioner/nfs-subdir-external-provisioner \
--set nfs.server=192.168.1.100 \
--set nfs.path=/exports/k8s-data \
--set storageClass.name=nfs-client \
--set storageClass.defaultClass=false \
--set storageClass.reclaimPolicy=Retain The reclaimPolicy=Retain setting is critical for production. It prevents accidental data deletion when a PVC is removed. You can manually clean up old directories after verifying backups.
Create a PersistentVolumeClaim
With the provisioner running, pods request storage through a standard PVC. The provisioner automatically creates a unique subdirectory on the NFS server for each claim, enabling safe multi-tenancy.
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: app-uploads-pvc
spec:
accessModes:
- ReadWriteMany
storageClassName: nfs-client
resources:
requests:
storage: 50Gi This PVC binds dynamically. No pre-created PV is needed unless you require static provisioning for compliance or legacy reasons. The ReadWriteMany mode is the primary advantage of NFS as Kubernetes Persistent Storage over block storage alternatives.
When should you avoid using NFS for Kubernetes workloads?
NFS is not universal storage. Misapplying it causes silent corruption, performance bottlenecks, and operational pain. Understanding these boundaries saves debugging time later.
| Workload Type | NFS Suitable? | Recommended Alternative | Reason |
|---|---|---|---|
| Web asset uploads (images, PDFs) | Yes | N/A | Sequential writes, low IOPS, shared read access required |
| Shared configuration files | Yes | ConfigMap (if static) | Small files, infrequent updates, multiple consumers |
| MySQL / PostgreSQL primary DB | No | Local SSD / EBS / RBD | Fsync latency kills transaction throughput; risk of corruption |
| Elasticsearch / OpenSearch | No | Local NVMe / EBS | Requires fsync guarantees NFS cannot reliably provide |
| CI/CD build caches | Conditional | S3 + cache plugin | High small-file churn saturates NFS metadata ops |
| Log aggregation buffer | No | Local disk + forwarder | Write amplification and locking issues under load |
In my experience maintaining Laravel applications that handle document uploads and media processing, NFS works excellently for user-generated content stored via packages like Spatie Media Library. However, the same applications' MySQL databases must always run on local or block storage. Mixing these concerns on a single NFS volume is a common mistake that surfaces only under production load.
How do you secure NFS exports for Kubernetes clusters?
Default NFS configurations are insecure. Production deployments require explicit hardening at both the server and client levels. Security here is non-negotiable, especially when handling sensitive data in legal-tech or financial applications.
Server-side export configuration
Your /etc/exports file should never use wildcards or allow root access. This example restricts access to a specific Kubernetes node subnet and enforces root squashing:
/exports/k8s-data 10.244.0.0/16(rw,sync,root_squash,no_subtree_check,sec=sys)
/exports/media 10.244.0.0/16(ro,sync,root_squash,no_subtree_check,sec=sys) The sync option is mandatory for data integrity. Async mode improves throughput but risks silent data loss during crashes. For legal-tech portals handling court documents or notarized files, this trade-off is unacceptable. Always prioritize correctness over speed.
Kubernetes-side isolation
Apply NetworkPolicies to restrict which pods can reach the NFS server. Without this, any compromised pod in the cluster can attempt to mount or scan your NFS exports. Combine this with Pod Security Admission to prevent privileged containers from bypassing filesystem restrictions.
If you are integrating payment gateways or handling sensitive user data in applications like those described in Laravel payment integration guides, ensure NFS volumes storing transaction records or receipts have additional encryption at rest. NFS itself does not encrypt data in transit; use Kerberos (sec=krb5p) or tunnel through a VPN for sensitive workloads.
What are the performance limitations and tuning options?
NFS performance depends heavily on workload characteristics and network configuration. Understanding these factors prevents over-provisioning or unexpected bottlenecks.
- Metadata operations are the bottleneck: NFS struggles with thousands of small files per second. Directory listings, recursive deletes, and git operations on large repositories will be slow regardless of bandwidth.
- Sequential throughput scales with network: Large file reads/writes perform well on 10Gbps+ networks. On typical 1Gbps links, expect ~100MB/s practical throughput after protocol overhead.
- Latency dominates IOPS: Each NFS operation requires a network round-trip. At 1ms RTT, theoretical max is ~1000 IOPS. Block storage delivers 10x-100x more.
- Client caching helps reads, hurts consistency: The
ac(attribute cache) mount option improves read performance but causes stale data visibility across pods. Disable for collaborative write workloads.
Mount options for production
The default mount options are rarely optimal. Specify these explicitly in your StorageClass or PV definition:
mountOptions:
- nfsvers=4.2
- rsize=1048576
- wsize=1048576
- hard
- intr
- timeo=600
- retrans=3
- noatime
- nodiratime Use nfsvers=4.2 for best performance and feature support. Avoid NFSv3 unless legacy systems require it. The hard mount option is essential; soft mounts cause silent I/O errors that corrupt application state. The intr flag allows interrupting hung operations during maintenance.
Tuning the NFS server
On the NFS server, increase thread count and adjust kernel parameters for concurrent Kubernetes clients:
# /etc/nfs.conf.d/k8s-tuning.conf
[nfsd]
threads = 32
vers4 = y
vers3 = n
# sysctl adjustments
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
sunrpc.tcp_slot_table_entries = 128 Monitor nfsstat -s output regularly. High th (thread exhaustion) or readahead misses indicate undersizing. For clusters serving more than 20 concurrent pods, consider dedicated NFS hardware or a distributed alternative like CephFS.
How do you troubleshoot common NFS mounting failures?
NFS issues manifest as pod hangs, CrashLoopBackOff states, or silent data inconsistencies. Systematic diagnosis prevents wasted time guessing.
- Verify server reachability from the node: SSH into the worker node and run
showmount -e <nfs-server-ip>. If this fails, check firewalls, UFW rules, and whether the NFS service is active. Never assume network connectivity because pods can reach other services. - Check mount options in the pod: Execute
cat /proc/mounts | grep nfsinside the running container. Confirmhardmount and correct NFS version. Soft mounts or wrong versions explain intermittent failures. - Inspect provisioner logs: Run
kubectl logs -n nfs-provisioner deploy/nfs-provisioner. Permission denied errors usually mean root squash is active but the provisioner expects root access. Fix by adjusting export options or running the provisioner with matching UID/GID. - Validate PVC binding: Use
kubectl describe pvc <name>. Events section reveals scheduling failures, insufficient capacity, or storage class mismatches. Pending PVCs often indicate missing default storage class annotation. - Test write permissions manually: Create a test pod with the same security context as your application. Attempt to write a file. Permission errors here confirm export or ownership misconfiguration before debugging application code.
A frequent issue in Nepal-based deployments involves timezone or locale settings affecting file timestamps. Ensure NFS server and Kubernetes nodes share synchronized NTP sources. Clock skew causes cache invalidation failures and confusing "file modified in future" errors during builds or backups.
For teams managing complex application stacks alongside infrastructure, understanding full-stack debugging approaches helps correlate storage issues with application behavior. Resources covering full-stack development practices often include troubleshooting methodologies applicable to storage-layer problems.
Practical next steps for NFS as Kubernetes Persistent Storage
NFS as Kubernetes Persistent Storage serves specific niches effectively when configured correctly. Start with the CSI provisioner, enforce security defaults, validate performance against your actual workload, and monitor continuously. Avoid using it for databases or high-IOPS workloads regardless of cost savings. For teams evaluating whether their infrastructure choices align with business needs, reviewing development and infrastructure cost considerations provides context for storage budget decisions.
If you need assistance designing storage architecture for Kubernetes-hosted applications or migrating legacy systems to containerized environments, reach out to discuss your specific requirements. Production storage decisions benefit from experienced review before data is at risk.

