Kubernetes series (Article 3)
After my write-up on Kubernetes Cluster Installation (Article 2), I began exploring several critical components that every Kubernetes cluster should include, especially in a homelab or bare-metal setup. These components are not optional but essential for building a resilient, manageable, and production-like environment.
In this article, I’ll cover three of those core components:
1. Helm – Kubernetes Package Manager
Helm is often called the “apt” or “yum” of the Kubernetes world. It’s a package manager that simplifies the deployment, upgrade, and management of applications inside your cluster. Rather than manually defining and applying multiple YAML files, Helm lets you install complex applications like Portainer, Prometheus, or Longhorn with a single command. It also supports version control and rollback, making your deployments more reliable and maintainable.
2. MetalLB – Load Balancing for Bare-Metal Clusters
In cloud environments like AWS, GCP, or Azure, Kubernetes integrates seamlessly with native cloud LoadBalancers. But in self-hosted or on-prem setups (like my Proxmox-based homelab), there is no built-in way for Kubernetes to assign external IPs to services of type. LoadBalancer
.
That’s where MetalLB comes in.
It acts as a load balancer implementation for bare-metal clusters, allowing you to expose Kubernetes services to your local network using confirmed IP addresses from a predefined pool, such as:
apiVersion: v1
kind: Service
metadata:
name: my-app
spec:
type: LoadBalancer
ports:
- port: 80
selector:
app: my-app
Without MetalLB, the LoadBalancer
The type of service would be unusable in a non-cloud environment, and it would never get an external IP. With MetalLB, services like this can be reached from anywhere on your local network using IPs like 192.168.80.20–30
.
3. Longhorn – Distributed Persistent Storage
Kubernetes doesn’t provide persistent storage out of the box, especially in bare-metal deployments. Pods are ephemeral, and once they’re rescheduled or a node fails, any local data is lost.
Longhorn solves that by offering a distributed block storage solution for Kubernetes. It provides highly available Persistent Volumes (PVs) that remain intact even when pods are restarted, rescheduled, or when a node crashes.
When a pod (such as a WordPress site, Nextcloud, or a database) makes a PersistentVolumeClaim
, Longhorn:
- Creates a replicated storage volume across multiple nodes
- Mounts the volume to the requesting pod
- Automatically handles failover and volume recovery
- Offers snapshots and backup options, including AWS S3 or NFS
Installing Helm
curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3
chmod 700 get_helm.sh
./get_helm.sh # Run To exicute the script
BashThis downloads and installs the latest Helm binary. You can then confirm it’s working with:
helm version
BashMetalLB Setup – LoadBalancer for Bare-Metal Clusters
Kubernetes clusters running in cloud environments can use built-in cloud LoadBalancers (like AWS ELB or Azure LB). However, on bare-metal or homelab setups like Proxmox, Kubernetes has no way to assign external IPs to Service
objects of type LoadBalancer
.
MetalLB fills that gap, giving your services external IPs within your local network.
Deploy MetalLB
Add the MetalLB Helm repository:
helm repo add metallb https://metallb.github.io/metallb
helm repo update
BashInstall MetalLB via Helm
helm upgrade --install metallb metallb/metallb \
--namespace metallb-system \
--create-namespace
BashConfigure IP Address Pool
Create a file called metallb-config.yaml
And define an IP range MetalLB can use to assign to services:
---
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
name: default-pool
namespace: metallb-system
spec:
addresses:
- 192.168.80.20-192.168.80.30
---
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
name: l2adv
namespace: metallb-system
YAMLThen apply the configuration:
kubectl apply -f metallb-config.yaml
BashImportant: To avoid conflicts, ensure this IP range is within your VLAN and not part of your DHCP pool in Pfsence.
Longhorn Setup – Distributed Persistent Storage for Kubernetes
Longhorn is a cloud-native, distributed block storage system for Kubernetes. It provides reliable Persistent Volumes (PVS) that can survive pod failures, rescheduling, and even node crashes, making it essential for databases, stateful apps, or file-based workloads.
Install Longhorn with Helm
Add the Longhorn Helm chart and install it:
helm repo add longhorn https://charts.longhorn.io
helm repo update
kubectl create namespace longhorn-system
helm install longhorn longhorn/longhorn --namespace longhorn-system
BashFix iSCSI Issues on All Nodes
Longhorn depends on the open-iscsi
for volume mounting. Without it, Longhorn pods may fail with CrashLoopBackOff
errors.
Run the following on every node:
sudo apt install -y open-iscsi
BashExpose Longhorn UI (Optional)
By default, the Longhorn UI is only accessible within the cluster. You can expose it using MetalLB by patching the longhorn-frontend
service:
kubectl patch svc longhorn-frontend -n longhorn-system \
-p '{"spec": {"type": "LoadBalancer"}}'
BashAccess the Longhorn Web UI
After patching, use this command to get the external IP assigned to the UI:
kubectl -n longhorn-system get svc longhorn-frontend
BashOr list everything in the namespace:
kubectl -n longhorn-system get all
BashLook for the EXTERNAL-IP
in the output. Open that IP in your browser to access the Longhorn dashboard.
Securely Exposing Longhorn UI via Traefik with Basic Auth
This guide explains how to expose the Longhorn UI through Traefik using an IngressRoute
, TLS via cert-manager
, and basic authentication middleware. This is important because Longhorn itself does not provide built-in authentication.
Step 1: Create longhorn-values.yaml
Customise resource settings for Longhorn components:
defaultSettings:
defaultReplicaCount: 3
longhornManager:
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 250m
memory: 256Mi
longhornDriver:
deployer:
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 250m
memory: 256Mi
csi:
attacher:
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 250m
memory: 256Mi
provisioner:
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 250m
memory: 256Mi
resizer:
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 250m
memory: 256Mi
snapshotter:
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 250m
memory: 256Mi
plugin:
kubeletRootDir: /var/lib/kubelet
resources:
requests:
cpu: 30m
memory: 30Mi
limits:
cpu: 100m
memory: 100Mi
longhornUI:
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 250m
memory: 256Mi
YAMLStep 2: Create IngressRoute
Create longhorn-ingressroute.yaml
:
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: longhorn-ui
namespace: longhorn-system
spec:
entryPoints:
- websecure
routes:
- match: Host(`longhorn.yourdomain.uk`)
kind: Rule
services:
- name: longhorn-frontend
namespace: longhorn-system
port: 80
middlewares:
- name: longhorn-auth # Remove if not using auth
tls:
secretName: traefik-tls
YAMLApply it:
kubectl apply -f longhorn-ingressroute.yaml
BashStep 3: Create a Certificate for Longhorn
Create longhorn-certificate.yaml
:
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: traefik-tls
namespace: longhorn-system
spec:
secretName: traefik-tls
commonName: longhorn.yourdomain.uk
dnsNames:
- "longhorn.yourdomain.uk"
issuerRef:
name: cloudflare
kind: ClusterIssuer
duration: 2160h # 90 days
renewBefore: 168h # 7 days
YAMLApply:
kubectl apply -f longhorn-certificate.yaml
BashStep 4: Enable Basic Auth Middleware
Create longhorn-middleware.yaml
:
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: longhorn-auth
namespace: longhorn-system
spec:
basicAuth:
secret: authsecret-longhorn
YAMLApply it:
kubectl apply -f longhorn-middleware.yaml
BashStep 5: Create Auth Secret
Create longhorn-middleware-secret.yaml
:
apiVersion: v1
kind: Secret
metadata:
name: authsecret-longhorn
namespace: longhorn-system
type: Opaque
data:
users: c2Fuamh1bWE6JDJ5J # base64 encoded "username:hashed-password"
YAMLGenerate it with:
htpasswd -nbB sanju yourpassword | base64
BashApply:
kubectl apply -f longhorn-middleware-secret.yaml
BashStep 6: Restart or Upgrade Longhorn
After making changes, restart Longhorn UI or upgrade the release:
helm upgrade longhorn longhorn/longhorn \
-n longhorn-system \
-f longhorn-values.yaml
BashOr restart only the UI:
kubectl rollout restart deployment longhorn-ui -n longhorn-system
BashYou can now access Longhorn securely:
https://longhorn.yourdomain.uk
With Traefik TLS and optional basic authentication in place.
This completes the setup for Helm, MetalLB, and Longhorn, three essential building blocks for running stable, production-like workloads in your homelab Kubernetes environment.