K3s en Proxmox: un clúster de Kubernetes de nivel producción en tu homelab
Homelab

K3s en Proxmox: un clúster de Kubernetes de nivel producción en tu homelab

Ricardo Gil
12 de octubre de 2026
10 min de lectura

Traducido del original en inglés. Read this article in English →

#K3s #Kubernetes #Proxmox #Home Lab #Self-Hosting #Longhorn #Traefik

K3s en Proxmox: un clúster de Kubernetes de nivel producción en tu homelab

Mucha gente duda en llevar Kubernetes a su homelab. Un K8s completo se siente exagerado para unos cuantos servicios autoalojados, y el mantenimiento de un clúster que tocas una vez por semana parece un mal negocio. K3s sobre VMs de Proxmox cambia esa cuenta por completo.

K3s viene en un solo binario de 70 MB. Pasa todas las pruebas de conformidad de Kubernetes. Funciona cómodamente en nodos con 1 CPU y 512 MB de RAM (aunque vas a querer más para cargas reales). Lo mantiene Rancher/SUSE y tiene desarrollo activo. Y si lo combinas con Proxmox como hipervisor, obtienes aislamiento a nivel de VM, snapshots antes de actualizar y recreación fácil de nodos, todo en hardware común.

Esta guía te lleva a montar un clúster K3s real de varios nodos en Proxmox: tres VMs, almacenamiento persistente con Longhorn, ingress con Traefik y acceso remoto con Tailscale. Nada de setups de juguete.


Por qué K3s y no Kubernetes completo (y por qué no simplemente Docker Compose)

Si corres servicios en tu homelab, Docker Compose funciona muy bien hasta que deja de funcionar. Los límites típicos: no hay actualizaciones progresivas, no hay reprogramación automática según el estado de salud, hay que manejar los certificados SSL a mano por servicio, no se aplican límites de recursos y las definiciones de las cargas no son portables entre máquinas.

K3s resuelve todo eso. Y a diferencia de Kubernetes completo:

  • No requiere etcd por defecto (usa SQLite en clústeres pequeños; puedes agregar etcd embebido para alta disponibilidad)
  • Elimina APIs heredadas y en fase alfa que agregan superficie de mantenimiento
  • Incluye Traefik, CoreDNS, Flannel y local-path provisioner: valores por defecto razonables, sin instalaciones extra
  • Se actualiza con k3s-upgrade-controller, aplicando un solo CRD

La contrapartida frente a k8s: K3s toma algunas decisiones por ti (Flannel como CNI por defecto, Traefik como ingress). Puedes cambiarlas, pero si necesitas redes con Calico/Cilium o vas detrás de lo más nuevo de sig-network, un k8s completo puede encajar mejor. ¿Para un homelab con cargas reales? K3s es la decisión correcta.


Requisitos de hardware y setup de referencia

No necesitas mucho. Un setup de referencia razonable es un mini PC Beelink EQ12 como host, con tres VMs de Proxmox:

Nodo Rol vCPU RAM Disco
k3s-server-01 Servidor (plano de control) 2 4 GB 32 GB
k3s-agent-01 Agente (worker) 2 4 GB 64 GB
k3s-agent-02 Agente (worker) 2 4 GB 64 GB

Los discos de 64 GB de los agentes son los que Longhorn usará para el almacenamiento distribuido. Un NVMe Samsung 970 EVO Plus de 500 GB como disco principal del host de Proxmox da suficientes IOPS para que todo se sienta ágil.

Si quieres correrlo en hardware dedicado, el Minisforum MS-01 es una opción excelente para un clúster de 3 nodos: doble 2.5GbE, un puerto SFP+ y espacio para dos NVMe por unidad. El Beelink GTi14 Ultra es otra opción sólida si prefieres un solo host corriendo varias VMs.

En cuanto a RAM, si tu mini PC lo soporta, sube al menos a 32 GB de DDR5 para tener margen tanto para las VMs como para el sistema del host.


Paso 1: crea las VMs en Proxmox

Para las VMs de K3s usamos imágenes cloud de Ubuntu 24.04 LTS. Descárgala una vez en Proxmox y crea una plantilla:

bash
# On the Proxmox host
wget https://cloud-images.ubuntu.com/noble/current/noble-server-cloudimg-amd64.img

# Create VM template
qm create 9000 --name ubuntu-2404-template --memory 2048 --net0 virtio,bridge=vmbr0
qm importdisk 9000 noble-server-cloudimg-amd64.img local-lvm
qm set 9000 --scsihw virtio-scsi-pci --scsi0 local-lvm:vm-9000-disk-0
qm set 9000 --boot c --bootdisk scsi0
qm set 9000 --ide2 local-lvm:cloudinit
qm set 9000 --serial0 socket --vga serial0
qm set 9000 --agent enabled=1
qm template 9000

Luego clónala tres veces:

bash
for i in 101 102 103; do
  qm clone 9000 $i --name k3s-node-$i --full
  qm set $i --memory 4096 --cores 2
  qm resize $i scsi0 +30G   # agents get +60G instead
done

Configura cloud-init en cada VM (llave SSH, IP fija, DNS):

bash
qm set 101 --ciuser ubuntu --sshkeys ~/.ssh/authorized_keys   --ipconfig0 ip=192.168.1.101/24,gw=192.168.1.1   --nameserver 192.168.1.1

Arráncalas y asegúrate de poder entrar por SSH antes de seguir.


Paso 2: instala K3s, primero el nodo servidor

En k3s-server-01 (192.168.1.101):

bash
curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC="server   --cluster-init   --tls-san 192.168.1.101   --disable traefik   --disable servicelb" sh -

Aquí desactivamos el Traefik y el ServiceLB integrados porque vamos a instalar Traefik con Helm para tener más control, y usaremos MetalLB para los servicios LoadBalancer en la red local.

Obtén el token del nodo para los agentes:

bash
sudo cat /var/lib/rancher/k3s/server/node-token

Verifica que el servidor esté arriba:

bash
sudo k3s kubectl get nodes
# NAME              STATUS   ROLES                       AGE   VERSION
# k3s-server-01     Ready    control-plane,etcd,master   30s   v1.32.x+k3s1

Paso 3: une los nodos agentes

En cada VM agente (reemplaza y la IP del servidor):

bash
curl -sfL https://get.k3s.io | K3S_URL=https://192.168.1.101:6443   K3S_TOKEN=<TOKEN> sh -

De vuelta en el servidor, confirma que ambos agentes se unieron:

bash
sudo k3s kubectl get nodes
# NAME              STATUS   ROLES                       AGE   VERSION
# k3s-server-01     Ready    control-plane,etcd,master   5m    v1.32.x+k3s1
# k3s-agent-01      Ready    <none>                      2m    v1.32.x+k3s1
# k3s-agent-02      Ready    <none>                      1m    v1.32.x+k3s1

Copia el kubeconfig a tu máquina local:

bash
# On the Proxmox host or your workstation
scp ubuntu@192.168.1.101:/etc/rancher/k3s/k3s.yaml ~/.kube/config-k3s
# Edit the server URL: change 127.0.0.1 to 192.168.1.101
export KUBECONFIG=~/.kube/config-k3s
kubectl get nodes  # works from your laptop now

Paso 4: almacenamiento persistente con Longhorn

El local-path provisioner integrado sirve para cargas sin estado, pero para cualquier cosa con base de datos o archivos, quieres Longhorn. Ofrece almacenamiento en bloques replicado entre tus nodos: si una VM agente se cae, tus datos sobreviven.

Instálalo con Helm:

bash
helm repo add longhorn https://charts.longhorn.io
helm repo update
helm install longhorn longhorn/longhorn   --namespace longhorn-system   --create-namespace   --set defaultSettings.defaultReplicaCount=2

Espera a que todos los pods estén en Running:

bash
kubectl -n longhorn-system get pods --watch

Define Longhorn como StorageClass por defecto:

bash
kubectl patch storageclass local-path   -p '{"metadata": {"annotations": {"storageclass.kubernetes.io/is-default-class": "false"}}}'
kubectl patch storageclass longhorn   -p '{"metadata": {"annotations": {"storageclass.kubernetes.io/is-default-class": "true"}}}'

Para que el almacenamiento funcione bien, asegúrate de que cada nodo agente tenga ese volumen de disco extra que mencioné. Longhorn detecta /var/lib/longhorn automáticamente. Monta el disco extra de Proxmox en esa ruta:

bash
# On each agent, after formatting the extra disk as ext4
sudo mkfs.ext4 /dev/sdb
echo '/dev/sdb /var/lib/longhorn ext4 defaults 0 2' | sudo tee -a /etc/fstab
sudo mount -a

El NVMe Western Digital Blue SN580 de 1 TB es una opción económica para los volúmenes de Longhorn: buen rendimiento secuencial y precio competitivo. El SSD SATA Samsung 870 EVO de 1 TB también funciona bien si tu Proxmox usa SATA en lugar de passthrough de NVMe.


Paso 5: ingress con Traefik y cert-manager

Instala Traefik con Helm usando un archivo de valores pensado para un homelab:

bash
helm repo add traefik https://traefik.github.io/charts
helm repo update

cat << 'EOF' > traefik-values.yaml
deployment:
  replicas: 1
service:
  type: LoadBalancer
  annotations:
    metallb.universe.tf/loadBalancerIPs: "192.168.1.200"
additionalArguments:
  - "--certificatesresolvers.letsencrypt.acme.email=contact@gilricardo.com"
  - "--certificatesresolvers.letsencrypt.acme.storage=/data/acme.json"
  - "--certificatesresolvers.letsencrypt.acme.tlschallenge=true"
EOF

helm install traefik traefik/traefik   --namespace traefik   --create-namespace   -f traefik-values.yaml

Para MetalLB (así los servicios de tipo LoadBalancer reciben IPs reales de tu red local):

bash
helm repo add metallb https://metallb.github.io/metallb
helm install metallb metallb/metallb -n metallb-system --create-namespace

# Configure IP pool
kubectl apply -f - <<EOF
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
  name: homelab-pool
  namespace: metallb-system
spec:
  addresses:
  - 192.168.1.200-192.168.1.220
---
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
  name: homelab-l2
  namespace: metallb-system
EOF

Ahora cualquier servicio LoadBalancer recibe una IP real en tu rango 192.168.1.200-220, accesible desde cualquier equipo de tu red local.


Paso 6: acceso remoto con el operador de Tailscale

El operador de Tailscale para Kubernetes es una forma limpia de exponer servicios del clúster en Tailscale sin instalar un cliente VPN en cada dispositivo. Instálalo:

bash
helm repo add tailscale https://pkgs.tailscale.com/helmcharts
helm repo update
helm upgrade --install tailscale-operator tailscale/tailscale-operator   --namespace=tailscale   --create-namespace   --set-string oauth.clientId=<TAILSCALE_CLIENT_ID>   --set-string oauth.clientSecret=<TAILSCALE_CLIENT_SECRET>   --wait

Luego agrega la anotación a cualquier servicio que quieras exponer en tu tailnet:

yaml
apiVersion: v1
kind: Service
metadata:
  name: my-app
  annotations:
    tailscale.com/expose: "true"
    tailscale.com/hostname: "my-app-k3s"
spec:
  type: LoadBalancer
  ...

El servicio recibe un hostname my-app-k3s en tu tailnet, accesible desde tu teléfono, tu laptop o cualquier dispositivo conectado a Tailscale. Sin redirección de puertos, sin DNS dinámico, sin dolores de cabeza con certificados para servicios internos.


Problemas comunes (ahórrate la depuración)

La E/S de disco de etcd importa más de lo que crees. Si el disco de la VM del plano de control es lento, etcd se vuelve inestable. Usa almacenamiento NVMe para la VM servidor. Un síntoma típico es que los comandos kubectl empiezan a colgarse de forma intermitente cuando la VM servidor está en un pool SATA lento.

Longhorn necesita nombres de nodo consistentes. Si recreas una VM y cambia el hostname, Longhorn se confunde sobre qué nodo tiene qué réplicas de volumen. Define los hostnames en cloud-init y no los cambies. Usa k3s-agent-01 y k3s-agent-02 como hostnames fijos, no el hostname por defecto ubuntu de cloud-init.

firewalld/ufw rompen la comunicación entre nodos. Los nodos de K3s se comunican en varios puertos (6443, 8472 UDP para Flannel VXLAN, 10250, etc.). Configura bien las reglas del firewall o (en un homelab) simplemente desactiva ufw en las VMs: sudo ufw disable.

Haz snapshots en Proxmox antes de cualquier actualización de K3s. Ejecuta qm snapshot pre-k3s-upgrade antes de actualizar. Las actualizaciones de K3s suelen ir bien, pero tener un camino de vuelta atrás con un solo comando te cuesta 30 segundos.

Para conectar tus mini PCs con un switch administrable, el TP-Link TL-SG108E de 8 puertos es un switch barato que soporta VLANs si quieres aislar el tráfico del clúster. El Netgear GS308E es otra buena opción en el mismo rango.


Despliega tu primera carga real

Este es un ejemplo completo que despliega Gitea (Git autoalojado) en el clúster con un PVC de Longhorn:

yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: gitea
  namespace: gitea
spec:
  replicas: 1
  selector:
    matchLabels:
      app: gitea
  template:
    metadata:
      labels:
        app: gitea
    spec:
      containers:
      - name: gitea
        image: gitea/gitea:latest
        ports:
        - containerPort: 3000
        - containerPort: 22
        volumeMounts:
        - name: gitea-data
          mountPath: /data
      volumes:
      - name: gitea-data
        persistentVolumeClaim:
          claimName: gitea-pvc
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: gitea-pvc
  namespace: gitea
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: longhorn
  resources:
    requests:
      storage: 20Gi
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: gitea
  namespace: gitea
  annotations:
    traefik.ingress.kubernetes.io/router.entrypoints: websecure
    traefik.ingress.kubernetes.io/router.tls.certresolver: letsencrypt
spec:
  rules:
  - host: git.yourdomain.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: gitea
            port:
              number: 3000

Aplícalo y Traefik se encarga del certificado TLS automáticamente. Longhorn replica el volumen /data en tus dos nodos agentes. Si un agente se cae, K3s reprograma el pod en el nodo que sobrevive y Longhorn sirve el volumen desde la réplica.

Esa es toda la historia de producción en un solo archivo YAML.


¿Vale la pena?

Si ya usas Proxmox, sí, sin duda. Montar K3s toma unas dos horas desde VMs nuevas hasta un clúster funcionando con almacenamiento e ingress. A partir de ahí, desplegar servicios nuevos es kubectl apply -f y obtienen TLS automático, límites de recursos, monitoreo de salud y agregación de logs sin esfuerzo extra.

La sobrecarga es real: solo Longhorn corre unos 10 pods por nodo y el plano de control consume ~1.5 GB de RAM en reposo. Pero en una máquina con 32 GB de RAM repartidos entre VMs, eso es poca cosa.

La ganancia más grande es que tu homelab ahora usa las mismas piezas que se usan en el trabajo. Cuando te toca depurar un problema de Kubernetes en el trabajo, puedes reproducirlo en local en minutos: no en un entorno de staging compartido, sino en hardware que controlas por completo.


Aviso: este artículo contiene enlaces de afiliado. Si compras a través de ellos, puedo ganar una pequeña comisión sin costo extra para ti.

¿Quieres algo así para tu negocio?

Instalo infraestructura privada y autoalojada en Proxmox: IA, automatización y herramientas internas en hardware que es tuyo, documentado y entregado. Ver el stack de IA privada →

¿No sabes qué necesitas? Agenda una llamada gratis.

📬Newsletter semanal

Recibe lo mejor de homelab e IA

Sin spam. Un correo por semana. Te das de baja cuando quieras.

Comparte este artículo