$ cat /blog/mini-pcs-to-proxmox-cluster.mdx

·8 min read

Turning two used mini PCs into a Proxmox cluster

How I took two refurbed office desktops and made them into the two-node Proxmox setup that runs every VM in my homelab. Why refurbs, why not full HA, and every gotcha I hit installing PVE for the first time — so you don't.

Two used mini PCs, three years of experimenting on top of them. This is the machine layer of my homelab — everything else runs as a VM on one of these two boxes. Here’s how they got there.

If you’re new to Proxmox: it’s Debian underneath, KVM for VMs, and a web UI to drive it. You install it like any other Linux distro, and afterwards you have a hypervisor that runs your workloads as VMs and containers. That’s the whole idea.

What I bought and why refurb

Both mini PCs are the same spec:

  • CPU: Intel Core i5, 6th gen (Skylake, 4 cores)
  • RAM: 16 GB DDR4
  • Storage: 256 GB HDD (I know, more on this in a second)
  • Origin: refurbed corporate lease returns

A quick pitch for refurb office mini PCs, if you haven’t gone this route: they are the best deal in homelab hardware right now. Companies refresh their fleets every 3–5 years and dump perfectly good machines onto the refurb market. You get a 5–7-year-old CPU with lots of RAM headroom for less than a decent restaurant meal. The CPU is old, but “old” in Intel-land still runs multiple VMs cheerfully.

Two things I’d change in hindsight:

  • The HDD. 256 GB spinning disk in each was fine at first, but any workload with real disk activity (Prometheus, Postgres, running a bunch of containers) is noticeably slow. I’ve since swapped both for cheap SATA SSDs. If you’re buying used mini PCs, budget for the SSD upgrade too. Non-negotiable.
  • The RAM. 16 GB per node is enough for what I run, but barely. If I could go back, I’d have hunted for the 32 GB variant.

Not things I’d change:

  • Two nodes instead of one. Even without HA, having two hypervisors means I can migrate VMs between them for maintenance. A single node means every reboot is downtime.
  • Both same spec. Mixed specs work fine, but same specs make it easier to migrate VMs around without worrying about who has enough resources.

Do I need a cluster?

This is the question that trips up most people going from one PVE node to two. Short answer: yes, cluster them; no, don’t turn on HA.

Longer answer:

Proxmox “clustering” means the two nodes share a VMID namespace and know about each other’s VMs. You can migrate a VM from one to the other with a right-click. VMs still have exactly one node running them at a time.

Proxmox “HA” (high availability) is a separate feature on top of clustering — it means if a node dies, another node automatically starts the dead node’s VMs. Sounds great. Requires more than two nodes to work reliably.

Here’s why: HA needs a majority vote (quorum) to decide “yes, that node is dead, take over.” With two nodes, if the network between them hiccups, both nodes think the other is dead, both try to take over. This is called split-brain, and it’s how you end up with two copies of your database corrupting each other.

Two-node HA can be made to work with a third “vote-only” device (a Raspberry Pi as a qdevice), but at that point I’d rather admit I don’t actually need HA. My VMs are backed up nightly. If a node dies, I restore from backup onto the other node. Downtime measured in minutes. That’s fine for a homelab.

So: clustered, not HA. Best of both worlds.

The bare-metal install (the boring 20 minutes)

Proxmox VE 8 ISO, flashed to a USB stick. Plug into each mini PC one at a time, boot from USB, walk the installer. It’s a Debian installer with different graphics.

During the install, three settings that matter:

  • Hostname: pve1 for the first box, pve2 for the second. Pick the final names here — you’ll be referring to them by name for years.
  • Static IP: absolutely set one. The Proxmox web UI is bound to whatever IP you enter, and DHCP moves that IP around means your bookmarks stop working. Also, cluster join uses the IP, and moving IPs post-cluster-join is painful.
  • Root password: put it in a password manager. You will forget it.

Everything else — timezone, disk layout, network — accept the defaults. The installer picks reasonable ones.

After first boot, log in as root either via SSH or the web UI (https://<pve-ip>:8006) and do these things once per node:

1. Swap the enterprise repo for no-subscription.

Fresh Proxmox installs point at the paid enterprise repo, and apt update will complain loudly. Fix:

# Disable the enterprise repo
mv /etc/apt/sources.list.d/pve-enterprise.list /etc/apt/sources.list.d/pve-enterprise.list.disabled

# Add the no-subscription repo
echo "deb http://download.proxmox.com/debian/pve bookworm pve-no-subscription" \
  > /etc/apt/sources.list.d/pve-no-subscription.list

apt update && apt dist-upgrade -y

Then reboot if the upgrade included kernel packages (it usually does on a fresh install).

2. Push your SSH pubkey.

From whatever machine you’ll use to manage the homelab (in my case, WSL on my Windows laptop), copy your public key to root on each node:

ssh-copy-id root@pve1
ssh-copy-id root@pve2

Then verify ssh root@pve1 works with no password prompt. This unlocks Ansible.

3. Form the cluster.

On pve1:

pvecm create homelab

On pve2:

pvecm add <pve1-ip>

Verify with pvecm status on either node — you should see both nodes, both marked online, quorum of 2.

That’s the manual part. ~15 minutes per node, ~30 minutes total. Everything from here is Ansible.

The classic first-timer mistake

If you skip step 1 above and just start installing things on the enterprise repo, apt update will fill your logs with warnings, and any package install command will print scary red text. It’s harmless but disorienting. Every Proxmox first-timer hits this. The fix is literally two lines in the shell above.

The other classic: forgetting to set a static IP during install, then trying to change it later. You canpvecm status uses IPs, so you’ll need to update /etc/pve/corosync.conf and /etc/hosts on both nodes. It’s fixable, but “fixable” is not the same as “worth going through.” Static IP during install. Trust me.

Handing off to Ansible

Once the manual steps above are done, one Ansible playbook takes each node from “fresh install” to “ready for VMs.” (If you’re wondering why Ansible and not shell scripts / Puppet / Nix, the origin story has the full argument.) The short version:

  • Register the NAS as PVE-visible storage (VMs can be backed by it, and PBS’s datastore lives there).
  • Set vm.swappiness=10 — hypervisors with lots of RAM shouldn’t be page-thrashing.
  • Set the CPU governor to powersave. These are mini PCs in a closet with passive cooling; running full-tilt buys me nothing except more heat. On the actual workloads (DNS, Prometheus scrapes, occasional Docker rebuild), I cannot measure a difference between powersave and performance.
  • Verify a cloud-init template exists on each node, or fail with the exact command to build one.

Idempotent, ~2 minutes per node on subsequent runs.

The template — the artifact everything downstream needs

The cloud-init template is a small VM configured just so, then frozen with qm template. Every “real” VM in the homelab is a clone of this template. Cloning takes seconds; the clone comes up with the right user account, right SSH keys, and a static IP defined at clone time.

I have one template per Proxmox node (they can’t share the same VMID). The naming scheme I settled on encodes the node in the tens digit and the distro in the units:

pve1: debian-13 template = VMID 9013
pve2: debian-13 template = VMID 9023

Obvious at a glance, no lookup needed at 2am. Do future-you a favor and pick a scheme like this. VMIDs are permanent-ish once you start using them.

Setting up the template itself is another Ansible playbook (downloads the Debian cloud image, imports it as a VM disk, configures cloud-init defaults, converts to template). About four minutes end-to-end per node the first time.

What “done” looks like

After the above:

  • Two Proxmox nodes, clustered, both reachable via SSH as root with no password prompt.
  • NAS registered as nas-spare on both nodes.
  • Sensible sysctls and CPU governor set.
  • A cloud-init template on each node ready to clone from.
  • Every subsequent VM I want is a qm clone away — about 30 seconds.

Total time budget:

StepTime
ISO install per node~15 min
Post-install manual (repos, SSH keys, cluster)~15 min per node
Template creation~4 min per node
Bootstrap playbook~2 min per node
All-in for two nodes, first time~about an hour

Every rebuild after that: minutes. Which is the whole point — I did the manual work once, and every hardware failure from here on is a boring restore, not a “how did I set this up again?” archaeology dig.

What comes next

Next up: the four Raspberry Pis — the other half of the compute layer. Different constraints (ARM64, no cloud-init template pattern), same idea: get to a repeatable baseline, then let Ansible take over.

After that, the fun starts: one post per service. DNS pair, VPN gateway, monitoring, media, and everything else that makes this pile of machines actually earn its keep.


$ cd /blog  // all posts