Hypervisor
Install Proxmox
These steps (re)install one node or the whole cluster. Everything the Proxmox
API can configure is managed by OpenTofu in the
infrastructure
repository. The steps below are the manual work before and after tofu apply.
1. Installer
Use the graphical installer and enter the following values.
| Setting | Value |
|---|---|
| Target disk | Force MP500 (112 GB NVMe) |
| Filesystem | ext4 |
| Advanced LVM options | swapsize = 8, maxvz = 0 |
| Hostname (FQDN) | pve-a.lan.stamx.nl |
| Management interface | Onboard NIC |
| Pin interface names | Enabled (nic0, nic1, nic2) |
| IP address (CIDR) | 10.0.99.2/24 |
| Gateway | 10.0.99.1 |
| DNS server | 10.0.99.1 |
| Setting | Value |
|---|---|
| Target disk | KINGSTON SUV400S37240G (240 GB SATA) |
| Filesystem | ext4 |
| Advanced LVM options | swapsize = 8, maxvz = 0 |
| Hostname (FQDN) | pve-b.lan.stamx.nl |
| Management interface | Onboard NIC |
| Pin interface names | Enabled (nic0, nic1, nic2) |
| IP address (CIDR) | 10.0.99.3/24 |
| Gateway | 10.0.99.1 |
| DNS server | 10.0.99.1 |
Warning
Only select the boot disk. The other NVMe holds the vm-storage ZFS pool
with the VM disks and must not be touched by the installer.
Note
maxvz = 0 skips the data volume, so there is no local-lvm storage to
remove afterwards. VM disks live on vm-storage.
Note
The OpenTofu stack expects the interface names nic0 (onboard), nic1 and
nic2 (PCI card). If the installer does not offer interface pinning, run
pve-network-interface-pinning generate after the installation and reboot.
2. Prepare each node
-
Add the Ansible SSH key. Run this from the desktop.
-
Extend the root logical volume over the free space. The installer caps root at 96 GiB and leaves some space unallocated.
-
Import the existing ZFS pool. Never create it, it holds the VM disks.
3. Create the cluster
-
Create the cluster on
pve-a. -
Join
pve-b. This asks for the root password ofpve-a.
Reinstalling a single node
The remaining node still knows the reinstalled node. Remove it there before
joining, for example for pve-b:
4. Create the OpenTofu API token
Run this once on any cluster node, for example over SSH from the desktop, and
store tofu@pve!provider=<value> in Bitwarden. The value is shown only once.
ssh root@10.0.99.2 'pveum user add tofu@pve --comment "OpenTofu" \
&& pveum aclmod / -user tofu@pve -role Administrator \
&& pveum user token add tofu@pve provider --privsep 0'
Note
Users, ACLs and tokens live in /etc/pve, which is replicated to every
node in the cluster, so running this once is enough. Do it after creating
the cluster: a joining node replaces its /etc/pve with the one from the
cluster.
5. Create the node certificates
The web interface and API of every node use a certificate from the stamx CA.
Skip this step when ~/certs/proxmox on the desktop already holds valid
certificates.
Run this on the desktop, not on a node
The CA private key must never leave the desktop. OpenTofu reads the
certificates from ~/certs/proxmox and uploads them to the nodes in the
next step.
-
Create the directory and go into it.
-
Copy the CA certificate from the ansible repository.
-
Create a private key and a certificate request for every node.
openssl req -new -newkey rsa:4096 -nodes -keyout pve-a.key -out pve-a.csr \ -subj "/O=stamx/CN=pve-a.lan.stamx.nl" \ -addext "subjectAltName=DNS:pve-a.lan.stamx.nl,DNS:pve-a,IP:10.0.99.2" \ -addext "basicConstraints=CA:FALSE" \ -addext "keyUsage=critical,digitalSignature,keyEncipherment" \ -addext "extendedKeyUsage=serverAuth"openssl req -new -newkey rsa:4096 -nodes -keyout pve-b.key -out pve-b.csr \ -subj "/O=stamx/CN=pve-b.lan.stamx.nl" \ -addext "subjectAltName=DNS:pve-b.lan.stamx.nl,DNS:pve-b,IP:10.0.99.3" \ -addext "basicConstraints=CA:FALSE" \ -addext "keyUsage=critical,digitalSignature,keyEncipherment" \ -addext "extendedKeyUsage=serverAuth" -
Sign the requests with the CA. Replace
/path/to/stamx-ca-private.keywith the location of the CA private key. -
Check the certificates. Both must report
OK. -
Remove the requests and protect the private keys.
Note
The certificates are valid for 825 days. To renew them, repeat this step
and run tofu apply.
6. Apply OpenTofu
Apply the proxmox stack, see OpenTofu usage.
This configures the APT repositories (enterprise off, no-subscription on), DNS,
/etc/hosts, the time zone, the bridges vmbr1 (wan) and vmbr2 (lan), the
SDN VLANs, the vm-storage storage, the lsi-hba PCI mapping, the pools, the
node certificates and the cluster options. The installer only creates vmbr0.
The state in git still knows the resources from before the reinstall, so OpenTofu only creates what is missing.
Reinstalling the whole cluster
HA, storage replication and pool membership refer to guests. After a reinstall of both nodes those guests do not exist yet, so leave them out of the first apply:
tofu apply \
-exclude=proxmox_haresource.opnsense \
-exclude=proxmox_replication.guest \
-exclude=proxmox_pool_membership.guest
Run a plain tofu apply once the guests are back. Reinstalling a single
node does not need this, because the guests keep running on the other
node.
7. Finish each node
-
Update the system. This needs the no-subscription repository from the previous step.
-
Enable Wake-on-LAN on the onboard NIC. Add the
upline to thenic0stanza in/etc/network/interfaces. -
Run the 'Proxmox VE CPU Scaling Governor' script and set it to
powersave. -
Set up two-factor authentication for
rootviaDatacenter->Permissions->Two Factor.
High Availability
Add QDevice
On the Qdevice
-
This command sets the root password, enables root login in the SSH configuration, and restarts the SSH service.
-
Install corosync packages
On the Proxmox node(s)
-
Install corosync-qdevice packages. Repeat for each node in your cluster.
-
Check current status
the output should look like this
Cluster information ------------------- Name: pve-cluster Config Version: 2 Transport: knet Secure auth: on Quorum information ------------------ Date: Wed Mar 26 13:41:51 2025 Quorum provider: corosync_votequorum Nodes: 2 Node ID: 0x00000002 Ring ID: 1.16 Quorate: Yes Votequorum information ---------------------- Expected votes: 2 Highest expected: 2 Total votes: 2 Quorum: 2 Flags: Quorate Membership information ---------------------- Nodeid Votes Name 0x00000001 1 10.0.99.2 0x00000002 1 10.0.99.3 (local) -
Add the QDevice to the cluster. Run this on one of the Proxmox nodes. Change the IP address to the IP of the Qdevice
-
Once this is completed check if the Qdevice has been added to the cluster.
The output should now look similar like this:
Cluster information ------------------- Name: pve-cluster Config Version: 3 Transport: knet Secure auth: on Quorum information ------------------ Date: Wed Mar 26 13:51:33 2025 Quorum provider: corosync_votequorum Nodes: 2 Node ID: 0x00000002 Ring ID: 1.16 Quorate: Yes Votequorum information ---------------------- Expected votes: 3 Highest expected: 3 Total votes: 3 Quorum: 2 Flags: Quorate Qdevice Membership information ---------------------- Nodeid Votes Qdevice Name 0x00000001 1 A,V,NMW 10.0.99.2 0x00000002 1 A,V,NMW 10.0.99.3 (local) 0x00000000 1 Qdevice
Make a VM HA
HA and storage replication are managed by OpenTofu in
opentofu/stacks/proxmox/ha.tf. Changes made in the web interface are not in
the code and get lost on a reinstall.
- Add the guest to
replicated_guestsinlocals.tf, so its disks are replicated topve-bevery 15 minutes. - Add a
proxmox_haresourcefor the guest toha.tf, and add it to theresourcesof a HA rule if it should prefer a node. - Apply OpenTofu.
Mount USB stick (backups)
-
Find the USB stick device
-
Create mount directory
-
Mount (change
<sd?>to the correct device)