aly badawy/homelab
all systems operational
// cluster · storage

Longhorn

Cloud-native distributed block storage built into the cluster. On a single-node setup, replicas are set to 1 — no need to replicate across nodes that don't exist. Automated NAS backups guard against data loss.

Healthy namespace: longhorn-system 1 replica NAS backup target

Longhorn provides the longhorn StorageClass used by Vault, Authentik, Nextcloud, Immich, and PostgreSQL. Every PVC backed by Longhorn has recurring snapshot and backup jobs enabled via annotation — backups land on the NAS over NFS.

01 Configuration

The key deviation from Longhorn defaults is replica count 1. With a single node there is no second node to place a replica on — leaving the default at 3 would cause volumes to stay in a degraded state. Everything else is standard.

Property Value
Namespace longhorn-system
Default replica count 1 — single-node cluster; replication has no benefit
Backup target NFS — nfs://172.20.20.2:/var/nfs/shared/backups
Backup poll interval 300 seconds
StorageClass longhorn (default for stateful apps)
UI longhorn.in.alybadawy.com (LAN and VPN only)

02 Backup annotations

Longhorn's recurring backup jobs are opt-in per PVC via an annotation. Any PVC with recurring-job-group.longhorn.io/default: enabled participates in the default backup schedule. All stateful app PVCs in this cluster carry this annotation.

k8s/components/vault/pvc.yaml yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: vault-data-lh
  namespace: security
  labels:
    app.kubernetes.io/name: vault
  annotations:
    recurring-job-group.longhorn.io/default: enabled  # ← joins default backup schedule
spec:
  accessModes: [ReadWriteOnce]
  storageClassName: longhorn
  resources:
    requests:
      storage: 5Gi
New PVCs need the annotation. If you add a new stateful app and forget recurring-job-group.longhorn.io/default: enabled, its volume will not be backed up. Check the Longhorn UI under Volumes to verify a new volume shows the recurring job attached.

03 Restoring volumes on a rebuild

When rebuilding from scratch, Longhorn PVCs are restored manually via the Longhorn UI before ArgoCD deploys stateful apps. This is Step 8 of the provisioning sequence:

  1. provision/restore-volumes installs Longhorn and opens the UI via port-forward on localhost:9000.
  2. In the UI: Backup → select each volume → Restore → create PV/PVC with the same name.
  3. The script waits for confirmation, then exits.
  4. When activate-gitops.sh deploys stateful apps, they bind to the pre-created PVCs instead of triggering dynamic provisioning.
Pre-bound PVCs. Restored PVCs are created with spec.volumeName pointing at the specific Longhorn volume. When ArgoCD syncs the app, the Deployment finds the PVC already bound — no data loss, no re-initialisation.

04 Access

access options bash
# UI via ingress (once ingress-nginx and cert-manager are live)
# → https://longhorn.in.alybadawy.com

# UI via port-forward (bootstrap / before ingress is live)
$ kubectl port-forward -n longhorn-system svc/longhorn-frontend 9000:80
# open http://localhost:9000

# check volume status from CLI
$ kubectl get pvc -A
$ kubectl get volumes.longhorn.io -n longhorn-system
last updated 2026-06-08 · view source on GitHub