Skip to main content

Storage (Longhorn)

Step 1: Storage prerequisites for Longhorn

Install Required Packages

sudo apt-get update
sudo apt-get install -y open-iscsi nfs-common cryptsetup
sudo systemctl enable --now iscsid

Create Storage Directory

warning

Longhorn needs a storage directory to exist. Create it on your preferred disk.

sudo mkdir -p /var/lib/longhorn
note

Update the matching path in bootstrap/templates/longhorn.yaml and ansible/group_vars/all.yaml.

note

The default path is /var/lib/longhorn to avoid user-specific home directories.

Add Node Label for Longhorn Disk

note

With createDefaultDiskLabeledNodes: true, Longhorn only creates disks on nodes with this label.

note

For single-node clusters, set defaultReplicaCount: 1 in bootstrap/templates/longhorn.yaml to avoid degraded volumes.

kubectl label node $(hostname) node.longhorn.io/create-default-disk=true --overwrite

Step 2: Resize PVCs safely

Longhorn supports volume expansion, but Kubernetes does not allow shrinking PVCs in place.

Expanding a PVC

Update the PVC size in Git and let ArgoCD sync. Longhorn will expand the volume and filesystem.

Reducing a PVC size (migration required)

To reduce a volume size without data loss, create a new PVC at the smaller size and copy data across.

  • Create a new PVC with the target size (for example jellyfin-media-200).
  • Create a temporary Pod that mounts both the old and new PVCs.
  • Copy data across and verify checksums.
  • Update the Deployment to use the new PVC.
  • Remove the old PVC once validated.

Example copy pod (replace names and namespaces):

apiVersion: v1
kind: Pod
metadata:
name: pvc-migration
namespace: media
spec:
restartPolicy: Never
containers:
- name: rsync
image: docker.io/library/alpine:3.20
command: ["/bin/sh", "-c"]
args:
- apk add --no-cache rsync && rsync -aHAX --info=progress2 /old/ /new/
volumeMounts:
- name: old
mountPath: /old
- name: new
mountPath: /new
volumes:
- name: old
persistentVolumeClaim:
claimName: jellyfin-media
- name: new
persistentVolumeClaim:
claimName: jellyfin-media-200

Step 3: Import media data into Longhorn

For large datasets, use rsync to copy from your workstation to the node, then move the data into the PVC mount. This avoids kubectl cp timeouts and supports resume.

Step 1: Sync data to the node (resumable)

rsync -av --partial --inplace --progress /path/to/Videos/ user@node:/home/user/media-import/

Re-run the same command until it finishes; rsync will resume incomplete files.

Step 2: Find the PVC mount path

POD_UID=$(kubectl -n media get pod -l app=jellyfin -o jsonpath='{.items[0].metadata.uid}')
PVC_ID=$(kubectl -n media get pvc jellyfin-media -o jsonpath='{.spec.volumeName}')
echo "/var/lib/kubelet/pods/${POD_UID}/volumes/kubernetes.io~csi/${PVC_ID}/mount"

Step 3: Copy into the PVC

sudo rsync -av --delete /home/user/media-import/ /var/lib/kubelet/pods/<pod-uid>/volumes/kubernetes.io~csi/<pvc-id>/mount/Videos/

After the copy, Jellyfin should see the media under /media/Videos.