Longhorn is a distributed block storage system for Kubernetes. This guide covers deploying it on a kubeadm-provisioned cluster running on RamNode KVM VPS instances, across one or more RamNode regions: Atlanta (ATL), Piscataway (EWR), Seattle (PNW), Los Angeles (LAX), and Amsterdam (NLX).
Assumption: you have a working Kubernetes cluster (1.27+) on RamNode VPS nodes, with
kubectlandhelmconfigured from your jump host. If any of that isn't true yet, say so and I'll add a cluster-bootstrap section.
1. Regional topology consideration (RamNode-specific)
Longhorn replicates volumes at the storage layer and is latency-sensitive between replicas. If your nodes span RamNode regions such as ATL, EWR, PNW, LAX, and NLX:
- Do not spread a single Longhorn volume's replicas across regions — cross-region latency will tank IOPS and can trigger replica rebuilds.
- Run one Longhorn deployment per region (i.e., regional clusters, not one global cluster), or if you must run a single multi-region cluster, use Longhorn's node/zone tagging so replicas stay within a region:
# Label nodes by region during kubeadm join / after
kubectl label node <node> topology.kubernetes.io/zone=atlLonghorn's Data Locality and anti-affinity settings key off standard
topology.kubernetes.io/zone labels, so tag every node accordingly before
installing.
2. Node prerequisites
Run on every node that will host Longhorn storage (all worker nodes, typically):
# Ubuntu/Debian nodes
sudo apt-get update
sudo apt-get install -y open-iscsi nfs-common
sudo systemctl enable --now iscsid
# CentOS/RHEL/AlmaLinux nodes
sudo yum install -y iscsi-initiator-utils nfs-utils
sudo systemctl enable --now iscsidCheck kernel modules Longhorn needs:
# On each node
sudo modprobe dm_crypt # if you plan to use encrypted volumes
lsmod | grep iscsi_tcpExtra disks
RamNode KVM VPS plans typically ship with a single root volume. If you want Longhorn to use dedicated block storage rather than carving space out of the root disk:
- Attach an additional volume/disk to each VPS via your provisioning path using your provider control panel or API.
- Format and mount it as a dedicated Longhorn data path:
sudo mkfs.ext4 /dev/vdb
sudo mkdir -p /var/lib/longhorn
echo '/dev/vdb /var/lib/longhorn ext4 defaults 0 2' | sudo tee -a /etc/fstab
sudo mount -aAutomate this with a configuration management role so it stays consistent across nodes rather than being done by hand.
Pre-install check tool
Longhorn ships an environment checker — run it before installing:
kubectl apply -f https://raw.githubusercontent.com/longhorn/longhorn/v1.7.2/deploy/prerequisite/longhorn-iscsi-installation.yaml
kubectl apply -f https://raw.githubusercontent.com/longhorn/longhorn/v1.7.2/deploy/prerequisite/longhorn-nfs-installation.yamlPin to the Longhorn version you're actually installing — check https://github.com/longhorn/longhorn/releases for the current stable tag before running this in production.
3. Install Longhorn (Helm)
helm repo add longhorn https://charts.longhorn.io
helm repo update
kubectl create namespace longhorn-system
helm install longhorn longhorn/longhorn \
--namespace longhorn-system \
--set defaultSettings.defaultDataLocality=best-effort \
--set defaultSettings.replicaAutoBalance=best-effort \
--set persistence.defaultClassReplicaCount=3For a single-region, small fleet, 3 replicas per volume is a reasonable default. For edge/DR-style nodes with fewer hosts, drop to 2 and accept the reduced fault tolerance.
Wait for all components to come up:
kubectl -n longhorn-system get pods -w4. Set as default StorageClass (optional)
kubectl get storageclass
kubectl patch storageclass longhorn -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'If you're also deploying CloudNativePG, you'll likely want a dedicated, non-default StorageClass tuned for database I/O (see the CloudNativePG guide's storage section) rather than relying on this default.
5. Access the UI
Don't expose the Longhorn UI publicly by default — port-forward from your jump host:
kubectl -n longhorn-system port-forward svc/longhorn-frontend 8080:80Then tunnel to it: ssh -L 8080:localhost:8080 jumphost. If you want
persistent access, put it behind an ingress with basic auth or your existing
VPN, not directly on the internet.
6. Backups
Longhorn backs up to an S3-compatible target. RamNode doesn't have a native object storage product, so common options:
- Stand up a small MinIO instance on its own RamNode VPS as the backup target.
- Point at an external S3-compatible provider.
kubectl -n longhorn-system create secret generic longhorn-backup-secret \
--from-literal=AWS_ACCESS_KEY_ID=<key> \
--from-literal=AWS_SECRET_ACCESS_KEY=<secret> \
--from-literal=AWS_ENDPOINTS=https://minio.internal.ramnode:9000Set the backup target in Longhorn UI (Settings → General → Backup Target) or
via kubectl edit settings.longhorn.io backup-target -n longhorn-system.
Set a RecurringJob for scheduled snapshots/backups per volume rather than
relying on manual backups.
7. Verification checklist
kubectl -n longhorn-system get pods # all Running
kubectl get storageclass # longhorn present
kubectl get nodes.longhorn.io -n longhorn-system # all nodes schedulableCreate a test PVC and pod, write data, delete the pod, recreate it, confirm data persists — do this once per region before calling it production-ready.
8. Troubleshooting notes
- Mixed CentOS 6/7 + Ubuntu hosts: Longhorn requires a reasonably modern kernel (4.x+) with iSCSI support. CentOS 6 nodes are very likely too old to run Longhorn workers reliably — plan to exclude them or complete their AlmaLinux migration first.
- Legacy Python interpreter issues you hit in the Ansible buildout don't
affect Longhorn itself (it's all container images), but if you're
automating node prep via Ansible, reuse the same
ansible_python_interpreteroverrides you already resolved.
