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:

Why We Tried It

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:

Local Path Provisioner: The Solution

What is Local Path Provisioner?

A simple storage provisioner that uses local node storage:

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

  1. PVC Created:

    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
      name: my-pvc
    spec:
      storageClassName: local-path
      resources:
        requests:
          storage: 10Gi
    
  2. Local Path Provisioner:

    • Creates directory on node: /opt/local-path-provisioner/pvc-<uuid>/
    • Creates PersistentVolume pointing to that directory
    • Binds PVC to PV
  3. 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

FeatureLonghornLocal Path
Setup ComplexityHighLow
DependenciesiSCSI requiredNone
Replication✅ Yes❌ No
Snapshots✅ Built-in❌ Manual
Web UI✅ Yes❌ No
Resource UsageHighLow
Single NodeOverkillPerfect
Multi NodeExcellentLimited

Use Cases

Local Path Provisioner is Perfect For:

When to Use Longhorn:

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:

Performance

Local Path

Longhorn

For single node, Local Path is faster.

Migration Path

If we need Longhorn later:

  1. Add more nodes to cluster
  2. Install Longhorn (with iSCSI setup)
  3. Migrate data from Local Path to Longhorn
  4. 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

  1. Monitor Disk Usage - Local Path uses node disk directly
  2. Set Reasonable Limits - Don’t overallocate storage
  3. Regular Backups - No built-in backup, plan accordingly
  4. Clean Up Unused PVCs - Reclaim storage space
  5. Use for Non-Critical Data - Important data needs backup strategy

Conclusion

For a single-node homelab, Local Path Provisioner is the perfect choice:

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.


Next: KubeVirt: Running VMs in Kubernetes