Proxmox Cluster and VMs
First page of the HA Kubernetes on Two Proxmox Hosts series. This page takes two bare machines to a working Proxmox cluster with six Kubernetes-ready VMs.
Prerequisites
Section titled “Prerequisites”- Proxmox VE 8.x ISO flashed to a USB stick.
- The IP plan from the hub page finalized: static IPs reserved for both hosts, the tiebreaker box, all six VMs, and the API VIP (outside the DHCP pool).
- The tiebreaker box reachable on the same subnet, with
aptand Docker available.
Step 1: Install Proxmox on Both Hosts
Section titled “Step 1: Install Proxmox on Both Hosts”Install Proxmox VE normally on each host (ext4 or ZFS on the single NVMe). During or right after install:
- Set the planned static IP and a unique hostname per host.
- Point both hosts at the same NTP source — etcd and corosync both misbehave under clock skew.
Switch to No-Subscription Repos
Section titled “Switch to No-Subscription Repos”On each host:
sed -i 's/^deb/#deb/' /etc/apt/sources.list.d/pve-enterprise.listecho "deb http://download.proxmox.com/debian/pve bookworm pve-no-subscription" > /etc/apt/sources.list.d/pve-no-subscription.listsed -i 's/^deb/#deb/' /etc/apt/sources.list.d/ceph.list 2>/dev/nullapt update && apt full-upgrade -ySuppress the subscription nag popup (cosmetic; re-run after pve-manager updates):
sed -i.bak "s/data.status !== 'Active'/false/g" /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.jssystemctl restart pveproxy.serviceStep 2: Form the 2-Node Cluster and Add the QDevice
Section titled “Step 2: Form the 2-Node Cluster and Add the QDevice”A 2-node cluster cannot form quorum alone, so a QDevice on the tiebreaker box provides the third vote. This is Proxmox-level quorum (corosync), independent of the Kubernetes/etcd quorum built later.
On Host A:
pvecm create homelab-clusterOn Host B:
pvecm add <HOST-A-IP>Verify both hosts see each other:
pvecm statusExpected: 2 nodes listed.
Add the QDevice
Section titled “Add the QDevice”On the tiebreaker box:
apt install corosync-qnetdOn both Proxmox hosts:
apt install corosync-qdeviceFrom one host:
pvecm qdevice setup <TIEBREAKER-IP>Verify:
pvecm statusExpected: Total votes: 3 and quorum maintained.
Step 3: Provision the Six VMs
Section titled “Step 3: Provision the Six VMs”Build a Debian 12 cloud-init template once per host, then clone it.
Create the Template
Section titled “Create the Template”wget https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-generic-amd64.qcow2qm create 9000 --memory 2048 --cores 2 --name debian12-template --net0 virtio,bridge=vmbr0qm importdisk 9000 debian-12-generic-amd64.qcow2 local-lvmqm set 9000 --scsihw virtio-scsi-pci --scsi0 local-lvm:vm-9000-disk-0qm set 9000 --ide2 local-lvm:cloudinitqm set 9000 --boot c --bootdisk scsi0qm template 9000Clone Per VM
Section titled “Clone Per VM”Per the hub’s VM table (control planes 60 GB, workers 150 GB). Example for cp1 on Host A:
qm clone 9000 101 --name cp1 --fullqm set 101 --memory 4096 --balloon 0 --cores 2qm resize 101 scsi0 60Gqm set 101 --ipconfig0 ip=<CP1-IP>/24,gw=<GATEWAY-IP>qm start 101Repeat for the two workers on Host A (--memory 4096 --balloon 0, qm resize ... 150G), then cp2 and two workers on Host B.
Set —balloon 0 on every VM: no memory ballooning or overcommit. Keep ~4 GB free for the Proxmox host itself; if a host shows memory pressure later, trim worker VMs to 3.5 GB instead of squeezing the hypervisor.
Pin the VMs to their hosts. Do not live-migrate or configure Proxmox HA failover for the control-plane/etcd VMs — the quorum design depends on etcd1 and etcd2 staying on separate physical machines. Migrating cp1 to Host B would put two etcd members on one host and defeat the failure model.
Step 4: Node Prep on All Six VMs
Section titled “Step 4: Node Prep on All Six VMs”Apply this on every VM (via cloud-init snippets or SSH). Versions come from the hub’s version matrix.
Kernel Modules and Sysctl
Section titled “Kernel Modules and Sysctl”cat <<EOF | sudo tee /etc/modules-load.d/k8s.confoverlaybr_netfilterEOFsudo modprobe overlay br_netfilter
cat <<EOF | sudo tee /etc/sysctl.d/k8s.confnet.ipv4.ip_forward = 1net.bridge.bridge-nf-call-iptables = 1EOFsudo sysctl --systemSwap Off and Time Sync
Section titled “Swap Off and Time Sync”sudo swapoff -asudo sed -i '/ swap / s/^/#/' /etc/fstabsudo apt install -y chrony && sudo systemctl enable --now chronycontainerd
Section titled “containerd”sudo apt install -y containerdsudo mkdir -p /etc/containerdcontainerd config default | sudo tee /etc/containerd/config.toml >/dev/nullsudo sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.tomlsudo systemctl restart containerd && sudo systemctl enable containerdSystemdCgroup = true is required — kubelet defaults to the systemd cgroup driver, and a mismatch causes pods to restart in loops.
kubeadm, kubelet, kubectl (Pinned and Held)
Section titled “kubeadm, kubelet, kubectl (Pinned and Held)”sudo apt install -y apt-transport-https ca-certificates curl gpgcurl -fsSL https://pkgs.k8s.io/core:/stable:/v1.33/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpgecho 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.33/deb/ /' | sudo tee /etc/apt/sources.list.d/kubernetes.listsudo apt updatesudo apt install -y kubelet kubeadm kubectlsudo apt-mark hold kubelet kubeadm kubectlLonghorn Prerequisites (Workers)
Section titled “Longhorn Prerequisites (Workers)”On the four worker VMs (needed later by the Longhorn page):
sudo apt install -y open-iscsi nfs-commonsudo systemctl enable --now iscsidVerify Before Moving On
Section titled “Verify Before Moving On”pvecm status # on either host: 3 votes, quorateqm list # on each host: its 3 VMs runningssh <any-vm> 'systemctl is-active containerd && kubeadm version -o short && free -h | grep -i swap'Expected: quorum with 3 votes, all six VMs running with their static IPs, containerd active, kubeadm at the pinned version, swap showing 0B.