A flash-and-walk-away Proxmox install
The problem: you need to install Proxmox VE on a box you can’t see the screen on — no monitor, dead HDMI, a remote/rack machine, or a batch of identical mini-PCs. The interactive installer is a non-starter.
The fix: Proxmox VE 8.2+ ships a real automated installer. You write a small answer.toml, bake it into a
stock ISO with proxmox-auto-install-assistant, flash it, and the box installs itself with no keyboard or
monitor — then it’s reachable over SSH. This guide is the whole flow plus the three gotchas that will otherwise
cost you an afternoon.
The 3-minute version
- Write
answer.toml(disk, network, root pw, your SSH key). proxmox-auto-install-assistant prepare-iso stock.iso --fetch-from iso --answer-file answer.toml --on-first-boot first-boot.sh --output auto.iso- Flash
auto.isoto USB, boot the headless box → it auto-installs → SSH in.
answer.toml (annotated)
[global]
keyboard = "en-us"
country = "us"
fqdn = "pve-node1.example.lan"
mailto = "you@example.com"
timezone = "America/Los_Angeles"
root-password-hashed = "$6$...." # openssl passwd -6 (see Gotcha 3)
root-ssh-keys = ["ssh-ed25519 AAAA... you@host"] # so you can get in with no console
[network]
source = "from-dhcp" # simplest; see Gotcha 2 for the self-heal
[disk-setup]
filesystem = "ext4"
filter.ID_BUS = "ata" # target the SATA SSD by BUS, not a name — see Gotcha 1
filter-match = "any"
Validate + build (needs the proxmox-auto-install-assistant package, on any Debian/PVE box):
proxmox-auto-install-assistant validate-answer answer.toml
proxmox-auto-install-assistant prepare-iso proxmox-ve_*.iso \
--fetch-from iso --answer-file answer.toml --on-first-boot first-boot.sh --output pve-auto.iso
Flash with dd/Rufus/Etcher, boot the box — an “Automated Installation” entry auto-runs after 10 s.
Gotcha 1 — booting from USB renames your disks (sda → sdb)
The single biggest silent failure: you set disk-list = ["sda"] for “the SSD,” but when you boot from a USB stick,
the stick usually grabs /dev/sda, pushing the internal SSD to /dev/sdb. The installer then targets the USB,
errors, and — with reboot-on-error = false — halts on a screen you can’t see.
Fix: never target by device name. Target by bus type so ordering doesn’t matter:
filter.ID_BUS = "ata" # the SATA SSD; excludes the USB (usb bus) and any NVMe. Use "nvme" for an NVMe target.
Gotcha 2 — DHCP can silently fall back to 192.168.100.2
If the NIC’s link isn’t up before the installer’s DHCP attempt (slow negotiation / switch STP), PVE quietly installs
with its hard-coded default 192.168.100.2/24 — so the node never appears on your LAN and looks “dead,” even
though the install succeeded. This is not a rogue DHCP server; it’s the installer default.
Fix: embed a first-boot self-heal script (--on-first-boot). At first boot the link is definitely up, so it
re-acquires DHCP and bakes a stable static config. Minimal version:
#!/usr/bin/env bash
set -uo pipefail
cur=$(ip -4 -o addr show vmbr0 | awk '{print $4}' | cut -d/ -f1 | head -1)
gw=$(ip route | awk '/^default/{print $3; exit}')
# already on a real, reachable network? done.
[ "$cur" != "192.168.100.2" ] && ping -c1 -W2 "${gw:-0}" >/dev/null 2>&1 && exit 0
# else: flip vmbr0 to DHCP, grab a lease, convert to static, fix /etc/hosts
sed -i 's/iface vmbr0 inet static/iface vmbr0 inet dhcp/' /etc/network/interfaces
sed -i '/address 192.168.100.2/d; /gateway 192.168.100.1/d' /etc/network/interfaces
ifreload -a; sleep 6
lease=$(ip -4 -o addr show vmbr0 | awk '{print $4}' | head -1); ip=${lease%/*}
[ -n "$ip" ] && [ "$ip" != "192.168.100.2" ] && sed -i "s/192.168.100.2/$ip/g" /etc/hosts
Add [first-boot] with source = "from-iso" to answer.toml, and pass --on-first-boot first-boot.sh to
prepare-iso.
Gotcha 3 — Python 3.13 removed the crypt module
Lots of “make the hashed root password” snippets use python3 -c 'import crypt...' — that fails on Python 3.13+.
Use OpenSSL instead:
openssl passwd -6 # prompts, prints a $6$ sha512crypt hash for root-password-hashed
Two more field notes
- Flash to a known-good USB and verify. A dying stick throws
Medium not presentI/O errors mid-write. A quick guard afterdd:head -c $(stat -c%s img.iso) /dev/sdX | md5sumand compare to the ISO’s md5 — if it mismatches, the stick is bad, swap it. - The internal disk must be bootable-from-USB. If it already has an OS and the box boots that instead, you’ll need BIOS boot-order (which needs video) — so test on a blank/wiped internal disk when you can.
Result: flash → boot → ~10–15 min later the node is on your LAN over SSH, no monitor ever attached. The same
answer.toml (with bus/size disk filters) re-images a whole batch of identical mini-PCs hands-off.
Written from a real deployment: Proxmox VE 9.x auto-install onto a video-less mini-PC and a batch plan for a mini-cluster. Adjust ids/subnets/versions to your gear.
Written by James Brooks — I run ThatNerdKnows (IT support + websites for small businesses). This is the deep end; if you’d rather just have it handled, that’s the day job.