Kokil Thapa - Professional Web Developer in Nepal
Freelancer Web Developer in Nepal with 15+ Years of Experience

Kokil Thapa is an experienced full-stack web developer focused on building fast, secure, and scalable web applications. He helps businesses and individuals create SEO-friendly, user-focused digital platforms designed for long-term growth.

NFS as Kubernetes Persistent Storage

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.

NFS Server/exports/dataK8s Control PlaneNFS Provisioner(CSI Driver)Worker NodesPod A (RWX)Pod B (RWX)NFS MountDynamic PV
NFS as Kubernetes Persistent Storage architecture showing the relationship between the NFS server, CSI provisioner, and consuming pods with RWX access.

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 TypeNFS Suitable?Recommended AlternativeReason
Web asset uploads (images, PDFs)YesN/ASequential writes, low IOPS, shared read access required
Shared configuration filesYesConfigMap (if static)Small files, infrequent updates, multiple consumers
MySQL / PostgreSQL primary DBNoLocal SSD / EBS / RBDFsync latency kills transaction throughput; risk of corruption
Elasticsearch / OpenSearchNoLocal NVMe / EBSRequires fsync guarantees NFS cannot reliably provide
CI/CD build cachesConditionalS3 + cache pluginHigh small-file churn saturates NFS metadata ops
Log aggregation bufferNoLocal disk + forwarderWrite 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.

NFS Server Hardeningroot_squash enabled (never no_root_squash)IP-restricted exports (/etc/exports)Firewall: port 2049 restricted to pod CIDRsec=sys or sec=krb5p authenticationRegular backup verificationKubernetes Client ControlsNetworkPolicy restricting NFS egressPod Security Standards (restricted)ReadOnlyRootFilesystem where possibleResource limits on NFS-using podsMonitoring mount failures + latency
Security controls split between NFS server hardening and Kubernetes client-side policies for safe NFS as Kubernetes Persistent Storage usage.

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.

NFS vs Block Storage Performance ProfileWorkloadNFS ScoreBlock ScoreVerdictLarge File UploadGood (80%)Excellent (95%)Both OKShared ConfigExcellent (90%)Poor (40%)NFS WinsDatabase I/OBad (20%)Excellent (98%)Avoid NFSSmall File ChurnWeak (35%)Strong (85%)Avoid NFSMedia StreamingGood (75%)Good (70%)TieScores normalized to typical 1Gbps NFS vs NVMe block storage baseline
Relative performance comparison helping decide when NFS as Kubernetes Persistent Storage is appropriate versus when block storage is required.

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.

  1. 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.
  2. Check mount options in the pod: Execute cat /proc/mounts | grep nfs inside the running container. Confirm hard mount and correct NFS version. Soft mounts or wrong versions explain intermittent failures.
  3. 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.
  4. 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.
  5. 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.

Frequently Asked Questions

Yes, for ReadWriteMany workloads like shared uploads or CMS assets. Avoid for high-IOPS databases; use block storage instead.

NFS has higher latency and lower IOPS than block storage. Suitable for file sharing, not transactional databases.

Rs 15,000–30,000/month (~USD 110–220) for a dedicated VM plus storage, depending on capacity and redundancy needs.

Define a PersistentVolume with nfs server and path fields, then bind it via a PersistentVolumeClaim. Ensure the NFS export allows the Kubernetes node IPs, mount options include hard,intr for reliability, and the kubelet service has network access to the NFS server. Test with a debug pod before deploying production workloads to verify permissions and latency.

Yes, NFS natively supports ReadWriteMany access mode. However, concurrent writes to the same file require application-level locking. In my experience with Laravel applications on Kubernetes, separate upload directories per tenant or timestamped filenames prevent corruption. Always set proper uid/gid in securityContext to match NFS export ownership, otherwise permission errors occur during runtime despite correct PV configuration.

NFSv3 lacks encryption and strong authentication; traffic is plaintext. Use NFSv4 with Kerberos or restrict exports to specific node IPs via /etc/exports. On shared infrastructure, isolate NFS traffic on a private VLAN. I always enable root_squash to prevent container root from mapping to NFS root. For sensitive data like legal documents, consider encrypting at rest or using CSI drivers with TLS support instead of bare NFS.

Soft mounts fail silently; always use hard,intr mount options so pods retry indefinitely but remain interruptible. Check NFS server load, network partitions, and stale file handles after server reboots. Run showmount -e from nodes to verify exports. In production deployments I have managed, most hangs trace to firewall rules blocking port 2049 or rpcbind, or NFS server running out of worker threads under concurrent pod mounts.

Cloud-managed EFS or Filestore removes operational overhead and scales automatically. Self-hosted NFS costs less at small scale but requires patching, monitoring, and backup management. For Nepal-based projects where cloud regions add latency or cost exceeds budget, self-hosted NFS on local VMs remains practical. Evaluate based on team capacity: if no one can troubleshoot NFS at 2 AM, pay for managed storage.

Snapshot the underlying NFS filesystem using LVM, ZFS, or storage-array snapshots. Alternatively, run Velero with restic to back up PVC contents to object storage. Schedule backups during low-traffic windows since NFS reads compete with application I/O. Verify restores quarterly. On client projects, I combine nightly rsync to offsite storage with weekly full snapshots, keeping retention aligned with business requirements rather than defaulting to arbitrary policies.

Yes, NFS volumes persist independently of nodes. When autoscaler adds or replaces nodes, ensure new nodes have network access to the NFS server and required packages installed via user-data scripts. Pre-bake NFS client tools into node images to avoid mount failures during scale-up. Test scaling events explicitly; I have seen deployments fail because new nodes lacked nfs-common package or security group rules, causing pending PVCs and crashed pods.

Use nfs-kernel-server on Ubuntu 22.04/24.04 LTS for stability and long-term support. Avoid userspace NFS servers for production due to lower throughput and missing kernel caching. Configure sync exports for data integrity unless benchmarking proves async safe for your workload. Export paths should reside on dedicated XFS or ext4 partitions with reserved inodes. Monitor with nfsstat and exportfs -v to catch misconfigurations before they impact running pods.

Track nfsstat -c on clients and nfsstat -s on server for RPC retransmissions, slow calls, and thread saturation. Expose metrics via Prometheus node_exporter with NFS collector enabled. Alert on retransmission rate above 1% and average RTT exceeding 10ms. Correlate with pod restart counts and PVC mount durations. In practice, I add Grafana dashboards showing per-export bandwidth alongside Kubernetes scheduler latency to distinguish NFS bottlenecks from application issues during incident response.

Mount the legacy NFS share temporarily in a migration pod, copy data to the new Kubernetes-bound NFS PV using rsync -aHAXx, then verify checksums. Update application configs to point to new PVC. Perform cutover during maintenance window with read-only source to prevent drift. Retain old share read-only for rollback. I have executed this pattern for WordPress media libraries and legal document repositories, validating file counts and permissions before decommissioning source exports.

Mismatched UID/GID between pod securityContext and NFS export ownership causes Permission denied. Set fsGroup in podSecurityPolicy or securityContext.fsGroup to match NFS directory owner. Disable root_squash only if absolutely necessary; prefer anonuid/anongid mapping. Verify export options with cat /proc/fs/nfs/exports. On Laravel projects, I standardize on UID 1000 for www-data across containers and NFS exports, enforced via CI checks to prevent deployment-time permission regressions.

Avoid NFS for databases, message queues, Elasticsearch indices, or any workload requiring fsync guarantees and low-latency random I/O. Block storage (EBS, RBD) or specialized operators (Postgres Operator, Redis Cluster) are mandatory. Also avoid when compliance mandates encrypted-at-rest storage without application-layer encryption. NFS excels for static assets, shared config, logs, and CMS uploads. Choosing wrong storage type causes silent data loss or chronic performance issues that surface only under production load.

Share this article

Quick Contact Options
Choose how you want to connect me: