Storage: Local Path Provisioner vs Longhorn
Choosing the right storage solution for a single-node Kubernetes cluster. This article explains why we chose Local Path Provisioner over Longhorn.
The Storage Challenge
Kubernetes needs a storage provisioner to create PersistentVolumes (PVs) for PersistentVolumeClaims (PVCs). For a single-node homelab, we need something simple but reliable.
Longhorn: The First Attempt
What is Longhorn?
Longhorn is a distributed block storage system for Kubernetes:
- Replicated storage across nodes
- Snapshots and backups
- Web UI for management
- Enterprise-grade features
Why We Tried It
- Popular in Kubernetes community
- Great features (snapshots, backups)
- Good documentation
- Web UI for management
Why It Failed
1. iSCSI Dependency
Longhorn requires open-iscsi on nodes:
talosctl -e 192.168.1.100 --nodes 192.168.1.100 read /usr/sbin/iscsiadm
# error: no such file or directory
Talos Linux is immutable and minimal - it doesn’t include iSCSI tools by default.
2. PodSecurity Issues
Even after labeling namespace as privileged, Longhorn pods crashed:
kubectl describe pod -n longhorn-system longhorn-manager-xxxxx
# Error: violates PodSecurity "baseline:latest"
# - hostPath volumes
# - privileged containers
3. Complexity Overkill
For a single node:
- Replication is unnecessary (no other nodes)
- Snapshots can be done at filesystem level
- Web UI adds overhead
- More moving parts = more failure points
Local Path Provisioner: The Solution
What is Local Path Provisioner?
A simple storage provisioner that uses local node storage:
- No external dependencies
- Works out of the box
- Lightweight and fast
- Perfect for single-node clusters
Installation
kubectl apply -f https://raw.githubusercontent.com/rancher/local-path-provisioner/v0.0.26/deploy/local-path-storage.yaml
# Set as default StorageClass
kubectl patch storageclass local-path \
-p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'
That’s it! No configuration needed.
How It Works
-
PVC Created:
apiVersion: v1 kind: PersistentVolumeClaim metadata: name: my-pvc spec: storageClassName: local-path resources: requests: storage: 10Gi -
Local Path Provisioner:
- Creates directory on node:
/opt/local-path-provisioner/pvc-<uuid>/ - Creates PersistentVolume pointing to that directory
- Binds PVC to PV
- Creates directory on node:
-
Pod Uses Storage:
volumeMounts: - name: data mountPath: /data volumes: - name: data persistentVolumeClaim: claimName: my-pvc
Configuration
Default configuration:
apiVersion: v1
kind: ConfigMap
metadata:
name: local-path-config
namespace: local-path-storage
data:
config.json: |-
{
"nodePathMap": [
{
"node": "DEFAULT_PATH_FOR_NON_LISTED_NODES",
"paths": ["/opt/local-path-provisioner"]
}
]
}
Customize storage paths per node if needed.
Comparison
| Feature | Longhorn | Local Path |
|---|---|---|
| Setup Complexity | High | Low |
| Dependencies | iSCSI required | None |
| Replication | ✅ Yes | ❌ No |
| Snapshots | ✅ Built-in | ❌ Manual |
| Web UI | ✅ Yes | ❌ No |
| Resource Usage | High | Low |
| Single Node | Overkill | Perfect |
| Multi Node | Excellent | Limited |
Use Cases
Local Path Provisioner is Perfect For:
- Single-node homelabs - Simple and sufficient
- Development environments - Fast setup
- Non-critical data - Logs, caches, temp files
- VM disks - KubeVirt VMs (non-replicated is OK for lab)
When to Use Longhorn:
- Multi-node clusters - Replication needed
- Production workloads - High availability required
- Backup requirements - Built-in snapshot/backup
- Complex storage needs - Multiple storage classes
Real-World Usage
NPM Data Persistence
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: npm-data
namespace: npm
spec:
storageClassName: local-path
resources:
requests:
storage: 10Gi
NPM configuration persists across pod restarts.
Blog Content
apiVersion: v1
kind: ConfigMap
metadata:
name: blog-content
namespace: blog
# ConfigMap for static files (no PVC needed)
Static blog uses ConfigMap (no persistence needed).
VM Disks
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: vm-ubuntu-disk
spec:
storageClassName: local-path
resources:
requests:
storage: 50Gi
KubeVirt VMs use PVCs for disk storage.
Backup Strategy
Since Local Path doesn’t have built-in backups:
Manual Backup
# Backup PVC data
kubectl exec -n npm $(kubectl get pod -n npm -l app=nginx-proxy-manager -o jsonpath='{.items[0].metadata.name}') -- \
tar -czf /tmp/backup.tar.gz /data
# Copy to external storage
kubectl cp npm/pod-name:/tmp/backup.tar.gz ./backup.tar.gz
Automated Backup (Future)
Options:
- Velero - Kubernetes backup tool
- CronJob - Scheduled backups
- rsync - Sync to external server
- Filesystem snapshots - LVM/ZFS snapshots
Performance
Local Path
- Latency: Very low (local disk)
- Throughput: Limited by disk speed
- IOPS: Limited by disk IOPS
- Overhead: Minimal
Longhorn
- Latency: Slightly higher (network replication)
- Throughput: Limited by network + disk
- IOPS: Distributed across nodes
- Overhead: Higher (replication, management)
For single node, Local Path is faster.
Migration Path
If we need Longhorn later:
- Add more nodes to cluster
- Install Longhorn (with iSCSI setup)
- Migrate data from Local Path to Longhorn
- Update StorageClass references
Local Path PVCs can coexist with Longhorn.
Troubleshooting
PVC Stuck in Pending
# Check provisioner pod
kubectl get pods -n local-path-storage
# Check logs
kubectl logs -n local-path-storage -l app=local-path-provisioner
# Check node permissions
talosctl -e 192.168.1.100 --nodes 192.168.1.100 ls -la /opt/local-path-provisioner
PodSecurity Issues
# Label namespace
kubectl label namespace local-path-storage \
pod-security.kubernetes.io/enforce=privileged --overwrite
Disk Space
# Check usage
talosctl -e 192.168.1.100 --nodes 192.168.1.100 df -h
# Clean up old PVCs
kubectl delete pvc old-pvc-name
Best Practices
- Monitor Disk Usage - Local Path uses node disk directly
- Set Reasonable Limits - Don’t overallocate storage
- Regular Backups - No built-in backup, plan accordingly
- Clean Up Unused PVCs - Reclaim storage space
- Use for Non-Critical Data - Important data needs backup strategy
Conclusion
For a single-node homelab, Local Path Provisioner is the perfect choice:
- ✅ Simple installation
- ✅ No dependencies
- ✅ Low overhead
- ✅ Fast performance
- ✅ Sufficient for lab workloads
Longhorn is excellent for multi-node production clusters, but overkill for a single-node lab.
Key Takeaway: Choose the right tool for your use case. Simpler is often better.