
Three Debian VMs with Proxmox-shaped configuration and compatibility endpoints, not an installed PVE host, LXC, or nested VM
By the OpenFactory Team · May 4, 2026
Proxmox VE is the #1 homelab host OS: 42.2% of respondents in the 2026 selfh.st survey, ahead of bare Debian or Ubuntu. For most people the starting shape is the same: one mini-PC, Proxmox on the metal, a handful of LXC containers for utility services, and one or two VMs for heavier apps.
The platform itself has moved on. Proxmox VE 9 rebased onto Debian 13 “Trixie”, and by VE 9.2 (May 2026) ships Linux kernel 7.0, QEMU 11, LXC 7, and ZFS 2.4 as defaults. The ZFS change that matters most to single-node builders: RAIDZ expansion finally landed, so you can grow a raidz1 pool one disk at a time with zpool attach instead of destroying and rebuilding it (walkthrough). That removes the classic objection to ZFS on a box you grow incrementally.
This post walks through that exact shape as an OpenFactory build prompt: three buildable Debian Trixie VMs (a Proxmox host node, one LXC container running Pi-hole, and one VM workload) from a single prompt, with the /etc/pve/ config tree, sample CT100 / VM200 conf files, and a mock PVE API on :8006 already baked in. Real pve-manager is a source-ISO install at deploy time; the lab gives you the topology and the configs to drop on top of it. You rehearse the shape and the boot before you ever touch the metal.
pve-node (10.80.0.10:8006): the PVE host with /etc/pve/{nodes,storage,qemu-server,lxc,priv}/ already laid out and a mock PVE API responding on :8006/api2/json/version / /nodes / /cluster/status.lxc-pihole (10.80.0.20): container template at /etc/pve/lxc/100.conf with hostname, rootfs, network, and unprivileged flag set; mock Pi-hole admin on :80 + DNS socket on :53.vm-app (10.80.0.30:8080): VM template at /etc/pve/qemu-server/200.conf with virtio NIC, SCSI disk, and agent: 1; mock health + info endpoints./etc/pve/storage.cfg with dir: local and lvmthin: local-lvm pre-populated.pve-manager on top./etc/pve/lxc/100.confisn't shaped right, if the mock API returns the wrong node id, or if the host can't reach the children on the lab subnet.Three Debian Trixie VMs on 10.80.0.0/24. The PVE host manages the two children (CT100 and VMID 200) through the mock PVE API on :8006; the container rides the host's shared kernel while the VM runs its own behind the hypervisor. That is the same split a real PVE box draws.
The most common question on a fresh Proxmox box is container or VM? On a single node the choice decides resource overhead, isolation, and kernel flexibility. An LXC container shares the host kernel; a KVM guest runs its own kernel behind a virtual hardware boundary. Either can be the better fit depending on workload, storage, devices, and security requirements. Proxmox documents both models in its administration guide. Benchmark your actual service, kernel, storage, and network path before using performance or density numbers for capacity planning.
A useful starting heuristic is LXC for trusted Linux services that can share the host kernel and KVM for a separate kernel, another operating system, or a stronger isolation boundary. Validate that heuristic against the workload's threat model. That is why this lab models lxc-pihole as a CT and vm-app as a full VM. The deciding factor once the workload matters is security: a privileged container that escapes its namespace is on your host kernel; a compromised VM is still boxed behind the hypervisor.
More often than the cluster evangelists admit. A single node with ZFS gives you checksummed storage that catches silent bit-rot, instant snapshots before every risky change, and zfs send replication to a second box: everything except automaticfail-over, and with RAIDZ expansion you can grow the pool a disk at a time. If your honest recovery objective is “restore within an hour from a backup,” not “survive a dead node with zero downtime,” one disciplined node covers it. You graduate to the 3-node cluster the day a few minutes of downtime stops being acceptable (not before), since three nodes means roughly triple the power, the patching, and a Corosync network to babysit.
Choose Deploy to open this exact, registered prompt in Elster Terminal and start the build. Choose Copy prompt when you want to edit it first in build.openfactory.tech.
Build a compact multi-node lab named `proxmox-single-node-lab`.
Output discipline: keep the plan small. Use one startup script per node, about 25 shell lines or less. Do not install `pve-manager`, `qemu-kvm`, `lxc`, the Proxmox apt repos, or any kernel modules at build time. The PVE host is a source-ISO deploy; this lab only stands up the configuration shape and a tiny Python stdlib HTTP service that mimics the PVE API. Write deployment-time config examples and tiny Python stdlib or shell compatibility stubs only. The goal is a buildable preparation lab, not a production Proxmox install.
## Topology
Create 3 buildable `debian-trixie` nodes, all `x86_64`, SSH enabled, DHCP/default route intact with lab aliases, firewall disabled, DNS `1.1.1.1` and `8.8.8.8`, user `ops` password `pve-ops` in `sudo`. Every recipe must set top-level `test_config` to `{ "enabled": false, "tests": [] }`.
- `pve-node`: role `pve-host`, 4 GB RAM, 32 GB disk, alias `10.80.0.10/24`, x `230`, y `60`
- `lxc-pihole`: role `lxc-container`, 1 GB RAM, 8 GB disk, alias `10.80.0.20/24`, x `110`, y `220`
- `vm-app`: role `vm-workload`, 2 GB RAM, 16 GB disk, alias `10.80.0.30/24`, x `350`, y `220`
Connections: `pve-node` to both children on `:22`; `pve-node` manages `lxc-pihole` (CT 100) and `vm-app` (VMID 200) via mock PVE API on `:8006`.
## Common Recipe Requirements
All nodes: features `headless`, `ssh`; packages `openssh-server`, `python3`, `curl`, `jq`, `iproute2`, `netcat-openbsd`, `ca-certificates`. Each startup script adds the alias with `IFACE=$(ip route show default | awk '{print $5; exit}')`, `ip link set "$IFACE" up || true`, and `ip addr add <alias> dev "$IFACE" || true`. If `os.startup_scripts[].after` is present, it must be the string `"network-online.target"`, not an array. Do not install `pve-manager`, `proxmox-backup-server`, `ceph`, `truenas-scale`, or any related apt packages: they are source-ISO deploys handled at provisioning time, not at build time.
## Node Requirements
`pve-node`: features `headless`, `ssh`. Create the deployment-time config tree `/etc/pve/{nodes/pve-node,storage,qemu-server,lxc,priv}` mode `0750 ops:ops` and seed:
- `/etc/pve/storage.cfg` with `dir: local\n path /var/lib/vz\n content backup,iso,vztmpl` plus `lvmthin: local-lvm\n thinpool data\n vgname pve\n content rootdir,images`.
- `/etc/pve/lxc/100.conf` representing the Pi-hole CT: `hostname: pihole`, `ostype: debian`, `arch: amd64`, `memory: 1024`, `swap: 512`, `cores: 1`, `net0: name=eth0,bridge=vmbr0,ip=10.80.0.20/24,gw=10.80.0.1`, `rootfs: local-lvm:vm-100-disk-0,size=8G`, `unprivileged: 1`.
- `/etc/pve/qemu-server/200.conf` representing the VM: `name: vm-app`, `memory: 2048`, `cores: 2`, `ostype: l26`, `agent: 1`, `net0: virtio,bridge=vmbr0`, `scsi0: local-lvm:vm-200-disk-0,size=16G`, `boot: order=scsi0`.
Add a Python stdlib HTTP service on `0.0.0.0:8006` exposing:
- `GET /api2/json/version` -> `200 {"data":{"version":"compat-1.0","release":"pve-compat","repoid":"compat-stub"}}`
- `GET /api2/json/nodes` -> `200 {"data":[{"node":"pve-node","status":"online","cpu":0.05,"maxcpu":4,"mem":1073741824,"maxmem":4294967296}]}`
- `GET /api2/json/cluster/status` -> `200 {"data":[{"type":"node","name":"pve-node","online":1,"local":1,"id":"node/pve-node"}]}`
- `GET /metrics` -> `pve_compat_up 1` plus `pve_node_online{node="pve-node"} 1`
Register `pve-compat.service`. Write `/root/pve-runbook.md` warning that real PVE installs replace this stub by booting the Proxmox VE source ISO and joining storage / networking through `pveceph` / `pvecm`.
`lxc-pihole`: features `headless`, `ssh`. Add a Python stdlib HTTP service on `0.0.0.0:80` exposing `GET /admin/api.php?summary` -> `200 {"status":"enabled","domains_being_blocked":"compat","dns_queries_today":"0"}` and `GET /metrics` with `pihole_compat_up 1`. Bind a Python stdlib TCP listener on `0.0.0.0:53` accepting connections (no DNS protocol needed). Register `pihole-compat.service`.
`vm-app`: features `headless`, `ssh`. Add a Python stdlib HTTP service on `0.0.0.0:8080` exposing `GET /health` -> `200 {"status":"ok","node":"vm-app","vmid":200}`, `GET /api/info` -> `200 {"name":"vm-app","vmid":200,"pve_host":"pve-node"}`, `GET /metrics` with `vm_app_compat_up 1`. Register `vm-app-compat.service`.
## Scenario
Emit exactly one group scenario named `proxmox-single-node-lab-validation`. Put `custom_tests[].assertions[]` inside the scenario entry; leave `scenarios[].tests` empty. Every assertion needs `on_vm`. Use only `port_listening`, `command_output`, and `http_responds`; do not emit `vm_boots`, `network_reachable`, or `service_running`.
- `Stack ports listen`: `port_listening` for `pve-node:8006`, `lxc-pihole:80`, `lxc-pihole:53`, `vm-app:8080`.
- `PVE API version`: on `pve-node`, `curl -fsS http://localhost:8006/api2/json/version | jq -e '.data.version == "compat-1.0"' >/dev/null && echo pve-ok`.
- `PVE node listing`: on `pve-node`, `curl -fsS http://localhost:8006/api2/json/nodes | jq -e '.data[0].node == "pve-node"' >/dev/null && echo node-listed`.
- `LXC + VM config files present`: on `pve-node`, `test -s /etc/pve/lxc/100.conf && test -s /etc/pve/qemu-server/200.conf && grep -q 'hostname: pihole' /etc/pve/lxc/100.conf && grep -q 'name: vm-app' /etc/pve/qemu-server/200.conf && echo configs-present`.
- `Child workloads respond`: on `lxc-pihole`, `curl -fsS 'http://localhost/admin/api.php?summary' | jq -e '.status == "enabled"' >/dev/null && echo lxc-ok`; on `vm-app`, `curl -fsS http://localhost:8080/health | jq -e '.vmid == 200' >/dev/null && echo vm-ok`.
- `Host reaches both children`: on `pve-node`, `nc -z -w 5 10.80.0.20 80 && nc -z -w 5 10.80.0.30 8080 && echo children-reachable`.
Preserve warnings that real Proxmox VE installation from the source ISO, `pvecm` cluster init / join, LVM-thin or ZFS root, real bridge / VLAN setup on `vmbr0`, LXC template downloads via `pveam`, VM disk image upload via `qm importdisk`, real Pi-hole installation in the LXC, snapshot scheduling, off-host backups via PBS, and `10.80.0.0/24` lab aliasing are deployment-time concerns.Driving OpenFactory from an AI agent instead of the browser? The same flow is exposed through the OpenFactory MCP server: submit the prompt programmatically, get the build-plan preview back, and call create_build / start_vm on the resulting recipes. Single-image builds go straight through the openfactory CLI.
The prompt produces a buildable preparation lab: the right topology, the right ports listening, deployment-time config templates dropped in the right places, and tiny compatibility services that report whether the expected lab connections respond. Those stubs are not the actual products and do not establish production readiness. A few things still sit outside the recipe and need operator attention before this carries real load:
/etc/pve after install.local-lvm; real install picks LVM-thin or ZFS during the PVE installer.vmbr0 on the host plus any VLAN tags your network needs. The lab assumes a flat bridge.pveam download local debian-12-standard... for the CT, qm importdisk for the VM. The config files are already in the right shape; the disks are yours to seed.vzdump or a dedicated Proxmox Backup Server for deduplicated, off-host copies. See the Proxmox + PBS post.pve-manager upgrade is a 10-second rollback, not a reinstall.The natural next step is the 3-node Proxmox cluster when one host stops being enough: same prompt vocabulary, three PVE nodes plus a QDevice witness. If storage is what you want to get right first, the Proxmox + TrueNAS stack pairs the hypervisor with a dedicated NAS, and the Proxmox + Ceph cluster is the distributed end state. For the DNS layer the LXC anchors, see the Pi-hole DNS cluster post.
Do I need Proxmox 9, or is 8.x fine for one node? VE 8 still works, but VE 9's RAIDZ expansion and newer ZFS, QEMU, and kernel make a fresh single-node build a clear case for 9, and this lab already builds on Debian Trixie, the same base as VE 9. Want Docker on the box? Run it in a vm-app-style KVM guest, not a privileged LXC.
Spinning up a fleet for a regulated environment? The Enterprise & GxP page covers fleet rollouts and audit trails, pricing lays out the tiers, and a build starts now at console.openfactory.tech.
Compare published self-service limits, or scope customer-controlled deployment and fleet requirements through a technical pilot.